197 lines
7.2 KiB
JavaScript
Executable File
197 lines
7.2 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// Cut the Tauri → Electron migration release (v0.12.0 or whatever).
|
|
// Bumps versions, writes the migration-release env fragment, commits, tags,
|
|
// pushes. Idempotent safeties: refuses to run on a dirty tree.
|
|
//
|
|
// Usage:
|
|
// node scripts/migration/01-cut-migration-release.mjs \
|
|
// --version 0.12.0 \
|
|
// --mac-url 'https://.../OpenWork-darwin-arm64-0.12.0-mac.zip' \
|
|
// --mac-arm64-url 'https://.../openwork-mac-arm64-0.12.0.zip' \
|
|
// --mac-x64-url 'https://.../openwork-mac-x64-0.12.0.zip' \
|
|
// --win-url 'https://.../OpenWork-Setup-0.12.0.exe' (optional) \
|
|
// --linux-url 'https://.../OpenWork-0.12.0.AppImage' (optional) \
|
|
// --dry-run
|
|
|
|
import { spawnSync } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = resolve(__dirname, "../..");
|
|
|
|
function parseArgs(argv) {
|
|
const out = { dryRun: false };
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const arg = argv[i];
|
|
if (arg === "--dry-run") out.dryRun = true;
|
|
else if (arg === "--version") out.version = argv[++i];
|
|
else if (arg === "--mac-url") out.macUrl = argv[++i];
|
|
else if (arg === "--mac-arm64-url") out.macArm64Url = argv[++i];
|
|
else if (arg === "--mac-x64-url") out.macX64Url = argv[++i];
|
|
else if (arg === "--mac-sha256") out.macSha256 = argv[++i];
|
|
else if (arg === "--win-url") out.winUrl = argv[++i];
|
|
else if (arg === "--win-x64-url") out.winX64Url = argv[++i];
|
|
else if (arg === "--linux-url") out.linuxUrl = argv[++i];
|
|
else if (arg === "--linux-arm64-url") out.linuxArm64Url = argv[++i];
|
|
else if (arg === "--linux-x64-url") out.linuxX64Url = argv[++i];
|
|
else if (arg === "--help" || arg === "-h") out.help = true;
|
|
else {
|
|
console.error(`unknown arg: ${arg}`);
|
|
process.exit(2);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function die(msg) {
|
|
console.error(`[cut-release] ${msg}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function run(cmd, args, opts = {}) {
|
|
console.log(`[cut-release] $ ${cmd} ${args.join(" ")}`);
|
|
if (opts.dryRun) return "";
|
|
const result = spawnSync(cmd, args, {
|
|
cwd: opts.cwd ?? repoRoot,
|
|
stdio: opts.capture ? ["ignore", "pipe", "inherit"] : "inherit",
|
|
encoding: "utf8",
|
|
});
|
|
if (result.status !== 0) die(`command failed: ${cmd}`);
|
|
return (result.stdout ?? "").trim();
|
|
}
|
|
|
|
function gitStatusClean() {
|
|
const result = spawnSync("git", ["status", "--porcelain"], {
|
|
cwd: repoRoot,
|
|
encoding: "utf8",
|
|
});
|
|
return result.status === 0 && (result.stdout ?? "").trim().length === 0;
|
|
}
|
|
|
|
function currentBranch() {
|
|
const result = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
cwd: repoRoot,
|
|
encoding: "utf8",
|
|
});
|
|
return (result.stdout ?? "").trim();
|
|
}
|
|
|
|
function tagExists(tag) {
|
|
const result = spawnSync("git", ["rev-parse", "-q", "--verify", `refs/tags/${tag}`], {
|
|
cwd: repoRoot,
|
|
encoding: "utf8",
|
|
});
|
|
return result.status === 0;
|
|
}
|
|
|
|
function remoteTagExists(tag) {
|
|
const result = spawnSync("git", ["ls-remote", "--tags", "origin", tag], {
|
|
cwd: repoRoot,
|
|
encoding: "utf8",
|
|
});
|
|
return result.status === 0 && (result.stdout ?? "").includes(`refs/tags/${tag}`);
|
|
}
|
|
|
|
async function main() {
|
|
const args = parseArgs(process.argv.slice(2));
|
|
if (args.help) {
|
|
console.log(
|
|
[
|
|
"Cut the Tauri → Electron migration release.",
|
|
"",
|
|
"Required: --version, --mac-url (or --mac-arm64-url + --mac-x64-url)",
|
|
"Optional: --mac-sha256, --win-url, --win-x64-url, --linux-url, --linux-arm64-url, --linux-x64-url, --dry-run",
|
|
].join("\n"),
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (!args.version) die("--version is required (e.g. --version 0.12.0)");
|
|
if (!args.macUrl && (!args.macArm64Url || !args.macX64Url)) {
|
|
die("--mac-url or both --mac-arm64-url + --mac-x64-url are required");
|
|
}
|
|
if (!/^\d+\.\d+\.\d+$/.test(args.version)) {
|
|
die(`--version must look like X.Y.Z, got "${args.version}"`);
|
|
}
|
|
|
|
if (!gitStatusClean()) {
|
|
die("working tree is dirty. commit or stash before cutting a release.");
|
|
}
|
|
|
|
const branch = currentBranch();
|
|
if (branch !== "dev" && branch !== "main") {
|
|
console.warn(
|
|
`[cut-release] WARNING: current branch is "${branch}". Releases are usually cut from main/dev.`,
|
|
);
|
|
}
|
|
|
|
const tag = `v${args.version}`;
|
|
if (tagExists(tag)) die(`tag ${tag} already exists locally`);
|
|
if (remoteTagExists(tag)) die(`tag ${tag} already exists on origin`);
|
|
|
|
// 1. Bump all 5 sync files via the existing helper.
|
|
run("pnpm", ["bump:set", "--", args.version], { dryRun: args.dryRun });
|
|
|
|
// Keep CI/release jobs using --frozen-lockfile green after bumping
|
|
// workspace package versions referenced by pnpm-lock.yaml.
|
|
run("pnpm", ["install", "--no-frozen-lockfile"], { dryRun: args.dryRun });
|
|
|
|
// 2. Write the migration-release env fragment. Gets picked up by the
|
|
// app build step during `Release App` via --copy-config.
|
|
const envFragment =
|
|
[
|
|
"# Generated by scripts/migration/01-cut-migration-release.mjs.",
|
|
"# Consumed by apps/app Vite build during the v0.12.0 release only.",
|
|
"VITE_OPENWORK_MIGRATION_RELEASE=1",
|
|
`VITE_OPENWORK_MIGRATION_VERSION=${args.version}`,
|
|
args.macUrl ? `VITE_OPENWORK_MIGRATION_MAC_URL=${args.macUrl}` : "",
|
|
args.macArm64Url ? `VITE_OPENWORK_MIGRATION_MAC_ARM64_URL=${args.macArm64Url}` : "",
|
|
args.macX64Url ? `VITE_OPENWORK_MIGRATION_MAC_X64_URL=${args.macX64Url}` : "",
|
|
args.macSha256 ? `VITE_OPENWORK_MIGRATION_MAC_SHA256=${args.macSha256}` : "",
|
|
args.winUrl ? `VITE_OPENWORK_MIGRATION_WINDOWS_URL=${args.winUrl}` : "",
|
|
args.winX64Url ? `VITE_OPENWORK_MIGRATION_WINDOWS_X64_URL=${args.winX64Url}` : "",
|
|
args.linuxUrl ? `VITE_OPENWORK_MIGRATION_LINUX_URL=${args.linuxUrl}` : "",
|
|
args.linuxArm64Url ? `VITE_OPENWORK_MIGRATION_LINUX_ARM64_URL=${args.linuxArm64Url}` : "",
|
|
args.linuxX64Url ? `VITE_OPENWORK_MIGRATION_LINUX_X64_URL=${args.linuxX64Url}` : "",
|
|
"",
|
|
]
|
|
.filter(Boolean)
|
|
.join("\n");
|
|
|
|
const envPath = resolve(repoRoot, "apps/app/.env.migration-release");
|
|
console.log(`[cut-release] writing ${envPath}`);
|
|
if (!args.dryRun) {
|
|
await writeFile(envPath, envFragment, "utf8");
|
|
}
|
|
|
|
// 3. Commit version bump + env fragment.
|
|
run("git", ["add", "-A"], { dryRun: args.dryRun });
|
|
run(
|
|
"git",
|
|
["commit", "-m", `chore(release): cut v${args.version} (Tauri → Electron migration)`],
|
|
{ dryRun: args.dryRun },
|
|
);
|
|
|
|
// 4. Tag + push.
|
|
run("git", ["tag", tag], { dryRun: args.dryRun });
|
|
run("git", ["push", "origin", branch], { dryRun: args.dryRun });
|
|
run("git", ["push", "origin", tag], { dryRun: args.dryRun });
|
|
|
|
console.log("");
|
|
console.log(`[cut-release] pushed ${tag}.`);
|
|
console.log(`[cut-release] watch the workflow:`);
|
|
console.log(` gh run list --repo different-ai/openwork --workflow "Release App" --limit 3`);
|
|
console.log(` gh run watch --repo different-ai/openwork`);
|
|
console.log("");
|
|
console.log(`[cut-release] once the workflow finishes, run:`);
|
|
console.log(` node scripts/migration/02-validate-migration.mjs --tag ${tag}`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|