From 20f99b631f02538e995cec7bfbb58167788b0eec Mon Sep 17 00:00:00 2001 From: Keren Dong Date: Wed, 8 Jul 2026 16:02:05 +0800 Subject: [PATCH] fix: keep KFD witnesses in sync Signed-off-by: Keren Dong --- .../kfd-1/libnode-contract-world.witness.json | 20 +-- .../collaboration-interface.prebuild.json | 2 +- .gyp/libnode-kfd-witnesses.js | 164 ++++++++++++++++++ buildchain.contract-lock.json | 4 +- buildchain.toml | 1 + package.json | 2 + 6 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 .gyp/libnode-kfd-witnesses.js diff --git a/.buildchain/kfd-1/libnode-contract-world.witness.json b/.buildchain/kfd-1/libnode-contract-world.witness.json index 4585921..d2fc15e 100644 --- a/.buildchain/kfd-1/libnode-contract-world.witness.json +++ b/.buildchain/kfd-1/libnode-contract-world.witness.json @@ -10,12 +10,12 @@ "contractWorld": { "id": "libnode-release-contract", "schemaId": "https://kfd.libkungfu.dev/schemas/kfd-1/contract-world.schema.json", - "digest": "sha256:e07b53986f76d8a3af0ec58b6c7abbc2b6a94261e2da0004280f0a143cf0c3a0" + "digest": "sha256:89209c832af5dffee0d73e33b0d2b4c68cd0b2693141ef6472e31e673ce5790c" }, "standardContract": { "id": "libnode-release-contract", "path": "libnode.release.json", - "sha256": "e07b53986f76d8a3af0ec58b6c7abbc2b6a94261e2da0004280f0a143cf0c3a0" + "sha256": "89209c832af5dffee0d73e33b0d2b4c68cd0b2693141ef6472e31e673ce5790c" }, "selfHostingBoundary": { "mode": "self-hosted-standard-contract", @@ -33,25 +33,25 @@ { "name": "package-manifest", "sourcePath": "package.json", - "sourceSha256": "9092f881f572b36c7bddda388a4bcd6c11b366e56405616715fb58bafe7173bf", + "sourceSha256": "a20cf95aed25d87a6cf960e74a2ec23d0411ffdd958c9321ece0ec8249f82979", "artifactPath": "package.json", - "expectedSha256": "9092f881f572b36c7bddda388a4bcd6c11b366e56405616715fb58bafe7173bf", + "expectedSha256": "a20cf95aed25d87a6cf960e74a2ec23d0411ffdd958c9321ece0ec8249f82979", "byteForByte": true }, { "name": "release-manifest", "sourcePath": "libnode.release.json", - "sourceSha256": "e07b53986f76d8a3af0ec58b6c7abbc2b6a94261e2da0004280f0a143cf0c3a0", + "sourceSha256": "89209c832af5dffee0d73e33b0d2b4c68cd0b2693141ef6472e31e673ce5790c", "artifactPath": "libnode.release.json", - "expectedSha256": "e07b53986f76d8a3af0ec58b6c7abbc2b6a94261e2da0004280f0a143cf0c3a0", + "expectedSha256": "89209c832af5dffee0d73e33b0d2b4c68cd0b2693141ef6472e31e673ce5790c", "byteForByte": true }, { "name": "buildchain-config", "sourcePath": "buildchain.toml", - "sourceSha256": "1ccbc2dee94a30dff10b7596125340a985e8150febe74e7efe0b67f11299e972", + "sourceSha256": "1ed7e2677a7b551264f86a40c6985adeebca590a942e358a57844b5d05a2aa3d", "artifactPath": "buildchain.toml", - "expectedSha256": "1ccbc2dee94a30dff10b7596125340a985e8150febe74e7efe0b67f11299e972", + "expectedSha256": "1ed7e2677a7b551264f86a40c6985adeebca590a942e358a57844b5d05a2aa3d", "byteForByte": true }, { @@ -97,9 +97,9 @@ { "name": "kfd3-prebuild-witness", "sourcePath": ".buildchain/kfd-3/collaboration-interface.prebuild.json", - "sourceSha256": "b6fe7ab725271e6a478550338b2c749fd848ca48b0536ab3ebbd10728a382812", + "sourceSha256": "a190401a0e1536b9d0f01e4168d30f7c8dae1a3bf6fed03c743a64e659d62262", "artifactPath": ".buildchain/kfd-3/collaboration-interface.prebuild.json", - "expectedSha256": "b6fe7ab725271e6a478550338b2c749fd848ca48b0536ab3ebbd10728a382812", + "expectedSha256": "a190401a0e1536b9d0f01e4168d30f7c8dae1a3bf6fed03c743a64e659d62262", "byteForByte": true } ] diff --git a/.buildchain/kfd-3/collaboration-interface.prebuild.json b/.buildchain/kfd-3/collaboration-interface.prebuild.json index b201df6..ecdb10e 100644 --- a/.buildchain/kfd-3/collaboration-interface.prebuild.json +++ b/.buildchain/kfd-3/collaboration-interface.prebuild.json @@ -11,7 +11,7 @@ "sourceRegistry": { "id": "libnode-release-contract", "path": "libnode.release.json", - "version": "22.22.3-kf.3-alpha.14" + "version": "22.22.3-kf.3-alpha.15" }, "collaborationInterfaceDigest": "sha256:da72832c4fee28021f90381b86ed94ccf6d8bddcb23b80419d256e5a9e4bb653", "collaborationInterface": { diff --git a/.gyp/libnode-kfd-witnesses.js b/.gyp/libnode-kfd-witnesses.js new file mode 100644 index 0000000..0379a30 --- /dev/null +++ b/.gyp/libnode-kfd-witnesses.js @@ -0,0 +1,164 @@ +const crypto = require('crypto'); +const childProcess = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const repoRoot = path.resolve(__dirname, '..'); +let useGitBlobContent = false; + +const paths = { + packageJson: 'package.json', + release: 'libnode.release.json', + buildchain: 'buildchain.toml', + platformPackage: '.gyp/node-platform-package.js', + releaseVerify: '.gyp/libnode-release-verify.js', + kfd3ArtifactVerify: '.gyp/libnode-kfd3-artifact-verify.js', + releaseWorkflow: '.github/workflows/release-new-version.yml', + kfd3Interface: '.buildchain/kfd-3/collaboration-interface.json', + kfd3Prebuild: '.buildchain/kfd-3/collaboration-interface.prebuild.json', + kfd1Witness: '.buildchain/kfd-1/libnode-contract-world.witness.json', +}; + +function readJson(relativePath) { + return JSON.parse(fileText(relativePath)); +} + +function gitBlob(relativePath) { + const result = childProcess.spawnSync('git', ['show', `HEAD:${relativePath}`], { + cwd: repoRoot, + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: true, + }); + if (result.status !== 0) { + const stderr = result.stderr.toString('utf8').trim(); + throw new Error(`git show HEAD:${relativePath} failed${stderr ? `: ${stderr}` : ''}`); + } + return result.stdout; +} + +function fileBytes(relativePath) { + return useGitBlobContent ? gitBlob(relativePath) : fs.readFileSync(path.join(repoRoot, relativePath)); +} + +function fileText(relativePath) { + return fileBytes(relativePath).toString('utf8'); +} + +function sha256File(relativePath) { + return crypto.createHash('sha256').update(fileBytes(relativePath)).digest('hex'); +} + +function jsonText(value) { + return `${JSON.stringify(value, null, 2)}\n`; +} + +function writeIfChanged(relativePath, text) { + const filePath = path.join(repoRoot, relativePath); + if (fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === text) return false; + fs.writeFileSync(filePath, text); + return true; +} + +function generatedKfd3Prebuild() { + const release = readJson(paths.release); + const prebuild = readJson(paths.kfd3Prebuild); + const interfaceDigest = `sha256:${sha256File(paths.kfd3Interface)}`; + + prebuild.sourceRegistry = { + ...(prebuild.sourceRegistry || {}), + version: release.npmVersion, + }; + prebuild.collaborationInterfaceDigest = interfaceDigest; + if (prebuild.collaborationInterface && typeof prebuild.collaborationInterface === 'object') { + prebuild.collaborationInterface.digest = interfaceDigest; + } + + return prebuild; +} + +function generatedKfd1Witness(kfd3PrebuildText) { + const witness = readJson(paths.kfd1Witness); + const releaseSha = sha256File(paths.release); + const kfd3PrebuildSha = crypto.createHash('sha256').update(kfd3PrebuildText).digest('hex'); + const surfaceSha = { + 'package-manifest': sha256File(paths.packageJson), + 'release-manifest': releaseSha, + 'buildchain-config': sha256File(paths.buildchain), + 'package-builder': sha256File(paths.platformPackage), + 'release-verifier': sha256File(paths.releaseVerify), + 'kfd3-artifact-verifier': sha256File(paths.kfd3ArtifactVerify), + 'release-workflow': sha256File(paths.releaseWorkflow), + 'kfd3-collaboration-interface': sha256File(paths.kfd3Interface), + 'kfd3-prebuild-witness': kfd3PrebuildSha, + }; + + witness.contractWorld = { + ...(witness.contractWorld || {}), + digest: `sha256:${releaseSha}`, + }; + witness.standardContract = { + ...(witness.standardContract || {}), + sha256: releaseSha, + }; + witness.surfaces = (witness.surfaces || []).map((surface) => { + const sha = surfaceSha[surface.name]; + if (!sha) { + throw new Error(`Unknown KFD-1 surface: ${surface.name}`); + } + return { + ...surface, + sourceSha256: sha, + expectedSha256: sha, + }; + }); + + return witness; +} + +function buildExpectedTexts() { + const kfd3PrebuildText = jsonText(generatedKfd3Prebuild()); + return { + [paths.kfd3Prebuild]: kfd3PrebuildText, + [paths.kfd1Witness]: jsonText(generatedKfd1Witness(kfd3PrebuildText)), + }; +} + +function check() { + const stale = []; + for (const [relativePath, expected] of Object.entries(buildExpectedTexts())) { + const current = fs.existsSync(path.join(repoRoot, relativePath)) + ? fs.readFileSync(path.join(repoRoot, relativePath), 'utf8') + : ''; + if (current.replace(/\r\n/g, '\n') !== expected) stale.push(relativePath); + } + + if (stale.length > 0) { + throw new Error(`KFD witness files are stale: ${stale.join(', ')}. Run corepack pnpm sync-kfd-witnesses.`); + } + console.log('libnode KFD witnesses ok'); +} + +function write() { + const changed = []; + for (const [relativePath, expected] of Object.entries(buildExpectedTexts())) { + if (writeIfChanged(relativePath, expected)) changed.push(relativePath); + } + console.log( + changed.length ? `updated KFD witnesses: ${changed.join(', ')}` : 'libnode KFD witnesses already up to date', + ); +} + +function main() { + const command = process.argv[2] || 'check'; + useGitBlobContent = command === 'check' && process.env.GITHUB_ACTIONS === 'true'; + if (command === 'check') return check(); + if (command === 'write') return write(); + throw new Error(`Unknown command: ${command}`); +} + +try { + main(); +} catch (error) { + console.error(error.message); + process.exit(1); +} diff --git a/buildchain.contract-lock.json b/buildchain.contract-lock.json index 2d4cf72..1edf473 100644 --- a/buildchain.contract-lock.json +++ b/buildchain.contract-lock.json @@ -3,9 +3,9 @@ "contract": "kungfu-buildchain-contract-lock", "buildchain": { "ref": "v2", - "resolvedSha": "6990341aaf93b51b936e5e66f380488d593daf6e", + "resolvedSha": "09694eed5ba1b2bcfd85fd7b805d42ea2fc9b72a", "contract": "kungfu-buildchain-runtime-contract-world", - "contractDigest": "sha256:3a82cbfd0cf15f85dac51b6ab03e3c3ce20ca2f3355d5319d969c43758ebb900", + "contractDigest": "sha256:6b6cd5aab74bde21e88bf2a551557280954fa9fcacc298b875b56bbd9e1e04db", "compatibilityDigest": "sha256:407d6d5ee6a96a34d2cc30db9ac64cf4b1819ab6d77bd7a57a07a124f22101ca", "majorLine": "v2", "compatibilityPolicy": "major-compatible", diff --git a/buildchain.toml b/buildchain.toml index 72324e2..62b3fe3 100644 --- a/buildchain.toml +++ b/buildchain.toml @@ -69,6 +69,7 @@ commands = [ timeout_minutes = 10 commands = [ "corepack pnpm verify-release", + "corepack pnpm verify-kfd-witnesses", "corepack pnpm verify-package-source", "git diff --check", ] diff --git a/package.json b/package.json index 95e409c..aab91da 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ "prepare-node-source": "node .gyp/node-source-prepare.js", "rebuild": "node .gyp/node-build.js rebuild", "verify-release": "node .gyp/libnode-release-verify.js", + "sync-kfd-witnesses": "node .gyp/libnode-kfd-witnesses.js write", + "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", "format": "prettier --write --parser typescript .gyp/*.js src/js/*.js"