117 lines
3.9 KiB
JavaScript
117 lines
3.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { createHash } from 'node:crypto';
|
|
import { lstatSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { OPENMAIC_PACKAGES, readManifest } from './openmaic-packages.mjs';
|
|
|
|
const DIGESTS_FILE = 'SHA256SUMS';
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
|
function expectedArtifacts() {
|
|
return OPENMAIC_PACKAGES.map((shortName) => {
|
|
const manifest = readManifest(shortName);
|
|
return {
|
|
shortName,
|
|
name: `@openmaic/${shortName}`,
|
|
version: manifest.version,
|
|
filename: `openmaic-${shortName}-${manifest.version}.tgz`,
|
|
};
|
|
});
|
|
}
|
|
|
|
function sha256(path) {
|
|
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
}
|
|
|
|
function readPackedManifest(path) {
|
|
return JSON.parse(
|
|
execFileSync('tar', ['-xzOf', path, 'package/package.json'], { encoding: 'utf8' }),
|
|
);
|
|
}
|
|
|
|
function assertArtifactSet(directory, expected, includeDigests) {
|
|
const expectedNames = new Set(expected.map(({ filename }) => filename));
|
|
if (includeDigests) expectedNames.add(DIGESTS_FILE);
|
|
|
|
const entries = readdirSync(directory, { withFileTypes: true });
|
|
assert.deepEqual(
|
|
entries.map(({ name }) => name).sort(),
|
|
[...expectedNames].sort(),
|
|
`Release artifact directory must contain exactly ${[...expectedNames].sort().join(', ')}`,
|
|
);
|
|
for (const entry of entries) {
|
|
const path = join(directory, entry.name);
|
|
assert(
|
|
entry.isFile() && !lstatSync(path).isSymbolicLink(),
|
|
`${entry.name} must be a regular file`,
|
|
);
|
|
}
|
|
|
|
for (const artifact of expected) {
|
|
const manifest = readPackedManifest(join(directory, artifact.filename));
|
|
assert.equal(
|
|
manifest.name,
|
|
artifact.name,
|
|
`${artifact.filename} contains package ${manifest.name}`,
|
|
);
|
|
assert.equal(
|
|
manifest.version,
|
|
artifact.version,
|
|
`${artifact.filename} contains version ${manifest.version}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function writeDigests(directory, expected) {
|
|
assertArtifactSet(directory, expected, false);
|
|
const contents = expected
|
|
.map(({ filename }) => `${sha256(join(directory, filename))} ${filename}`)
|
|
.join('\n');
|
|
writeFileSync(join(directory, DIGESTS_FILE), `${contents}\n`);
|
|
console.log(`Recorded SHA-256 digests for ${expected.length} package tarballs.`);
|
|
}
|
|
|
|
function verifyDigests(directory, expected) {
|
|
assertArtifactSet(directory, expected, true);
|
|
const lines = readFileSync(join(directory, DIGESTS_FILE), 'utf8').trimEnd().split('\n');
|
|
const recorded = new Map();
|
|
|
|
for (const line of lines) {
|
|
const match = /^([0-9a-f]{64}) ([^/]+\.tgz)$/.exec(line);
|
|
assert(match, `Invalid ${DIGESTS_FILE} entry: ${JSON.stringify(line)}`);
|
|
assert(!recorded.has(match[2]), `${DIGESTS_FILE} lists ${match[2]} more than once`);
|
|
recorded.set(match[2], match[1]);
|
|
}
|
|
|
|
assert.deepEqual(
|
|
[...recorded.keys()].sort(),
|
|
expected.map(({ filename }) => filename).sort(),
|
|
`${DIGESTS_FILE} must list every allowed package tarball exactly once`,
|
|
);
|
|
|
|
for (const { filename } of expected) {
|
|
assert.equal(
|
|
sha256(join(directory, filename)),
|
|
recorded.get(filename),
|
|
`${filename} failed SHA-256 verification`,
|
|
);
|
|
}
|
|
console.log(`Verified SHA-256 digests for ${expected.length} package tarballs.`);
|
|
}
|
|
|
|
const args = process.argv.slice(2);
|
|
const write = args[0] === '--write';
|
|
const directoryArgument = args[write ? 1 : 0];
|
|
assert(
|
|
directoryArgument,
|
|
`Usage: node ${basename(process.argv[1])} [--write] <artifact-directory>`,
|
|
);
|
|
assert.equal(args.length, write ? 2 : 1, 'Unexpected command-line arguments');
|
|
|
|
const directory = resolve(root, directoryArgument);
|
|
const expected = expectedArtifacts();
|
|
if (write) writeDigests(directory, expected);
|
|
else verifyDigests(directory, expected);
|