fix(fpagent): sweep scratch left by a SIGKILLed installer run

The EXIT trap cannot run on SIGKILL, so a killed run could leave its
fpagent-install.* temp dir behind. Sweep those before any network work,
scoped to our prefix and this user and only dirs older than a day so a
concurrent install is never touched.
This commit is contained in:
2026-10-02 10:49:07 +01:00
parent e3392c7750
commit 0698a9c9a9
2 changed files with 53 additions and 10 deletions
+43 -8
View File
@@ -4,11 +4,21 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createApp, FPAGENT_REPO } from '../server.js';
import { startServer, stopServer, urlOf } from './helpers.js';
const SCRIPT = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'..',
'public',
'fpagent',
'install.sh'
);
const TOKEN = 'release-install-secret';
const TAG = 'v9.9.9';
const ARCHIVE = 'fpagent-deadbee-linux-x86_64.tar.gz';
@@ -150,12 +160,37 @@ test('install.sh is served publicly at /fpagent/install.sh', async () => {
});
test('install.sh is valid bash', () => {
const script = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'..',
'public',
'fpagent',
'install.sh'
);
execFileSync('bash', ['-n', script], { stdio: 'pipe' });
execFileSync('bash', ['-n', SCRIPT], { stdio: 'pipe' });
});
test('install.sh sweeps only its own stale scratch dirs', () => {
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'fpagent-sweep-test-'));
const stale = path.join(base, 'fpagent-install.oldoldold');
const fresh = path.join(base, 'fpagent-install.freshfresh');
const unrelated = path.join(base, 'someone-else.oldold');
for (const dir of [stale, fresh, unrelated]) fs.mkdirSync(dir);
const old = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000);
fs.utimesSync(stale, old, old);
fs.utimesSync(unrelated, old, old);
try {
// The base URL is unreachable, so the run fails after the sweep: enough to
// exercise the cleanup without downloading anything.
let failed = false;
try {
execFileSync('bash', [SCRIPT], {
stdio: 'pipe',
env: { ...process.env, TMPDIR: base, FPAGENT_BASE_URL: 'http://127.0.0.1:9' },
});
} catch {
failed = true;
}
assert.equal(failed, true, 'the installer should fail on the unreachable base');
assert.equal(fs.existsSync(stale), false, 'stale fpagent scratch should be removed');
assert.equal(fs.existsSync(fresh), true, 'fresh (possibly concurrent) scratch should survive');
assert.equal(fs.existsSync(unrelated), true, 'another prefix should never be touched');
} finally {
fs.rmSync(base, { recursive: true, force: true });
}
});