mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 22:26:38 +03:00
The engine binaries move from the impeccable-dist channel to this repo's own GitHub Releases (tag engine-v<ENGINE_VERSION>), and the closed detector the engine links arrives as detector-v<DETECTOR_VERSION> releases on the same repo. This commit wires the public side for that; the crates themselves land in the next commit. - Launcher (sh + cmd), npm shim, fetch-engine and check-engine-release now download from github.com/pbakaus/impeccable/releases/download/engine-v<X>/. - release.mjs gains `engine`: verifies ENGINE_VERSION against the platform package pins and the detector release, tags, pushes; release-engine.yml builds the five targets and publishes. check-detector-release.mjs is the matching release-order guard (with tests). - Root Cargo.toml (workspace, lto = false with the reason), rust-toolchain.toml (exact pin), DETECTOR_VERSION, /target ignored. - CI: rust + rust-windows jobs and an oracle job that replays the goldens against a source build, warn-only until the first detector release exists; ci-test-plan exposes a `rust` output. - docs/ENGINE.md (the crate map and the closed-detector mechanism) and the CLAUDE.md engine, release-order and rules sections. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vau2X53xGTjjTCXWMVBoNY
73 lines
3.3 KiB
JavaScript
Executable File
73 lines
3.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// `impeccable` npm shim: finds the platform binary and execs it with argv.
|
|
// Order: $IMPECCABLE_BIN, the @impeccable/cli-<os>-<arch> optional dependency,
|
|
// the version-pinned user cache (~/.impeccable/bin/<version>/), then a
|
|
// download into that cache from the public release channel.
|
|
import { spawnSync } from 'node:child_process';
|
|
import { createHash } from 'node:crypto';
|
|
import fs from 'node:fs';
|
|
import { createRequire } from 'node:module';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const pkg = require('../../package.json');
|
|
const OS = { darwin: 'darwin', linux: 'linux', win32: 'windows' }[process.platform] || process.platform;
|
|
const ARCH = { arm64: 'arm64', x64: 'x64' }[process.arch] || process.arch;
|
|
const TARGET = `${OS}-${ARCH}`;
|
|
const EXE = OS === 'windows' ? 'impeccable.exe' : 'impeccable';
|
|
const PLATFORM_PKG = `@impeccable/cli-${TARGET}`;
|
|
// The engine version travels as the pinned optionalDependency range.
|
|
const VERSION = String(pkg.optionalDependencies?.[PLATFORM_PKG] || Object.values(pkg.optionalDependencies || {})[0] || '').replace(/^[^\d]*/, '');
|
|
const CACHE_ROOT = process.env.IMPECCABLE_HOME || path.join(os.homedir(), '.impeccable');
|
|
const CACHED = path.join(CACHE_ROOT, 'bin', VERSION, EXE);
|
|
const BASE = (process.env.IMPECCABLE_DOWNLOAD_BASE || 'https://github.com/pbakaus/impeccable/releases/download').replace(/\/$/, '');
|
|
const URL = `${BASE}/engine-v${VERSION}/impeccable-${TARGET}${OS === 'windows' ? '.exe' : ''}`;
|
|
|
|
function exists(p) { try { return !!p && fs.statSync(p).isFile(); } catch { return false; } }
|
|
function fromPackage() {
|
|
try { return path.join(path.dirname(require.resolve(`${PLATFORM_PKG}/package.json`)), 'bin', EXE); } catch { return null; }
|
|
}
|
|
async function download() {
|
|
if (!VERSION) return null;
|
|
const res = await fetch(URL, { redirect: 'follow' });
|
|
if (!res.ok) return null;
|
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
const sum = await fetch(`${URL}.sha256`, { redirect: 'follow' }).then(r => (r.ok ? r.text() : ''), () => '');
|
|
const expected = sum.trim().split(/\s+/)[0];
|
|
if (expected && createHash('sha256').update(buf).digest('hex') !== expected) {
|
|
throw new Error(`checksum mismatch downloading ${URL}`);
|
|
}
|
|
fs.mkdirSync(path.dirname(CACHED), { recursive: true });
|
|
const tmp = `${CACHED}.part.${process.pid}`;
|
|
fs.writeFileSync(tmp, buf, { mode: 0o755 });
|
|
fs.renameSync(tmp, CACHED);
|
|
return CACHED;
|
|
}
|
|
async function locate() {
|
|
const envBin = process.env.IMPECCABLE_BIN;
|
|
if (exists(envBin)) return envBin;
|
|
const fromPkg = fromPackage();
|
|
if (exists(fromPkg)) return fromPkg;
|
|
if (exists(CACHED)) return CACHED;
|
|
return download().catch((err) => { process.stderr.write(`impeccable: ${err.message}\n`); return null; });
|
|
}
|
|
|
|
const bin = await locate();
|
|
if (!bin) {
|
|
process.stderr.write(
|
|
`impeccable: no binary for ${TARGET}. Install ${PLATFORM_PKG}@${VERSION}, set IMPECCABLE_BIN, `
|
|
+ `or download impeccable-${TARGET} v${VERSION} from ${BASE} into ${CACHED}.\n`,
|
|
);
|
|
process.exit(127);
|
|
}
|
|
const result = spawnSync(bin, process.argv.slice(2), {
|
|
stdio: 'inherit',
|
|
env: { IMPECCABLE_SELF: 'npx impeccable', ...process.env },
|
|
});
|
|
if (result.error) {
|
|
process.stderr.write(`impeccable: failed to run ${bin}: ${result.error.message}\n`);
|
|
process.exit(127);
|
|
}
|
|
process.exit(result.status === null ? 1 : result.status);
|