diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5c17cff8..acc582313 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3796,6 +3796,8 @@ bootstrap routes use this grant, not an Environment ID as authentication. Public artifact routes contain no credentials. Native bundles must match the Core source revision and Runtime wire version. Core release qualification consumes the same three-platform native CI artifacts and includes them in its distribution. +`make check-distribution` exercises catalog assembly with manifests larger than +Node's default subprocess output buffer; catalog reads allow up to 64 MiB. Bootstrap scripts own platform download/extraction only; installation, startup, connection verification and Runtime execution remain common. Serialize background PID inspection and publication so concurrent starts cannot create duplicate daemons. diff --git a/Makefile b/Makefile index 34b6bf41e..c2e1197e0 100644 --- a/Makefile +++ b/Makefile @@ -133,6 +133,7 @@ check-microsandbox-provider: .PHONY: check-distribution build-core-distribution check-distribution: + node --test scripts/build-native-catalog.test.mjs go test ./services/core-console -count=1 PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s deploy/install -p 'test_*.py' PYTHONDONTWRITEBYTECODE=1 python3 scripts/core-distribution-manifest.test.py diff --git a/scripts/build-native-catalog.mjs b/scripts/build-native-catalog.mjs index c7e5b24c0..ab97b7e96 100644 --- a/scripts/build-native-catalog.mjs +++ b/scripts/build-native-catalog.mjs @@ -15,7 +15,7 @@ await mkdir(output, { recursive: true }); const artifacts = {}; for (const [ci, platform] of Object.entries({ 'Linux-X64': 'linux-amd64', 'macOS-ARM64': 'darwin-arm64', 'Windows-X64': 'windows-amd64' })) { const archive = resolve(input, `oac-native-installer-${ci}.tar.gz`); - const bundle = JSON.parse(execFileSync('tar', ['-xOzf', archive, './bundle.json'], { encoding: 'utf8' })); + const bundle = JSON.parse(execFileSync('tar', ['-xOzf', archive, './bundle.json'], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 })); if (bundle.daemon_version !== version || `${bundle.os}-${bundle.arch}` !== platform) throw new Error(`Mismatched native artifact: ${platform}`); const hash = createHash('sha256'); for await (const chunk of createReadStream(archive)) hash.update(chunk); diff --git a/scripts/build-native-catalog.test.mjs b/scripts/build-native-catalog.test.mjs new file mode 100644 index 000000000..9328b3d35 --- /dev/null +++ b/scripts/build-native-catalog.test.mjs @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import test from 'node:test'; + +test('catalog accepts large native manifests and still rejects a foreign revision', async () => { + const directory = await mkdtemp(join(tmpdir(), 'oac-native-catalog-')); + try { + const input = join(directory, 'input'); + const output = join(directory, 'output'); + const bundle = join(directory, 'bundle'); + await Promise.all([mkdir(input), mkdir(bundle)]); + const version = execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); + for (const [ci, os, arch] of [['Linux-X64', 'linux', 'amd64'], ['macOS-ARM64', 'darwin', 'arm64'], ['Windows-X64', 'windows', 'amd64']]) { + await writeFile(join(bundle, 'bundle.json'), JSON.stringify({ daemon_version: version, os, arch, files: { fixture: 'x'.repeat(2 * 1024 * 1024) } })); + execFileSync('tar', ['-czf', join(input, `oac-native-installer-${ci}.tar.gz`), '-C', bundle, './bundle.json']); + } + const script = resolve('scripts/build-native-catalog.mjs'); + execFileSync(process.execPath, [script, input, output]); + const catalog = JSON.parse(await readFile(join(output, 'catalog.json'), 'utf8')); + assert.equal(catalog.version, version); + assert.equal(Object.keys(catalog.artifacts).length, 3); + const linux = await readFile(join(input, 'oac-native-installer-Linux-X64.tar.gz')); + assert.deepEqual(await readFile(join(output, 'linux-amd64.tar.gz')), linux); + assert.equal(catalog.artifacts['linux-amd64'].sha256, createHash('sha256').update(linux).digest('hex')); + await writeFile(join(bundle, 'bundle.json'), JSON.stringify({ daemon_version: 'foreign', os: 'linux', arch: 'amd64' })); + execFileSync('tar', ['-czf', join(input, 'oac-native-installer-Linux-X64.tar.gz'), '-C', bundle, './bundle.json']); + assert.throws(() => execFileSync(process.execPath, [script, input, output], { stdio: 'pipe' }), error => error.stderr.toString().includes('Mismatched native artifact')); + } finally { + await rm(directory, { recursive: true, force: true }); + } +});