diff --git a/.buildchain/kfd-1/libnode-contract-world.witness.json b/.buildchain/kfd-1/libnode-contract-world.witness.json index 3ac12b6..cee8054 100644 --- a/.buildchain/kfd-1/libnode-contract-world.witness.json +++ b/.buildchain/kfd-1/libnode-contract-world.witness.json @@ -33,9 +33,9 @@ { "name": "package-manifest", "sourcePath": "package.json", - "sourceSha256": "de626755c62f2b3b252e0680b86e5dcddab7fdf8207ed76627e8c780d3210f56", + "sourceSha256": "58595a23baf50d1590a231a728056696b9fdf15f196d3d94dff40f55c1206820", "artifactPath": "package.json", - "expectedSha256": "de626755c62f2b3b252e0680b86e5dcddab7fdf8207ed76627e8c780d3210f56", + "expectedSha256": "58595a23baf50d1590a231a728056696b9fdf15f196d3d94dff40f55c1206820", "byteForByte": true }, { @@ -49,9 +49,9 @@ { "name": "buildchain-config", "sourcePath": "buildchain.toml", - "sourceSha256": "3e9634b497b36eb78d3ea5eb0063fc49a98a32d6d34d34047aa5fe72e4de2f58", + "sourceSha256": "3013c0ee402d8925416f3426da63cb901bf9cfcd9bbdb674cfd5ec210a3bfbc0", "artifactPath": "buildchain.toml", - "expectedSha256": "3e9634b497b36eb78d3ea5eb0063fc49a98a32d6d34d34047aa5fe72e4de2f58", + "expectedSha256": "3013c0ee402d8925416f3426da63cb901bf9cfcd9bbdb674cfd5ec210a3bfbc0", "byteForByte": true }, { diff --git a/.gyp/libnode-entrypoint.test.js b/.gyp/libnode-entrypoint.test.js index 25fd732..b232cea 100644 --- a/.gyp/libnode-entrypoint.test.js +++ b/.gyp/libnode-entrypoint.test.js @@ -29,13 +29,20 @@ function loadEntrypoint(platform, arch) { return { exports, loadedPackages }; } -test('Linux ARM64 resolves the published platform package', () => { - const loaded = loadEntrypoint('linux', 'arm64'); +for (const [platform, arch, identity, packageName] of [ + ['darwin', 'arm64', 'darwin-arm64', '@kungfu-tech/libnode-darwin-arm64'], + ['linux', 'arm64', 'linux-arm64', '@kungfu-tech/libnode-linux-arm64'], + ['linux', 'x64', 'linux-x64', '@kungfu-tech/libnode-linux-x64'], + ['win32', 'x64', 'win32-x64', '@kungfu-tech/libnode-win32-x64'], +]) { + test(`${identity} resolves the published platform package`, () => { + const loaded = loadEntrypoint(platform, arch); - assert.deepEqual(loaded.loadedPackages, ['@kungfu-tech/libnode-linux-arm64']); - assert.equal(loaded.exports.platform, 'linux-arm64'); - assert.equal(loaded.exports.platformPackageName, '@kungfu-tech/libnode-linux-arm64'); -}); + assert.deepEqual(loaded.loadedPackages, [packageName]); + assert.equal(loaded.exports.platform, identity); + assert.equal(loaded.exports.platformPackageName, packageName); + }); +} test('macOS x64 remains explicitly unsupported', () => { assert.throws(() => loadEntrypoint('darwin', 'x64'), /Unsupported libnode platform: darwin-x64/); diff --git a/.gyp/libnode-platform-policy.js b/.gyp/libnode-platform-policy.js new file mode 100644 index 0000000..7243075 --- /dev/null +++ b/.gyp/libnode-platform-policy.js @@ -0,0 +1,62 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const repoRoot = path.resolve(__dirname, '..'); +const activeSurfaces = [ + '.buildchain/kfd-2/public-release-trust.claim.json', + '.buildchain/kfd-3/collaboration-interface.json', + '.buildchain/kfd-3/collaboration-interface.prebuild.json', + '.github/workflows/build.yml', + '.github/workflows/release-new-version.yml', + '.github/workflows/release-verify.yaml', + '.gyp/libnode-kfd3-artifact-verify.js', + '.gyp/node-platform-package.js', + '.gyp/npm-publish-tarballs.js', + 'buildchain.toml', + 'libnode.release.json', + 'package.json', + 'src/js/index.js', +]; +const retiredIdentity = /darwin[-/](?:x64|x86_64)|macos[-/](?:x64|x86_64)|macos-15-intel|libnode-darwin-x64/giu; + +function inspectEntries(entries) { + const violations = []; + for (const entry of entries) { + const lines = entry.content.split(/\r?\n/u); + for (let index = 0; index < lines.length; index += 1) { + const matches = [...lines[index].matchAll(retiredIdentity)].map((match) => match[0]); + if (matches.length > 0) { + violations.push({ + path: entry.path, + line: index + 1, + matches: [...new Set(matches)].sort(), + }); + } + } + } + return { + schema: 'libnode.platform-retirement-policy/v1', + status: violations.length === 0 ? 'pass' : 'fail', + activeSurfaces: entries.map(({ path: entryPath }) => entryPath).sort(), + violations, + }; +} + +function inspectRepository(root = repoRoot) { + return inspectEntries( + activeSurfaces.map((relative) => ({ + path: relative, + content: fs.readFileSync(path.join(root, relative), 'utf8'), + })), + ); +} + +function main() { + const report = inspectRepository(); + process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); + if (report.status !== 'pass') process.exitCode = 1; +} + +if (require.main === module) main(); + +module.exports = { activeSurfaces, inspectEntries, inspectRepository }; diff --git a/.gyp/libnode-platform-policy.test.js b/.gyp/libnode-platform-policy.test.js new file mode 100644 index 0000000..edbe246 --- /dev/null +++ b/.gyp/libnode-platform-policy.test.js @@ -0,0 +1,31 @@ +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const test = require('node:test'); + +const { inspectEntries, inspectRepository } = require('./libnode-platform-policy'); + +const repoRoot = path.resolve(__dirname, '..'); + +test('all active package and release surfaces exclude retired Intel macOS identities', () => { + const report = inspectRepository(); + assert.equal(report.status, 'pass', JSON.stringify(report.violations)); + assert.deepEqual(report.violations, []); +}); + +test('an active Intel macOS package identity fails closed', () => { + const report = inspectEntries([ + { + path: 'package.json', + content: JSON.stringify({ optionalDependencies: { '@kungfu-tech/libnode-darwin-x64': '*' } }), + }, + ]); + assert.equal(report.status, 'fail'); + assert.deepEqual(report.violations, [{ path: 'package.json', line: 1, matches: ['libnode-darwin-x64'] }]); +}); + +test('public documentation and the main entrypoint keep an explicit negative contract', () => { + const read = (relative) => fs.readFileSync(path.join(repoRoot, relative), 'utf8'); + assert.match(read('README.md'), /macOS x86_64 \(`darwin-x64`\) is explicitly unsupported/u); + assert.match(read('.gyp/libnode-entrypoint.test.js'), /Unsupported libnode platform: darwin-x64/u); +}); diff --git a/buildchain.toml b/buildchain.toml index f84212c..2dc83aa 100644 --- a/buildchain.toml +++ b/buildchain.toml @@ -77,6 +77,7 @@ command = "corepack pnpm sync-kfd-witnesses" timeout_minutes = 10 commands = [ "node --test .gyp/libnode-entrypoint.test.js", + "corepack pnpm verify-platform-policy", "node --test .gyp/libnode-kfd-release-evidence.test.js", "corepack pnpm verify-release", "corepack pnpm verify-kfd-witnesses", diff --git a/package.json b/package.json index e09e52b..2aa96a6 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "verify-kfd-witnesses": "node .gyp/libnode-kfd-witnesses.js check", "verify-kfd3-artifact": "node .gyp/libnode-kfd3-artifact-verify.js", "verify-package-source": "node .gyp/node-platform-package.js verify-source", + "verify-platform-policy": "node .gyp/libnode-platform-policy.js && node --test .gyp/libnode-platform-policy.test.js", "format": "prettier --write --parser typescript .gyp/*.js src/js/*.js" }, "packageManager": "pnpm@11.9.0",