112 lines
3 KiB
JavaScript
112 lines
3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* generate-release-manifest.mjs — emit the immutable build contract consumed by
|
|
* SF server health checks and deployment automation.
|
|
*
|
|
* Purpose: give Forgejo, Kubernetes, and the running web surface one small JSON
|
|
* artifact that proves which source revision, image, and verification gates are
|
|
* being promoted.
|
|
*
|
|
* Consumer: Forgejo self-deploy workflow, Docker image build, and web
|
|
* /api/version readiness surfaces.
|
|
*/
|
|
import { execFileSync } from "node:child_process";
|
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
|
const args = process.argv.slice(2);
|
|
const outIndex = args.indexOf("--out");
|
|
const outPath =
|
|
outIndex >= 0 && args[outIndex + 1]
|
|
? resolve(process.cwd(), args[outIndex + 1])
|
|
: resolve(root, "dist", "sf-release-manifest.json");
|
|
|
|
const packageJson = JSON.parse(
|
|
readFileSync(resolve(root, "package.json"), "utf8"),
|
|
);
|
|
|
|
const manifest = {
|
|
schemaVersion: 1,
|
|
name: packageJson.name,
|
|
version: packageJson.version,
|
|
git: {
|
|
sha:
|
|
env("SF_GIT_SHA") ??
|
|
env("GITHUB_SHA") ??
|
|
env("FORGEJO_SHA") ??
|
|
git(["rev-parse", "HEAD"]),
|
|
ref:
|
|
env("SF_GIT_REF") ??
|
|
env("GITHUB_REF_NAME") ??
|
|
env("FORGEJO_REF_NAME") ??
|
|
env("CI_COMMIT_REF_NAME") ??
|
|
git(["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
},
|
|
image: {
|
|
repository:
|
|
env("SF_IMAGE_REPOSITORY") ??
|
|
env("SF_RELEASE_IMAGE")?.replace(/@sha256:.+$/, "") ??
|
|
null,
|
|
ref: env("SF_RELEASE_IMAGE") ?? null,
|
|
digest: env("SF_RELEASE_IMAGE_DIGEST") ?? null,
|
|
registry: env("SF_RELEASE_REGISTRY") ?? null,
|
|
},
|
|
build: {
|
|
source: env("CI_SERVER_NAME") ?? env("GITHUB_SERVER_URL") ?? "local",
|
|
workflow: env("GITHUB_WORKFLOW") ?? env("CI_WORKFLOW") ?? null,
|
|
runId: env("GITHUB_RUN_ID") ?? env("CI_PIPELINE_ID") ?? null,
|
|
runNumber: env("GITHUB_RUN_NUMBER") ?? env("CI_PIPELINE_NUMBER") ?? null,
|
|
builtAt: new Date().toISOString(),
|
|
node: process.version,
|
|
npm: command(["npm", "--version"]),
|
|
},
|
|
server: {
|
|
defaultHost: "0.0.0.0",
|
|
defaultPort: 4000,
|
|
probes: {
|
|
healthz: "/api/healthz",
|
|
ready: "/api/ready",
|
|
version: "/api/version",
|
|
},
|
|
},
|
|
gates: [
|
|
"npm ci",
|
|
"npm --prefix web ci",
|
|
"npm run build:core",
|
|
"npm run build:web-host",
|
|
"npm run typecheck:extensions",
|
|
"npm run test:unit",
|
|
"server:/api/healthz",
|
|
"server:/api/ready",
|
|
"server:/api/version",
|
|
],
|
|
};
|
|
|
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
writeFileSync(`${outPath}.tmp`, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
renameSync(`${outPath}.tmp`, outPath);
|
|
process.stdout.write(`${outPath}\n`);
|
|
|
|
function env(name) {
|
|
const value = process.env[name]?.trim();
|
|
return value ? value : null;
|
|
}
|
|
|
|
function git(args) {
|
|
return command(["git", ...args]);
|
|
}
|
|
|
|
function command(parts) {
|
|
try {
|
|
return execFileSync(parts[0], parts.slice(1), {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
}).trim();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|