fix: accept published installer migration checksums

This commit is contained in:
Colin
2026-06-04 12:23:29 -04:00
parent e8dc420b4d
commit b8e15c7a98
12 changed files with 198 additions and 3 deletions

View File

@@ -56,9 +56,18 @@ describe('ci-test-scope.cjs', () => {
assert.strictEqual(result.code_changed, true);
assert.strictEqual(result.full_matrix, true);
assert.ok(result.targeted_tests.includes('tests/install.test.cjs'));
assert.ok(result.targeted_tests.includes('tests/installer-migration-checksum-compat.test.cjs'));
assert.ok(result.targeted_tests.includes('tests/release-tarball-smoke.install.test.cjs'));
});
test('ADR-457 installer source changes wake scoped CI and checksum compatibility guard', () => {
const result = scopeFor(['src/installer-migrations/000-first-time-baseline.cts']);
assert.strictEqual(result.code_changed, true);
assert.strictEqual(result.full_matrix, true);
assert.ok(result.targeted_tests.includes('tests/installer-migration-checksum-compat.test.cjs'));
assert.ok(result.windows_tests.includes('tests/installer-migration-checksum-compat.test.cjs'));
});
test('missing required CLI values fail with usage', () => {
const r = spawnSync(process.execPath, [SCRIPT, '--files'], {
cwd: ROOT,

View File

@@ -0,0 +1,49 @@
{
"_comment": "Published installer migration checksums that must remain accepted by future releases. If an existing migration implementation changes, preserve compatibility here via legacyChecksums or create a new fix-forward migration id.",
"migrations": {
"2026-05-11-first-time-baseline-scan": {
"published": [
{
"version": "1.2.0",
"checksum": "sha256:34608ea4e2f4e1c53b069604892860e603600d8573cc6a5584e4194044b48e67"
},
{
"version": "1.3.0",
"checksum": "sha256:4ec58d35b30dbf39cc56e3972146086d8d31861ecd800cf0b37a7aa94fe74c2a"
}
]
},
"2026-05-11-legacy-orphan-files": {
"published": [
{
"version": "1.2.0",
"checksum": "sha256:4488e38c127a5225b31016918bcbc85ba3fd3139291ad407b94e76c03c0b89d3"
},
{
"version": "1.3.0",
"checksum": "sha256:e492698748a2436a12a55f0940f539b9bf651d8ffcac6f60cd856a6dabd6788c"
}
]
},
"2026-05-11-codex-legacy-hooks-json": {
"published": [
{
"version": "1.2.0",
"checksum": "sha256:41f1545704dc72dfc3ab019207677a8652e389b200e8c450d52317df5bc198da"
},
{
"version": "1.3.0",
"checksum": "sha256:5ce55294aa02f25758f604a569c899a6d2d060299189f5f447f68d8033157058"
}
]
},
"2026-06-02-rename-get-shit-done-to-gsd-core": {
"published": [
{
"version": "1.3.0",
"checksum": "sha256:3a9f1d97f64097fb313203d19c6d93a187a38df61dd299afa5eef73e16124e95"
}
]
}
}
}

View File

@@ -0,0 +1,94 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const {
computeInstallerMigrationChecksum,
discoverInstallerMigrations,
planInstallerMigrations,
} = require('../gsd-core/bin/lib/installer-migrations.cjs');
const { cleanup } = require('./helpers.cjs');
const FIXTURE = require('./fixtures/installer-migrations/published-checksums.json');
const MIGRATIONS_DIR = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'installer-migrations');
function createConfigDir() {
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-migration-checksum-compat-'));
fs.writeFileSync(
path.join(configDir, 'gsd-file-manifest.json'),
JSON.stringify({ version: 'compat-fixture', timestamp: '2026-06-04T00:00:00.000Z', mode: 'full', files: {} }, null, 2),
'utf8'
);
return configDir;
}
function writeAppliedState(configDir, migration, checksum, version) {
fs.writeFileSync(
path.join(configDir, 'gsd-install-state.json'),
JSON.stringify({
schemaVersion: 1,
appliedMigrations: [
{
id: migration.id,
checksum,
appliedAt: '2026-06-04T00:00:00.000Z',
packageVersion: version,
journal: 'gsd-migration-journal/published-compat-fixture.json',
},
],
}, null, 2),
'utf8'
);
}
function planContextFor(migration) {
return {
runtime: Array.isArray(migration.runtimes) && migration.runtimes.length > 0 ? migration.runtimes[0] : 'claude',
scope: Array.isArray(migration.scopes) && migration.scopes.length > 0 ? migration.scopes[0] : 'global',
};
}
test('shipped installer migration checksums remain accepted for upgrade compatibility', () => {
const migrations = discoverInstallerMigrations({ migrationsDir: MIGRATIONS_DIR });
const byId = new Map(migrations.map((migration) => [migration.id, migration]));
const fixtureIds = new Set(Object.keys(FIXTURE.migrations));
for (const migration of migrations) {
assert.ok(
fixtureIds.has(migration.id),
`current migration must be pinned in published-checksums.json: ${migration.id}`
);
}
for (const [migrationId, fixture] of Object.entries(FIXTURE.migrations)) {
const migration = byId.get(migrationId);
assert.ok(migration, `published migration fixture no longer exists: ${migrationId}`);
const currentChecksum = computeInstallerMigrationChecksum(migration);
assert.ok(
fixture.published.some((entry) => entry.checksum === currentChecksum),
`${migrationId} current checksum ${currentChecksum} is not pinned in published-checksums.json`
);
for (const { version, checksum } of fixture.published) {
const configDir = createConfigDir();
try {
writeAppliedState(configDir, migration, checksum, version);
assert.doesNotThrow(
() => planInstallerMigrations({
configDir,
migrations: [migration],
...planContextFor(migration),
}),
`${migrationId} must accept checksum ${checksum} from ${version}`
);
} finally {
cleanup(configDir);
}
}
}
});