Files
zeekay 6487b1198c harden supply chain, fix CI provisioning, finish rebrand
Supply chain (zapc npm wrapper): npm/install.js now downloads the published
<asset>.tar.gz.sha256 and verifies the tarball with SHA-256 (createHash) BEFORE
extract/chmod/exec; fails closed on download error, malformed digest, or
mismatch. The release already emits the hashes — now they are consumed.

CI: replace the nonexistent `apt-get install -y zap libzap-dev` (5 sites across
ci.yml + coverage.yml) with a composite action (.github/actions/install-zap)
that builds and installs the real `zap` schema front-end from zap-proto/cpp-core
— the binary `zapc::CompilerCommand` execs as `zap compile`. Fix the dangling
zapc/test/rust.zap symlink (../rust.capnp -> ../rust.zap) that broke the test
crate. Unify CI trigger branch master -> main.

Whitespace: zapc consumes a binary code_generator_request from the front-end
and does not parse .zap text, so whitespace-significant syntax is inherited
transparently from cpp-core's desugar — documented in LLM.md, no redundant
parser added (one way to do everything).

Rebrand: drop the last Cap'n-Proto brand references from zap-native docs
(LLM.md, benchmark/README.md). Historical fork blog posts and license headers
keep their provenance.

Verified: `cargo build --workspace && cargo test --workspace` pass with the
front-end on PATH (200+ tests, 0 failures); deps refreshed within-major
(bytes 1.12.0, getrandom 0.4.3).
2026-06-19 23:52:23 -07:00

102 lines
3.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// Downloads the canonical zap-proto/rust zapc binary for this platform/arch from
// the matching GitHub release. Native binaries only — no build on install.
//
// SUPPLY CHAIN: the release publishes `<asset>.tar.gz.sha256` next to each
// tarball (`shasum -a 256 <asset>.tar.gz`). We download that digest first and
// verify the tarball against it with SHA-256 BEFORE extracting, chmod, or exec.
// Fail closed on any download error, malformed digest, or mismatch — a tampered
// or truncated asset never reaches the filesystem as an executable.
const https = require('https');
const fs = require('fs');
const path = require('path');
const { createHash } = require('crypto');
const { execFileSync } = require('child_process');
const VERSION = require('./package.json').version;
const REPO = 'zap-proto/rust';
const TAG = `zapc-v${VERSION}`;
const TARGETS = {
'darwin-arm64': 'aarch64-apple-darwin',
'darwin-x64': 'x86_64-apple-darwin',
'linux-x64': 'x86_64-unknown-linux-musl',
'linux-arm64': 'aarch64-unknown-linux-musl',
};
const key = `${process.platform}-${process.arch}`;
const target = TARGETS[key];
if (!target) {
console.error(`[zapc] unsupported platform ${key} (darwin/linux × x64/arm64 only)`);
process.exit(1);
}
const binDir = path.join(__dirname, 'bin');
fs.mkdirSync(binDir, { recursive: true });
const asset = `zapc-${target}.tar.gz`;
const base = `https://github.com/${REPO}/releases/download/${TAG}`;
const url = `${base}/${asset}`;
const sumUrl = `${url}.sha256`;
const tarPath = path.join(binDir, 'zapc.tar.gz');
function fail(msg) {
console.error(`[zapc] ${msg}`);
process.exit(1);
}
// Follow redirects, buffer the whole body. Used for the small .sha256 file.
function fetchText(u, cb, redirects = 0) {
if (redirects > 5) return fail(`too many redirects fetching ${u}`);
https.get(u, (res) => {
if (res.statusCode === 301 || res.statusCode === 302) {
res.resume();
return fetchText(res.headers.location, cb, redirects + 1);
}
if (res.statusCode !== 200) return fail(`download failed ${res.statusCode}: ${u}`);
const chunks = [];
res.on('data', (c) => chunks.push(c));
res.on('end', () => cb(Buffer.concat(chunks).toString('utf8')));
}).on('error', (e) => fail(e.message));
}
// Follow redirects, stream to dest. Used for the (large) tarball.
function download(u, dest, cb, redirects = 0) {
if (redirects > 5) return fail(`too many redirects fetching ${u}`);
https.get(u, (res) => {
if (res.statusCode === 301 || res.statusCode === 302) {
res.resume();
return download(res.headers.location, dest, cb, redirects + 1);
}
if (res.statusCode !== 200) return fail(`download failed ${res.statusCode}: ${u}`);
const f = fs.createWriteStream(dest);
res.pipe(f);
f.on('finish', () => f.close(cb));
}).on('error', (e) => fail(e.message));
}
// `shasum -a 256 file` → "<64-hex> file". Take the first token, validate shape.
function parseSha256(text) {
const hex = text.trim().split(/\s+/)[0]?.toLowerCase() ?? '';
if (!/^[0-9a-f]{64}$/.test(hex)) fail(`malformed .sha256 digest: ${JSON.stringify(text)}`);
return hex;
}
function sha256File(file) {
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
}
console.log(`[zapc] downloading ${target} ${TAG}`);
fetchText(sumUrl, (sumText) => {
const expected = parseSha256(sumText);
download(url, tarPath, () => {
const actual = sha256File(tarPath);
if (actual !== expected) {
fs.unlinkSync(tarPath);
fail(`checksum mismatch for ${asset}\n expected ${expected}\n actual ${actual}`);
}
execFileSync('tar', ['-xzf', tarPath, '-C', binDir]);
fs.unlinkSync(tarPath);
fs.chmodSync(path.join(binDir, 'zapc'), 0o755);
console.log('[zapc] installed bin/zapc (sha256 verified)');
});
});