fix(runtime): remove build staging folders left over by killed builds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
76f88163d1
commit
4fbb2e84c8
2 files changed
+43
-1
No files matched your search
+17
-1
@@ -1,5 +1,5 @@
|
||||
import { build } from 'astro';
|
||||
import { cp, mkdir, rm } from 'node:fs/promises';
|
||||
import { cp, mkdir, readdir, rm, stat } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -10,6 +10,21 @@ import { clashingPostWarnings, reservedPostWarnings } from '../site/data.ts';
|
||||
|
||||
export const APP_ROOT = fileURLToPath(new URL('../..', import.meta.url));
|
||||
|
||||
const STALE_STAGING_MS = 3_600_000;
|
||||
|
||||
/**
|
||||
* Removes `build-*` staging folders in `dir` older than an hour: leftovers of builds that were killed
|
||||
* (timeout, container stop) before their own cleanup ran. Younger ones may belong to a running build.
|
||||
*/
|
||||
export async function removeStaleStaging(dir: string, now = Date.now()): Promise<void> {
|
||||
const names = await readdir(dir).catch(() => [] as string[]);
|
||||
for (const name of names.filter((n) => n.startsWith('build-'))) {
|
||||
const path = join(dir, name);
|
||||
const info = await stat(path).catch(() => null);
|
||||
if (info?.isDirectory() && now - info.mtimeMs > STALE_STAGING_MS) await rm(path, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a content directory, fetches the external sources once, and renders the static site
|
||||
* into `outDir`. `cacheDir` holds the per-source caches (so an outage falls back to the last good
|
||||
@@ -25,6 +40,7 @@ export async function buildSite(contentDir: string, outDir: string, opts: { cach
|
||||
const cacheDir = resolve(opts.cacheDir ?? join(APP_ROOT, '.tmp', 'sources'));
|
||||
const { content, errors } = await loadContent(source);
|
||||
if (!content) throw new ContentError(errors);
|
||||
await removeStaleStaging(join(APP_ROOT, '.tmp'));
|
||||
|
||||
await mkdir(cacheDir, { recursive: true });
|
||||
const sources = await loadSources({ cacheDir, env: process.env });
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { expect, test } from 'bun:test';
|
||||
import { existsSync, mkdirSync, mkdtempSync, utimesSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { removeStaleStaging } from '../../src/runtime/build.ts';
|
||||
|
||||
test('removes build staging folders older than an hour, keeps fresh ones and everything else', async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'site-tmp-'));
|
||||
const now = Date.now();
|
||||
const make = (name: string, ageMs: number) => {
|
||||
const p = join(dir, name);
|
||||
mkdirSync(p);
|
||||
writeFileSync(join(p, 'index.html'), 'x');
|
||||
const t = new Date(now - ageMs);
|
||||
utimesSync(p, t, t);
|
||||
return p;
|
||||
};
|
||||
const stale = make('build-old', 2 * 3_600_000);
|
||||
const fresh = make('build-new', 10 * 60_000);
|
||||
const other = make('sources', 5 * 3_600_000);
|
||||
await removeStaleStaging(dir, now);
|
||||
expect(existsSync(stale)).toBe(false);
|
||||
expect(existsSync(fresh)).toBe(true);
|
||||
expect(existsSync(other)).toBe(true);
|
||||
await removeStaleStaging(join(dir, 'missing'), now); // no folder yet: nothing to do
|
||||
});
|
||||
Reference in new issue
Block a user