fix(release): enforce binary version integrity pre/post publish
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Build an npm tarball locally and verify that the bundled host-platform native
|
||||
* binary reports the same version as package.json.
|
||||
*
|
||||
* This catches publish drifts where package.json is bumped but the embedded
|
||||
* binary still points to an older fork version.
|
||||
*/
|
||||
|
||||
import { existsSync, mkdtempSync, readFileSync, rmSync, unlinkSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = join(__dirname, '..');
|
||||
|
||||
const pkg = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf8'));
|
||||
const expectedVersion = String(pkg.version || '').trim();
|
||||
|
||||
if (!expectedVersion) {
|
||||
console.error('Error: package.json version is empty');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ext = process.platform === 'win32' ? '.exe' : '';
|
||||
const hostBinaryName = `agent-browser-${process.platform}-${process.arch}${ext}`;
|
||||
|
||||
let tempDir = '';
|
||||
let packedTarball = '';
|
||||
|
||||
try {
|
||||
const packRaw = execFileSync('npm', ['pack', '--json'], {
|
||||
cwd: rootDir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
const parsed = JSON.parse(packRaw);
|
||||
const filename = parsed?.[0]?.filename;
|
||||
if (!filename || typeof filename !== 'string') {
|
||||
throw new Error(`unexpected npm pack output: ${packRaw}`);
|
||||
}
|
||||
|
||||
packedTarball = join(rootDir, filename);
|
||||
if (!existsSync(packedTarball)) {
|
||||
throw new Error(`tarball not found: ${packedTarball}`);
|
||||
}
|
||||
|
||||
tempDir = mkdtempSync(join(tmpdir(), 'ab-pack-verify-'));
|
||||
execFileSync('tar', ['-xzf', packedTarball, '-C', tempDir], {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
const packedRoot = join(tempDir, 'package');
|
||||
const packedPkg = JSON.parse(readFileSync(join(packedRoot, 'package.json'), 'utf8'));
|
||||
if (String(packedPkg.version || '').trim() !== expectedVersion) {
|
||||
throw new Error(
|
||||
`packed package.json version mismatch: expected ${expectedVersion}, got ${packedPkg.version}`
|
||||
);
|
||||
}
|
||||
|
||||
const packedBinary = join(packedRoot, 'bin', hostBinaryName);
|
||||
if (!existsSync(packedBinary)) {
|
||||
throw new Error(`host binary missing in tarball: bin/${hostBinaryName}`);
|
||||
}
|
||||
|
||||
const binaryVersion = execFileSync(packedBinary, ['--version'], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}).trim();
|
||||
|
||||
if (!binaryVersion.includes(expectedVersion)) {
|
||||
throw new Error(
|
||||
`tarball host binary version mismatch: expected ${expectedVersion}, got "${binaryVersion}"`
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`✓ Packed tarball host binary matches package.json (${expectedVersion})`);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.error(`Error: ${message}`);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
||||
if (packedTarball && existsSync(packedTarball)) unlinkSync(packedTarball);
|
||||
}
|
||||
Reference in New Issue
Block a user