chore(release): add colored output to release script
This commit is contained in:
parent
d8b4ec92cf
commit
56fbbbfc8e
|
|
@ -42,6 +42,23 @@ let yes = false;
|
|||
let skipFetch = false;
|
||||
let force = false;
|
||||
|
||||
// NO_COLOR (https://no-color.org) wins, then FORCE_COLOR, then autodetect TTY.
|
||||
const USE_COLOR = !process.env.NO_COLOR && (process.stdout.isTTY || (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== "0"));
|
||||
function ansi(code, text) {
|
||||
return USE_COLOR ? `\x1b[${code}m${text}\x1b[0m` : text;
|
||||
}
|
||||
const bold = (s) => ansi(1, s);
|
||||
const dim = (s) => ansi(2, s);
|
||||
const cyan = (s) => ansi(36, s);
|
||||
const green = (s) => ansi(32, s);
|
||||
const yellow = (s) => ansi(33, s);
|
||||
const red = (s) => ansi(31, s);
|
||||
const magenta = (s) => ansi(35, s);
|
||||
// `Label: value` with a dimmed label and a colored value.
|
||||
function kv(label, value, color = cyan) {
|
||||
return `${dim(label)}: ${color(value)}`;
|
||||
}
|
||||
|
||||
for (const arg of args) {
|
||||
switch (arg) {
|
||||
case "--":
|
||||
|
|
@ -109,29 +126,29 @@ async function releasePackages(bump) {
|
|||
const releaseTag = `${PACKAGE_TAG_PREFIX}${releaseVersion}`;
|
||||
const workflowArgs = ["workflow", "run", PACKAGES_WORKFLOW, "--repo", REPO, "-f", `version=${releaseVersion}`];
|
||||
|
||||
console.log(`Release target: Node packages / MCP`);
|
||||
console.log(`Current package version: ${latestVersion ?? "none"}`);
|
||||
console.log(kv("Release target", "Node packages / MCP", bold));
|
||||
console.log(kv("Current package version", latestVersion ?? "none", green));
|
||||
printReleaseStatus(status);
|
||||
if (!status.needed && !force) {
|
||||
console.log("No Node package release needed; publish-relevant package files have not changed.");
|
||||
console.log("Use --force to trigger the workflow anyway.");
|
||||
console.log(yellow("No Node package release needed; publish-relevant package files have not changed."));
|
||||
console.log(dim("Use --force to trigger the workflow anyway."));
|
||||
return;
|
||||
}
|
||||
console.log(`New package version: ${releaseVersion}`);
|
||||
console.log(`Release tag: ${releaseTag}`);
|
||||
console.log(`Workflow: Node Packages Release (${PACKAGES_WORKFLOW})`);
|
||||
console.log(`Command: gh ${workflowArgs.join(" ")}`);
|
||||
console.log(kv("New package version", releaseVersion, green));
|
||||
console.log(kv("Release tag", releaseTag, yellow));
|
||||
console.log(kv("Workflow", `Node Packages Release (${PACKAGES_WORKFLOW})`, magenta));
|
||||
console.log(kv("Command", `gh ${workflowArgs.join(" ")}`, dim));
|
||||
|
||||
if (dryRun) {
|
||||
console.log("Dry run only; workflow was not triggered.");
|
||||
console.log(dim("Dry run only; workflow was not triggered."));
|
||||
return;
|
||||
}
|
||||
|
||||
ensureGhReady(PACKAGES_WORKFLOW);
|
||||
await confirmOrExit(`Confirm triggering Node Packages Release for ${releaseVersion}? [y/N] `);
|
||||
await confirmOrExit(`Confirm triggering Node Packages Release for ${bold(releaseVersion)}? [y/N] `);
|
||||
|
||||
run("gh", workflowArgs, { stdio: "inherit" });
|
||||
console.log(`Triggered Node Packages Release for ${releaseVersion}.`);
|
||||
console.log(green(`Triggered Node Packages Release for ${releaseVersion}.`));
|
||||
}
|
||||
|
||||
async function releaseAgents(bump) {
|
||||
|
|
@ -143,28 +160,28 @@ async function releaseAgents(bump) {
|
|||
fail(`Tag ${releaseTag} already exists.`);
|
||||
}
|
||||
|
||||
console.log(`Release target: Agents`);
|
||||
console.log(`Current agent tag: ${latest.tag}${latest.source ? ` (${latest.source})` : ""}`);
|
||||
console.log(kv("Release target", "Agents", bold));
|
||||
console.log(kv("Current agent tag", `${latest.tag}${latest.source ? ` (${latest.source})` : ""}`, yellow));
|
||||
printReleaseStatus(status);
|
||||
if (!status.needed && !force) {
|
||||
console.log("No agents release needed; publish-relevant agent runtime files have not changed.");
|
||||
console.log("Use --force to create the tag anyway.");
|
||||
console.log(yellow("No agents release needed; publish-relevant agent runtime files have not changed."));
|
||||
console.log(dim("Use --force to create the tag anyway."));
|
||||
return;
|
||||
}
|
||||
console.log(`New agent tag: ${releaseTag}`);
|
||||
console.log(`Workflow: Agents Release (.github/workflows/agents-release.yml)`);
|
||||
console.log(`Commands: git tag ${releaseTag} && git push origin ${releaseTag}`);
|
||||
console.log(kv("New agent tag", releaseTag, yellow));
|
||||
console.log(kv("Workflow", "Agents Release (.github/workflows/agents-release.yml)", magenta));
|
||||
console.log(kv("Commands", `git tag ${releaseTag} && git push origin ${releaseTag}`, dim));
|
||||
|
||||
if (dryRun) {
|
||||
console.log("Dry run only; tag was not created or pushed.");
|
||||
console.log(dim("Dry run only; tag was not created or pushed."));
|
||||
return;
|
||||
}
|
||||
|
||||
await confirmOrExit(`Confirm creating and pushing tag ${releaseTag}? [y/N] `);
|
||||
await confirmOrExit(`Confirm creating and pushing tag ${bold(releaseTag)}? [y/N] `);
|
||||
|
||||
run("git", ["tag", releaseTag], { stdio: "inherit" });
|
||||
run("git", ["push", "origin", releaseTag], { stdio: "inherit" });
|
||||
console.log(`Pushed ${releaseTag}; Agents Release will run from the tag push.`);
|
||||
console.log(green(`Pushed ${releaseTag}; Agents Release will run from the tag push.`));
|
||||
}
|
||||
|
||||
async function publishApp(tagInput) {
|
||||
|
|
@ -172,33 +189,33 @@ async function publishApp(tagInput) {
|
|||
const releaseTag = tagInput ? resolveAppTag(tagInput) : latest.tag;
|
||||
const workflowArgs = ["workflow", "run", APP_PUBLISH_WORKFLOW, "--repo", REPO, "-f", `tag=${releaseTag}`];
|
||||
|
||||
console.log(`Release target: App distribution`);
|
||||
console.log(`Latest app tag: ${latest.tag}`);
|
||||
console.log(`Publish tag: ${releaseTag}`);
|
||||
console.log(`Workflow: Publish Packages (${APP_PUBLISH_WORKFLOW})`);
|
||||
console.log(`Command: gh ${workflowArgs.join(" ")}`);
|
||||
console.log(kv("Release target", "App distribution", bold));
|
||||
console.log(kv("Latest app tag", latest.tag, yellow));
|
||||
console.log(kv("Publish tag", releaseTag, yellow));
|
||||
console.log(kv("Workflow", `Publish Packages (${APP_PUBLISH_WORKFLOW})`, magenta));
|
||||
console.log(kv("Command", `gh ${workflowArgs.join(" ")}`, dim));
|
||||
|
||||
if (dryRun) {
|
||||
console.log("Dry run only; workflow was not triggered.");
|
||||
console.log(dim("Dry run only; workflow was not triggered."));
|
||||
return;
|
||||
}
|
||||
|
||||
ensureGhReady(APP_PUBLISH_WORKFLOW);
|
||||
run("gh", ["release", "view", releaseTag, "--repo", REPO], { stdio: "inherit" });
|
||||
await confirmOrExit(`Confirm publishing app distribution for ${releaseTag}? [y/N] `);
|
||||
await confirmOrExit(`Confirm publishing app distribution for ${bold(releaseTag)}? [y/N] `);
|
||||
|
||||
run("gh", workflowArgs, { stdio: "inherit" });
|
||||
console.log(`Triggered Publish Packages for ${releaseTag}.`);
|
||||
console.log(green(`Triggered Publish Packages for ${releaseTag}.`));
|
||||
}
|
||||
|
||||
async function promptTarget() {
|
||||
const packageStatus = getPackageReleaseStatus();
|
||||
const agentStatus = getAgentReleaseStatus(getLatestAgentTag());
|
||||
const answer = await ask(`Select release target:
|
||||
1. Node packages / MCP (${formatStatusSummary(packageStatus)})
|
||||
2. Agents (${formatStatusSummary(agentStatus)})
|
||||
3. App distribution
|
||||
Choice [1]: `);
|
||||
const answer = await ask(`${bold("Select release target:")}
|
||||
${cyan("1")}. Node packages / MCP (${formatStatusSummary(packageStatus)})
|
||||
${cyan("2")}. Agents (${formatStatusSummary(agentStatus)})
|
||||
${cyan("3")}. App distribution
|
||||
${dim("Choice")} [1]: `);
|
||||
|
||||
const normalized = answer.trim().toLowerCase();
|
||||
if (!normalized || normalized === "1" || normalized === "packages" || normalized === "mcp") return "packages";
|
||||
|
|
@ -215,7 +232,7 @@ async function confirmOrExit(message) {
|
|||
|
||||
const answer = await ask(message);
|
||||
if (!["y", "yes"].includes(answer.trim().toLowerCase())) {
|
||||
console.log("Cancelled.");
|
||||
console.log(dim("Cancelled."));
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -337,18 +354,18 @@ function getChangedFilesSince(ref, paths) {
|
|||
}
|
||||
|
||||
function printReleaseStatus(status) {
|
||||
console.log(`Release needed: ${status.needed ? "yes" : "no"}`);
|
||||
console.log(`Compared against: ${status.baseline}`);
|
||||
console.log(kv("Release needed", status.needed ? "yes" : "no", status.needed ? yellow : green));
|
||||
console.log(kv("Compared against", status.baseline, dim));
|
||||
if (status.reason) {
|
||||
console.log(`Reason: ${status.reason}`);
|
||||
console.log(kv("Reason", status.reason, dim));
|
||||
}
|
||||
if (status.changedFiles.length > 0) {
|
||||
console.log(`Changed publish-relevant files:`);
|
||||
console.log(dim("Changed publish-relevant files:"));
|
||||
for (const file of status.changedFiles.slice(0, 20)) {
|
||||
console.log(` - ${file}`);
|
||||
console.log(` ${dim("-")} ${file}`);
|
||||
}
|
||||
if (status.changedFiles.length > 20) {
|
||||
console.log(` ... and ${status.changedFiles.length - 20} more`);
|
||||
console.log(dim(` ... and ${status.changedFiles.length - 20} more`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -356,9 +373,9 @@ function printReleaseStatus(status) {
|
|||
function formatStatusSummary(status) {
|
||||
if (status.needed) {
|
||||
const detail = status.changedFiles.length > 0 ? `${status.changedFiles.length} changed file${status.changedFiles.length === 1 ? "" : "s"}` : "release baseline missing";
|
||||
return `needs release, ${detail}`;
|
||||
return `${yellow("needs release")}, ${detail}`;
|
||||
}
|
||||
return `no release needed since ${status.baseline}`;
|
||||
return `${green("no release needed")} since ${status.baseline}`;
|
||||
}
|
||||
|
||||
function getLatestSemverTag(prefix) {
|
||||
|
|
@ -503,7 +520,7 @@ function escapeRegExp(value) {
|
|||
}
|
||||
|
||||
function fail(message) {
|
||||
console.error(message);
|
||||
console.error(red(message));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue