mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
224 lines
5.9 KiB
JavaScript
224 lines
5.9 KiB
JavaScript
import { execFileSync } from "node:child_process";
|
|
import {
|
|
cpSync,
|
|
existsSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
readdirSync,
|
|
rmSync,
|
|
writeFileSync,
|
|
} from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { basename, join, resolve } from "node:path";
|
|
|
|
const repo = process.env.GITHUB_REPOSITORY ?? "block/sprout";
|
|
const version = requireVersionEnv();
|
|
const latestTag = "sprout-desktop-latest";
|
|
const tauriTarget = process.env.TAURI_TARGET ?? "aarch64-apple-darwin";
|
|
const updaterPlatform = process.env.UPDATER_PLATFORM ?? "darwin-aarch64";
|
|
const dryRun = process.env.DRY_RUN === "true" || process.env.DRY_RUN === "1";
|
|
|
|
const defaultBundleDirs = [
|
|
`src-tauri/target/${tauriTarget}/release/bundle/macos`,
|
|
"src-tauri/target/release/bundle/macos",
|
|
];
|
|
const bundleDir = resolve(
|
|
process.cwd(),
|
|
process.env.UPDATER_BUNDLE_DIR ??
|
|
defaultBundleDirs.find((dir) => existsSync(resolve(process.cwd(), dir))) ??
|
|
defaultBundleDirs[0],
|
|
);
|
|
const latestPath = join(bundleDir, "latest.json");
|
|
|
|
function requireVersionEnv() {
|
|
const v = process.env.VERSION;
|
|
if (!v || !v.trim()) {
|
|
throw new Error(
|
|
"VERSION env var is required. CI sets this from the git tag; for local use, run: VERSION=x.y.z pnpm run release:updater:publish",
|
|
);
|
|
}
|
|
return v.trim();
|
|
}
|
|
|
|
function requirePath(path) {
|
|
if (!existsSync(path)) {
|
|
throw new Error(`Missing required file: ${path}`);
|
|
}
|
|
}
|
|
|
|
function runGh(args, options = {}) {
|
|
return execFileSync("gh", args, options);
|
|
}
|
|
|
|
function releaseExists(tag) {
|
|
try {
|
|
runGh(["release", "view", tag, "--repo", repo], { stdio: "ignore" });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function ensureRelease(tag, title) {
|
|
if (releaseExists(tag)) {
|
|
return;
|
|
}
|
|
|
|
const args = [
|
|
"release",
|
|
"create",
|
|
tag,
|
|
"--repo",
|
|
repo,
|
|
"--title",
|
|
title,
|
|
"--notes",
|
|
"Automated release placeholder.",
|
|
];
|
|
|
|
runGh(args, { stdio: "inherit" });
|
|
}
|
|
|
|
function readReleaseAssets(tag) {
|
|
const assetsRaw = runGh(
|
|
[
|
|
"release",
|
|
"view",
|
|
tag,
|
|
"--repo",
|
|
repo,
|
|
"--json",
|
|
"assets",
|
|
"--jq",
|
|
".assets[].name",
|
|
],
|
|
{ encoding: "utf-8" },
|
|
);
|
|
return assetsRaw
|
|
.split("\n")
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0);
|
|
}
|
|
|
|
function downloadUrl(name) {
|
|
return `https://github.com/${repo}/releases/download/${latestTag}/${encodeURIComponent(name)}`;
|
|
}
|
|
|
|
function resolveArchivePath() {
|
|
const canonicalArchiveName = "Sprout.app.tar.gz";
|
|
const canonicalPath = join(bundleDir, canonicalArchiveName);
|
|
if (existsSync(canonicalPath)) {
|
|
return canonicalPath;
|
|
}
|
|
|
|
const candidates = readdirSync(bundleDir).filter((entry) =>
|
|
entry.endsWith(".app.tar.gz"),
|
|
);
|
|
if (candidates.length === 1) {
|
|
return join(bundleDir, candidates[0]);
|
|
}
|
|
if (candidates.length === 0) {
|
|
throw new Error(
|
|
`Could not find updater archive in ${bundleDir}. Expected ${canonicalArchiveName}.`,
|
|
);
|
|
}
|
|
throw new Error(
|
|
`Found multiple updater archives in ${bundleDir}: ${candidates.join(", ")}. Cannot determine which to use.`,
|
|
);
|
|
}
|
|
|
|
function buildLatestJson(signaturePath) {
|
|
const signature = readFileSync(signaturePath, "utf-8").trim();
|
|
return {
|
|
version,
|
|
notes: `Release v${version}.`,
|
|
pub_date: new Date().toISOString(),
|
|
platforms: {
|
|
[updaterPlatform]: {
|
|
signature,
|
|
url: "",
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
function main() {
|
|
const archivePath = resolveArchivePath();
|
|
const signaturePath = `${archivePath}.sig`;
|
|
requirePath(archivePath);
|
|
requirePath(signaturePath);
|
|
|
|
const latest = existsSync(latestPath)
|
|
? JSON.parse(readFileSync(latestPath, "utf-8"))
|
|
: buildLatestJson(signaturePath);
|
|
latest.version = version;
|
|
latest.pub_date = new Date().toISOString();
|
|
|
|
const platformRecord = latest?.platforms?.[updaterPlatform];
|
|
if (!platformRecord) {
|
|
const available = Object.keys(latest?.platforms ?? {});
|
|
throw new Error(
|
|
`Platform "${updaterPlatform}" missing in latest.json. Available: ${available.join(", ") || "(none)"}`,
|
|
);
|
|
}
|
|
|
|
const archiveName = basename(archivePath);
|
|
const signatureName = basename(signaturePath);
|
|
platformRecord.signature = readFileSync(signaturePath, "utf-8").trim();
|
|
platformRecord.url = downloadUrl(archiveName);
|
|
|
|
const stageDir = mkdtempSync(join(tmpdir(), "sprout-github-updater-"));
|
|
const stagedLatestPath = join(stageDir, "latest.json");
|
|
|
|
try {
|
|
cpSync(archivePath, join(stageDir, archiveName));
|
|
cpSync(signaturePath, join(stageDir, signatureName));
|
|
writeFileSync(stagedLatestPath, `${JSON.stringify(latest, null, 2)}\n`);
|
|
|
|
const uploadArgs = [
|
|
"release",
|
|
"upload",
|
|
latestTag,
|
|
stagedLatestPath,
|
|
join(stageDir, archiveName),
|
|
join(stageDir, signatureName),
|
|
"--repo",
|
|
repo,
|
|
"--clobber",
|
|
];
|
|
|
|
console.log(`Preparing updater upload for ${repo}`);
|
|
console.log(`- latest tag: ${latestTag}`);
|
|
console.log(`- updater archive: ${archiveName}`);
|
|
console.log(`- updater endpoint: ${downloadUrl("latest.json")}`);
|
|
|
|
if (dryRun) {
|
|
console.log("DRY_RUN enabled. Skipping upload.");
|
|
console.log(
|
|
`gh release view ${latestTag} --repo ${repo} || gh release create ${latestTag} --repo ${repo} --title "Sprout Desktop Latest" --notes "Automated release placeholder."`,
|
|
);
|
|
console.log(`gh ${uploadArgs.join(" ")}`);
|
|
return;
|
|
}
|
|
|
|
ensureRelease(latestTag, "Sprout Desktop Latest");
|
|
runGh(uploadArgs, { stdio: "inherit" });
|
|
|
|
const latestAssets = readReleaseAssets(latestTag);
|
|
for (const expected of ["latest.json", archiveName, signatureName]) {
|
|
if (!latestAssets.includes(expected)) {
|
|
throw new Error(
|
|
`Release ${latestTag} is missing ${expected} after upload`,
|
|
);
|
|
}
|
|
}
|
|
|
|
console.log("GitHub updater assets verified.");
|
|
console.log(`Updater endpoint: ${downloadUrl("latest.json")}`);
|
|
} finally {
|
|
rmSync(stageDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
main();
|