mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-02 05:24:34 +08:00
fix: correct CLI version mismatch and add release guard (0.8.1) (#123)
* fix: correct CLI version mismatch and add release guard\n\n- Add patch changeset for 0.8.1\n- Add pack-version check to validate tarball version\n- Update release script to include versioning and guard * chore(release): simplify release script and harden pack-version-check\n\n- Run pack-version check before publish only\n- Remove redundant changeset version + explicit build in release script\n- Always cleanup temp dir and tgz\n- Quieter, faster npm install during guard * chore(release): clarify CI vs local release scripts; refine pack guard\n\n- Add scripts: release:ci (no version), release:local (runs changeset version)\n- Workflow uses release:ci to ensure version PR bump precedes publish\n- Pack guard: document npm vs pnpm choice; improve JSON fallback handling
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
Fix CLI version mismatch and add a release guard that validates the packed tarball prints the same version as package.json via `openspec --version`.
|
||||
|
||||
@@ -41,7 +41,9 @@ jobs:
|
||||
with:
|
||||
title: 'chore(release): version packages'
|
||||
createGithubReleases: true
|
||||
publish: pnpm run release
|
||||
# Use CI-specific release script: relies on version PR having been merged
|
||||
# so package.json already contains the bumped version.
|
||||
publish: pnpm run release:ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
+4
-1
@@ -46,7 +46,10 @@
|
||||
"test:coverage": "vitest --coverage",
|
||||
"prepare": "pnpm run build",
|
||||
"prepublishOnly": "pnpm run build",
|
||||
"release": "pnpm run build && pnpm exec changeset publish",
|
||||
"check:pack-version": "node scripts/pack-version-check.mjs",
|
||||
"release": "pnpm run release:ci",
|
||||
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
||||
"release:local": "pnpm exec changeset version && pnpm run check:pack-version && pnpm exec changeset publish",
|
||||
"changeset": "changeset"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env node
|
||||
// Guard: Ensure the packed tarball's CLI `--version` matches package.json.
|
||||
//
|
||||
// Notes:
|
||||
// - We intentionally use `npm pack` (not pnpm) because `npm pack --json` is
|
||||
// consistently supported and returns the tarball metadata we need. The
|
||||
// project uses pnpm for install/publish, but this guard only needs to pack
|
||||
// locally and verify the installed CLI output.
|
||||
// - `npm pack` triggers the package's `prepare` script (build), and
|
||||
// `changeset publish` triggers `prepublishOnly` (also builds here). This
|
||||
// means an explicit build is not strictly necessary for the guard.
|
||||
|
||||
import { execFileSync } from 'child_process';
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import path from 'path';
|
||||
|
||||
function log(msg) {
|
||||
if (process.env.CI) return; // keep CI logs quiet by default
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
function run(cmd, args, opts = {}) {
|
||||
return execFileSync(cmd, args, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], ...opts });
|
||||
}
|
||||
|
||||
function npmPack() {
|
||||
try {
|
||||
const jsonOut = run('npm', ['pack', '--json', '--silent']);
|
||||
const arr = JSON.parse(jsonOut);
|
||||
if (Array.isArray(arr) && arr.length > 0) {
|
||||
const last = arr[arr.length - 1];
|
||||
const file = (last && typeof last === 'object' && last.filename) || (typeof last === 'string' ? last : null);
|
||||
if (file) return String(file).trim();
|
||||
}
|
||||
// Unexpected JSON shape or empty array; fallback to plain output
|
||||
const out = run('npm', ['pack', '--silent']).trim();
|
||||
const lines = out.split(/\r?\n/);
|
||||
return lines[lines.length - 1].trim();
|
||||
} catch (e) {
|
||||
// Fallback for environments not supporting --json
|
||||
const out = run('npm', ['pack', '--silent']).trim();
|
||||
const lines = out.split(/\r?\n/);
|
||||
return lines[lines.length - 1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const pkg = JSON.parse(readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'));
|
||||
const expected = pkg.version;
|
||||
|
||||
let work;
|
||||
let tgzPath;
|
||||
|
||||
try {
|
||||
log(`Packing @fission-ai/openspec@${expected}...`);
|
||||
const filename = npmPack();
|
||||
tgzPath = path.resolve(filename);
|
||||
log(`Created: ${tgzPath}`);
|
||||
|
||||
work = mkdtempSync(path.join(tmpdir(), 'openspec-pack-check-'));
|
||||
log(`Temp dir: ${work}`);
|
||||
|
||||
// Make a tiny project
|
||||
writeFileSync(
|
||||
path.join(work, 'package.json'),
|
||||
JSON.stringify({ name: 'pack-check', private: true }, null, 2)
|
||||
);
|
||||
|
||||
// Try to avoid noisy output and speed up
|
||||
const env = {
|
||||
...process.env,
|
||||
npm_config_loglevel: 'silent',
|
||||
npm_config_audit: 'false',
|
||||
npm_config_fund: 'false',
|
||||
npm_config_progress: 'false',
|
||||
};
|
||||
|
||||
// Install the tarball
|
||||
run('npm', ['install', tgzPath, '--silent', '--no-audit', '--no-fund'], { cwd: work, env });
|
||||
|
||||
// Run the installed CLI via Node to avoid bin resolution/platform issues
|
||||
const binRel = path.join('node_modules', '@fission-ai', 'openspec', 'bin', 'openspec.js');
|
||||
const actual = run(process.execPath, [binRel, '--version'], { cwd: work }).trim();
|
||||
|
||||
if (actual !== expected) {
|
||||
throw new Error(
|
||||
`Packed CLI version mismatch: expected ${expected}, got ${actual}. ` +
|
||||
'Ensure the dist is built and the CLI reads version from package.json.'
|
||||
);
|
||||
}
|
||||
|
||||
log('Version check passed.');
|
||||
} finally {
|
||||
// Always attempt cleanup
|
||||
if (work) {
|
||||
try { rmSync(work, { recursive: true, force: true }); } catch {}
|
||||
}
|
||||
if (tgzPath) {
|
||||
try { rmSync(tgzPath, { force: true }); } catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
console.log('✅ pack-version-check: OK');
|
||||
} catch (err) {
|
||||
console.error(`❌ pack-version-check: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user