| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- import { spawnSync } from 'node:child_process';
- import {
- copyFileSync,
- mkdirSync,
- mkdtempSync,
- readdirSync,
- readFileSync,
- rmSync,
- writeFileSync,
- } from 'node:fs';
- import { tmpdir } from 'node:os';
- import path from 'node:path';
- import { fileURLToPath } from 'node:url';
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const repoRoot = path.resolve(__dirname, '..');
- const distDir = path.join(repoRoot, 'dist');
- const suspiciousPathPatterns = [
- /\/Users\/[^\s'"`]+(?:node_modules|oh-my-opencode-slim)[^\s'"`]*/,
- /\/home\/[^\s'"`]+(?:node_modules|oh-my-opencode-slim)[^\s'"`]*/,
- ];
- const suspiciousImportPatterns = [/from\s+["']vscode-jsonrpc\/node["']/];
- const packagedRequiredFiles = [
- 'package.json',
- 'README.md',
- 'LICENSE',
- 'dist/index.js',
- 'dist/index.d.ts',
- 'dist/cli/index.js',
- 'oh-my-opencode-slim.schema.json',
- 'src/companion/companion-manifest.json',
- 'src/skills/simplify/SKILL.md',
- 'src/skills/codemap/SKILL.md',
- 'src/skills/clonedeps/SKILL.md',
- 'src/skills/deepwork/SKILL.md',
- 'src/skills/verification-planning/SKILL.md',
- 'src/skills/reflect/SKILL.md',
- 'src/skills/oh-my-opencode-slim/SKILL.md',
- 'src/skills/worktrees/SKILL.md',
- ];
- function fail(message: string): never {
- throw new Error(message);
- }
- function run(command: string, args: string[], options: { cwd?: string } = {}) {
- const result = spawnSync(command, args, {
- cwd: options.cwd ?? repoRoot,
- encoding: 'utf8',
- stdio: ['ignore', 'pipe', 'pipe'],
- });
- if (result.status !== 0) {
- const detail = [result.stdout, result.stderr].filter(Boolean).join('\n');
- fail(
- `Command failed: ${command} ${args.join(' ')}${detail ? `\n${detail}` : ''}`,
- );
- }
- return result.stdout.trim();
- }
- function parsePackJson(output: string) {
- const start = output.indexOf('[');
- const end = output.lastIndexOf(']');
- if (start === -1 || end === -1 || end < start) {
- fail(`Could not locate npm pack JSON output:\n${output}`);
- }
- return JSON.parse(output.slice(start, end + 1)) as Array<{
- filename?: string;
- files?: Array<{ path: string }>;
- }>;
- }
- function walkFiles(dir: string): string[] {
- const entries = readdirSync(dir, { withFileTypes: true });
- return entries.flatMap((entry) => {
- const fullPath = path.join(dir, entry.name);
- if (entry.isDirectory()) return walkFiles(fullPath);
- return [fullPath];
- });
- }
- function verifyDistHasNoLeakedPaths() {
- console.log('Checking dist for leaked machine paths...');
- const files = walkFiles(distDir).filter((file) =>
- /\.(?:js|d\.ts|map|json)$/.test(file),
- );
- const leaks: string[] = [];
- for (const file of files) {
- const content = readFileSync(file, 'utf8');
- for (const pattern of suspiciousPathPatterns) {
- const match = content.match(pattern);
- if (!match) continue;
- leaks.push(`${path.relative(repoRoot, file)}: ${match[0]}`);
- }
- for (const pattern of suspiciousImportPatterns) {
- const match = content.match(pattern);
- if (!match) continue;
- leaks.push(`${path.relative(repoRoot, file)}: ${match[0]}`);
- }
- }
- if (leaks.length > 0) {
- fail(
- `Built artifact contains machine-specific paths:\n${leaks.join('\n')}`,
- );
- }
- }
- function packArtifact() {
- console.log('Packing npm artifact...');
- const output = run('npm', ['pack', '--json', '--ignore-scripts'], {
- cwd: repoRoot,
- });
- const parsed = parsePackJson(output);
- const tarball = parsed[0]?.filename;
- if (!tarball) {
- fail(`npm pack did not return a tarball filename:\n${output}`);
- }
- const packagedFiles = new Set(
- (parsed[0]?.files ?? []).map((file) => file.path),
- );
- for (const requiredFile of packagedRequiredFiles) {
- if (!packagedFiles.has(requiredFile)) {
- fail(`npm pack artifact is missing required file: ${requiredFile}`);
- }
- }
- return path.join(repoRoot, tarball);
- }
- function verifyFreshInstall(tarballPath: string) {
- const tempRoot = mkdtempSync(path.join(tmpdir(), 'omos-release-'));
- try {
- console.log('Installing packed artifact into clean temp project...');
- const installDir = path.join(tempRoot, 'install');
- const tarballTarget = path.join(tempRoot, path.basename(tarballPath));
- copyFileSync(tarballPath, tarballTarget);
- mkdirSync(installDir, { recursive: true });
- writeFileSync(
- path.join(installDir, 'package.json'),
- JSON.stringify(
- { name: 'verify-release-artifact', private: true },
- null,
- 2,
- ),
- );
- run('bun', ['add', '--ignore-scripts', tarballTarget], {
- cwd: installDir,
- });
- const installedEntry = path.join(
- installDir,
- 'node_modules',
- 'oh-my-opencode-slim',
- 'dist',
- 'index.js',
- );
- const installedEntryContent = readFileSync(installedEntry, 'utf8');
- for (const pattern of suspiciousPathPatterns) {
- const match = installedEntryContent.match(pattern);
- if (match) {
- fail(
- `Installed package still contains machine-specific path: ${match[0]}`,
- );
- }
- }
- const smokeScript = [
- "import pkg from 'oh-my-opencode-slim';",
- "if (typeof pkg !== 'function') throw new Error('default export is not a function');",
- "console.log('package loads');",
- 'process.exit(0);',
- ].join('\n');
- console.log('Importing installed package entrypoint...');
- run('node', ['--input-type=module', '--eval', smokeScript], {
- cwd: installDir,
- });
- } finally {
- rmSync(tempRoot, { recursive: true, force: true });
- }
- }
- function cleanupTarball(tarballPath: string) {
- rmSync(tarballPath, { force: true });
- }
- function main() {
- verifyDistHasNoLeakedPaths();
- const tarballPath = packArtifact();
- try {
- verifyFreshInstall(tarballPath);
- } finally {
- cleanupTarball(tarballPath);
- }
- console.log('Release artifact verification passed.');
- }
- main();
|