Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 539d615aef3390a8eae087be907cded1328bbdc8
parent 6db57f62bd7cd8c2d2fbf5893f7f3197fca25e90
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue,  6 Oct 2026 08:50:04 -0400

publish: stamps fall back to the image's ARCHILYZER_COMMIT/BRANCH without a .git; deploy-homepage --to local writes ARCHILYZER_HOMEPAGE_OUT

Both names are release 18 S5's (declared in lib/envVars.ts there, with
imageBuildFacts); read by name here until the two slices are merged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mcommon/publish/stageBodies.ts | 31+++++++++++++++++++------------
Mcommon/publish/stamps.test.ts | 7+++++++
Mcommon/publish/stamps.ts | 16++++++++++++++++
3 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/common/publish/stageBodies.ts b/common/publish/stageBodies.ts @@ -33,6 +33,7 @@ import { ALL_TARGET, HOMEPAGE_TARGET, HUB_TARGET, + imageBuildFacts, newStampId, readBuiltStamp, readDeployedFile, @@ -93,12 +94,16 @@ async function git(cwd: string, args: string[]): Promise<string | null> { } } -/** The checkout's commit and branch (null where there is no repository). */ -export async function checkoutInfo(paths: Pick<Paths, "monorepoRoot">): Promise<{ - commit: string | null; - branch: string | null; -}> { +/** + * The checkout's commit and branch; where there is no repository (the runtime + * image), the commit and branch the image was built from; else null. + */ +export async function checkoutInfo( + paths: Pick<Paths, "monorepoRoot">, + env: NodeJS.ProcessEnv = process.env, +): Promise<{ commit: string | null; branch: string | null }> { const commit = await git(paths.monorepoRoot, ["rev-parse", "HEAD"]); + if (commit === null) return imageBuildFacts(env); const branch = await git(paths.monorepoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]); return { commit, branch: branch === "HEAD" ? null : branch }; } @@ -506,10 +511,12 @@ export function localSiteOut(env: NodeJS.ProcessEnv = process.env): string | nul return env.ARCHILYZER_SITE_OUT?.trim() || null; } -/** …and the homepage: `homepage` beside it (the `homepage` service's mount). */ +// …and the homepage: what the `homepage` service serves (release 18 S5 +// declares the name; read by name here until both slices are merged). +const HOMEPAGE_OUT_NAME = "ARCHILYZER_HOMEPAGE_OUT"; + export function localHomepageOut(env: NodeJS.ProcessEnv = process.env): string | null { - const site = localSiteOut(env); - return site ? path.join(path.dirname(site), "homepage") : null; + return env[HOMEPAGE_OUT_NAME]?.trim() || null; } // Replace the CONTENTS of `dest` (a volume mount: never the directory itself). @@ -537,7 +544,7 @@ async function deployStage( r: StageRequest, target: string, ship: (o: { onLog: (l: string) => void; previewBranch?: string }) => Promise<void>, - local: { src: string; dest: string | null } | null, + local: { src: string; dest: string | null; needs: string } | null, project: string | null, ): Promise<StageOutcome> { const { paths, onLog, signal } = ctx; @@ -548,7 +555,7 @@ async function deployStage( if (kind === "local") { if (!local) throw new StageFailure(`${target} has no local target`, 2); if (!local.dest) { - throw new StageFailure("--to local needs ARCHILYZER_SITE_OUT (the directory the docker `site` service serves)", 3); + throw new StageFailure(`--to local needs ${local.needs}`, 3); } // A bundle built private is never published, here either. const priv = builtAudienceProblem(local.src); @@ -660,7 +667,7 @@ export async function runStageBody(ctx: StageContext, r: StageRequest): Promise< outDir: out, stagingDir: b.dockerSiteStagingDir(paths, site.siteId), }), - { src: out, dest: localSiteOut() }, + { src: out, dest: localSiteOut(), needs: "ARCHILYZER_SITE_OUT (the directory the docker `site` service serves)" }, site.cloudflareProject?.trim() || null, ); } @@ -686,7 +693,7 @@ export async function runStageBody(ctx: StageContext, r: StageRequest): Promise< r, HOMEPAGE_TARGET, (o) => b.deployHomepage({ paths, signal: ctx.signal, onLog: o.onLog, previewBranch: o.previewBranch }), - { src: b.homepageOutDir(paths), dest: localHomepageOut() }, + { src: b.homepageOutDir(paths), dest: localHomepageOut(), needs: "ARCHILYZER_HOMEPAGE_OUT (the directory the docker `homepage` service serves)" }, b.HOMEPAGE_PAGES_PROJECT, ); } diff --git a/common/publish/stamps.test.ts b/common/publish/stamps.test.ts @@ -10,6 +10,7 @@ import { builtStampPath, deployRecordFor, deployedPath, + imageBuildFacts, indexStampPath, newStampId, readBuiltStamp, @@ -123,6 +124,12 @@ test("recordDeploy keeps every other record, and deployRecordFor finds each kind } }); +test("imageBuildFacts: the image's commit and branch, empty = null", () => { + assert.deepEqual(imageBuildFacts({ ARCHILYZER_COMMIT: " abc ", ARCHILYZER_BRANCH: "" }), { commit: "abc", branch: null }); + assert.deepEqual(imageBuildFacts({}), { commit: null, branch: null }); + assert.deepEqual(imageBuildFacts({ ARCHILYZER_COMMIT: "c", ARCHILYZER_BRANCH: "main" }), { commit: "c", branch: "main" }); +}); + test("newStampId is unique and sorts by time", () => { const a = newStampId(1_000); const b = newStampId(2_000); diff --git a/common/publish/stamps.ts b/common/publish/stamps.ts @@ -135,6 +135,22 @@ export function deployedPath(paths: Pick<Paths, "exportBuildsDir">, target: stri return path.join(targetDir(paths, target), "deployed.json"); } +// The runtime image's build facts (Dockerfile build args → ENV): the stamps' +// `commit` / `branch` where there is no .git to ask. Release 18 S5 declares +// the two names and exports the same helper (`imageBuildFacts`, +// lib/envVars.ts); this local one is swapped for it in one line once both +// slices are merged. Empty = null. +const IMAGE_COMMIT_NAME = "ARCHILYZER_COMMIT"; +const IMAGE_BRANCH_NAME = "ARCHILYZER_BRANCH"; + +export function imageBuildFacts(env: NodeJS.ProcessEnv = process.env): { + commit: string | null; + branch: string | null; +} { + const v = (k: string) => env[k]?.trim() || null; + return { commit: v(IMAGE_COMMIT_NAME), branch: v(IMAGE_BRANCH_NAME) }; +} + /** A fresh, sortable, unique stamp id. */ export function newStampId(now = Date.now()): string { return `${now.toString(36).padStart(9, "0")}-${randomBytes(4).toString("hex")}`;