mirror of
https://github.com/different-ai/openwork
synced 2026-04-26 01:25:10 +02:00
- scripts/release/prepare.mjs: bump, lockfile, review, commit, tag - scripts/release/ship.mjs: push tag + dev, print GHA URLs - Both support --dry-run for safe testing - Add pnpm aliases: release:prepare, release:ship, etc.
117 lines
3.8 KiB
JavaScript
117 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* release:ship
|
|
*
|
|
* Pushes the current tag + dev branch to origin, then prints the
|
|
* GitHub Actions workflow URL. Optionally tails the workflow run.
|
|
*
|
|
* Flags:
|
|
* --dry-run Print what would happen without pushing.
|
|
* --watch Tail the GHA workflow run after push.
|
|
*/
|
|
import { execSync } from "node:child_process";
|
|
import { resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
const args = process.argv.slice(2);
|
|
|
|
const dryRun = args.includes("--dry-run");
|
|
const watch = args.includes("--watch");
|
|
|
|
const log = (msg) => console.log(` ${msg}`);
|
|
const heading = (msg) => console.log(`\n▸ ${msg}`);
|
|
const success = (msg) => console.log(` ✓ ${msg}`);
|
|
const fail = (msg) => {
|
|
console.error(` ✗ ${msg}`);
|
|
process.exit(1);
|
|
};
|
|
|
|
const run = (cmd, opts = {}) => {
|
|
if (dryRun && !opts.readOnly) {
|
|
log(`[dry-run] ${cmd}`);
|
|
return "";
|
|
}
|
|
try {
|
|
return execSync(cmd, {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
stdio: opts.inherit ? "inherit" : "pipe",
|
|
}).trim();
|
|
} catch (err) {
|
|
if (opts.allowFail) return "";
|
|
fail(`Command failed: ${cmd}\n${err.stderr || err.message}`);
|
|
}
|
|
};
|
|
|
|
// ── Step 1: Resolve tag from HEAD ───────────────────────────────────
|
|
heading("Resolving tag");
|
|
|
|
const tag = run("git describe --tags --exact-match HEAD", {
|
|
readOnly: true,
|
|
allowFail: true,
|
|
});
|
|
|
|
if (!tag) {
|
|
fail(
|
|
"HEAD is not tagged. Run 'pnpm release:prepare' first.\n" +
|
|
" (Expected a vX.Y.Z tag on HEAD)"
|
|
);
|
|
}
|
|
|
|
if (!/^v\d+\.\d+\.\d+/.test(tag)) {
|
|
fail(`Tag '${tag}' does not look like a release tag (expected vX.Y.Z)`);
|
|
}
|
|
|
|
success(`Found tag: ${tag}`);
|
|
|
|
// ── Step 2: Push tag ────────────────────────────────────────────────
|
|
heading("Pushing tag to origin");
|
|
run(`git push origin ${tag}`);
|
|
success(`Pushed ${tag}`);
|
|
|
|
// ── Step 3: Push dev ────────────────────────────────────────────────
|
|
heading("Pushing dev to origin");
|
|
run("git push origin dev");
|
|
success("Pushed dev");
|
|
|
|
// ── Step 4: Print workflow URL ──────────────────────────────────────
|
|
heading("GitHub Actions");
|
|
|
|
const repo = "different-ai/openwork";
|
|
const url = `https://github.com/${repo}/actions/workflows/release-macos-aarch64.yml`;
|
|
log(`Workflow: ${url}`);
|
|
log(`Release: https://github.com/${repo}/releases/tag/${tag}`);
|
|
|
|
// ── Step 5: Optionally watch ────────────────────────────────────────
|
|
if (watch && !dryRun) {
|
|
heading("Watching workflow run");
|
|
log("Waiting for workflow to appear…");
|
|
|
|
// Give GitHub a moment to register the run
|
|
execSync("sleep 10", { cwd: root });
|
|
|
|
try {
|
|
const runs = run(
|
|
`gh run list --repo ${repo} --workflow "Release App" --limit 1 --json databaseId,headBranch,event -q ".[0].databaseId"`,
|
|
{ readOnly: true }
|
|
);
|
|
if (runs) {
|
|
log(`Run ID: ${runs}`);
|
|
run(`gh run watch ${runs} --repo ${repo} --exit-status`, { inherit: true });
|
|
} else {
|
|
log("Could not find the workflow run. Check the Actions tab manually.");
|
|
}
|
|
} catch {
|
|
log("Workflow watch exited (check status on GitHub).");
|
|
}
|
|
}
|
|
|
|
// ── Summary ─────────────────────────────────────────────────────────
|
|
console.log("\n" + "─".repeat(50));
|
|
console.log(` Shipped: ${tag}`);
|
|
if (dryRun) {
|
|
console.log(" Mode: DRY RUN (nothing was pushed)");
|
|
}
|
|
console.log("─".repeat(50) + "\n");
|