Skip to content

Commit 065aa5d

Browse files
authored
Merge pull request #325 from apresmoi/fix/prune-staging-after-publish
fix(publish): delete earlier editions' staged builds after a successful push
2 parents 32281c6 + 0dfbb22 commit 065aa5d

3 files changed

Lines changed: 32 additions & 5 deletions

File tree

‎agentic-org/scripts/publish-edition-branch.mjs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ import { buildBylinesTsv } from './build-bylines-tsv.mjs';
3030
import { CORPUS_PROVENANCE_FILE, hostAssertedProvenance } from './edition-provenance.mjs';
3131
import { PUBLIC_CONTENT_PATHS, isPublicContentPath } from './public-content.mjs';
3232
import { releaseClock } from './release-time.mjs';
33-
import { resolveStagedEdition } from './staged-edition.mjs';
34-
export { artifactDigest, resolveStagedEdition } from './staged-edition.mjs';
33+
import { pruneEarlierEditions, resolveStagedEdition } from './staged-edition.mjs';
34+
export { artifactDigest, pruneEarlierEditions, resolveStagedEdition } from './staged-edition.mjs';
3535
export { CORPUS_PROVENANCE_FILE, CORPUS_PROVENANCE_VERSION, DEFAULT_LANDED_RECORD, LANDED_VERSION, editionCorpusClaim, hostAssertedProvenance, landedCorpus } from './edition-provenance.mjs';
3636

3737
// One ed25519 deploy key per repository, reached through an SSH host alias so
@@ -382,7 +382,8 @@ export async function publishEditionBranch(options, url = remoteUrl(EDITION_PUSH
382382
}
383383

384384
export async function main(argv = process.argv.slice(2)) {
385-
const result = await publishEditionBranch(parseArguments(argv));
385+
const options = parseArguments(argv), result = await publishEditionBranch(options);
386+
if (!options.dryRun && (result.pushed || result.already_published)) await pruneEarlierEditions(options.staging, result.edition).catch((error) => console.warn(`staging prune skipped: ${error.message}`));
386387
// A positive signal, not an exit code: the branch and commit are what a
387388
// person needs to open the pull request, and their absence is the failure.
388389
console.log(JSON.stringify(result, undefined, 2));

‎agentic-org/scripts/staged-edition.mjs‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createHash } from 'node:crypto';
2-
import { lstat, readFile, readdir, readlink, stat } from 'node:fs/promises';
2+
import { lstat, readFile, readdir, readlink, rm, stat } from 'node:fs/promises';
33
import path from 'node:path';
44
import { validateRenderedEvidence } from '../../ops/validate-rendered-evidence.mjs';
55

@@ -94,3 +94,18 @@ async function stagedReceipt(stateRoot, edition, artifact) {
9494
return value;
9595
}
9696

97+
// Each staged artifact is a full site build (~0.5 GB) and only proves the
98+
// edition compiles; once its branch is pushed it is never read again. Only
99+
// earlier editions go: today's may still be restaged for a late addition, and a
100+
// candidate pressman is writing right now always carries today's date.
101+
export async function pruneEarlierEditions(stagingRoot, edition) {
102+
const removed = [];
103+
for (const name of await readdir(stagingRoot)) {
104+
const date = name.slice(0, 10);
105+
if (!EDITION_PATTERN.test(date) || name[10] !== '-' || date >= edition) continue;
106+
await rm(path.join(stagingRoot, name), { recursive: true, force: true });
107+
removed.push(name);
108+
}
109+
return removed;
110+
}
111+

‎agentic-org/scripts/staged-edition.test.mjs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
33
import { mkdir, mkdtemp, readFile, readdir, rename, rm, symlink, writeFile } from 'node:fs/promises';
44
import { tmpdir } from 'node:os';
55
import path from 'node:path';
6-
import { artifactDigest, resolveStagedEdition } from './staged-edition.mjs';
6+
import { artifactDigest, pruneEarlierEditions, resolveStagedEdition } from './staged-edition.mjs';
77

88
const edition = '2026-09-11';
99
async function stages(t) {
@@ -101,3 +101,14 @@ test('publication rechecks final evidence even when the receipt authenticates co
101101
await writeFile(latest.file, JSON.stringify(latest.receipt));
102102
assert.equal((await f.resolve()).artifact, latest.artifact);
103103
});
104+
105+
test('pruning after a publish removes only earlier editions and keeps the current link working', async t => {
106+
const f = await stages(t);
107+
const older = ['2026-09-09-aaaaaaaaaaaaaaaa', '2026-09-10-bbbbbbbbbbbbbbbb'];
108+
for (const name of older) await mkdir(path.join(f.staging, name, 'website'), { recursive: true });
109+
await mkdir(path.join(f.staging, 'scratch'));
110+
assert.deepEqual((await pruneEarlierEditions(f.staging, edition)).sort(), older);
111+
const left = (await readdir(f.staging)).sort();
112+
assert.deepEqual(left, ['current-edition', ...f.entries.map(e => e.name), 'scratch'].sort());
113+
assert.equal((await f.resolve()).artifact, f.entries.at(-1).artifact);
114+
});

0 commit comments

Comments
 (0)