mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-09 16:35:27 +02:00
ci(runner): build fresh Pi12 Intel daemon from frozen source
Pin the final source and profile, restore fresh Rust compilation with SDK provenance, and keep manual immutable-source checks independent. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
2b3924f91d
commit
9541eb1878
5 files changed
+163
-175
No files matched your search
@@ -68,8 +68,9 @@ on:
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
# Publishing a newer image must not discard an earlier verification's evidence.
|
||||
group: docker-runner-check-${{ github.event.pull_request.number || github.ref }}-${{ inputs.diagnose_pi_startup && 'pi-startup-diagnostic' || inputs.verify_pi_intel && 'pi-native-intel' || inputs.diagnose_ajv_pack && 'ajv-diagnostic' || inputs.verify_runner && 'runner-verification' || inputs.source_e2e_support_only && 'source-e2e-support' || inputs.source_root_tests_only && 'source-root-tests' || inputs.verify_source && 'verification' || inputs.verify_public_install && 'public-install' || inputs.build_eval_viewer && 'viewer' || 'image' }}
|
||||
# Different immutable manual sources retain independent evidence; the same
|
||||
# source/mode still supersedes itself. Ordinary PR grouping is unchanged.
|
||||
group: docker-runner-check-${{ github.event.pull_request.number || github.ref }}-${{ inputs.diagnose_pi_startup && 'pi-startup-diagnostic' || inputs.verify_pi_intel && 'pi-native-intel' || inputs.diagnose_ajv_pack && 'ajv-diagnostic' || inputs.verify_runner && 'runner-verification' || inputs.source_e2e_support_only && 'source-e2e-support' || inputs.source_root_tests_only && 'source-root-tests' || inputs.verify_source && 'verification' || inputs.verify_public_install && 'public-install' || inputs.build_eval_viewer && 'viewer' || 'image' }}${{ github.event_name == 'workflow_dispatch' && inputs.expected_source_sha && format('-{0}', inputs.expected_source_sha) || '' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
@@ -176,7 +177,7 @@ jobs:
|
||||
test "$SOURCE_ROOT_TESTS_ONLY" != true
|
||||
test "$DIAGNOSE_AJV" != true
|
||||
test "$IMAGE_MODE" != true
|
||||
test "$EXPECTED_SOURCE_SHA" = efe019a79f50440d7bd6c3bc6c75fb8f18953093
|
||||
test "$EXPECTED_SOURCE_SHA" = b9e5d6ecdb05ab7244c90976e07c950f8d09b15b
|
||||
test "$EXPECTED_LOCK_SHA256" = 38338a6867358440c5ab5993eaeb85fb501bc7df65d24acf52fbd63c880ee4ba
|
||||
# Standard hosted runner minutes are free for this public repository.
|
||||
# Artifact storage and unrelated resource costs are not inferred here.
|
||||
@@ -230,24 +231,13 @@ jobs:
|
||||
with:
|
||||
version: 9.15.4
|
||||
package_json_file: candidate/package.json
|
||||
- name: Download exact historical native daemon evidence
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh api repos/paperclipai/paperclip/actions/artifacts/11197947432/zip \
|
||||
> "$RUNNER_TEMP/pi-native-daemon-36933598154.zip"
|
||||
printf '%s %s\n' 2f9892bfa65318e3a61fd39f09fbe26374098eff6cfc3126fa1cba8df31b85c1 \
|
||||
"$RUNNER_TEMP/pi-native-daemon-36933598154.zip" | shasum -a 256 --check
|
||||
- name: Build frozen pack and run unchanged no-provider contracts once
|
||||
timeout-minutes: 55
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 -B trusted-ci/scripts/ci/pi-intel/verify.py \
|
||||
--source "$GITHUB_WORKSPACE/candidate" \
|
||||
--output "$GITHUB_WORKSPACE/pi-intel-evidence" \
|
||||
--daemon-archive "$RUNNER_TEMP/pi-native-daemon-36933598154.zip"
|
||||
--output "$GITHUB_WORKSPACE/pi-intel-evidence"
|
||||
- name: Admit complete evidence within the reserved storage bound
|
||||
if: always()
|
||||
id: pi_intel_evidence
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
"digest": "sha256:48cecd8dc77a5533240fcf2f29d19be05380da4a79f8e5061480f94241db75a8"
|
||||
},
|
||||
"pi": {
|
||||
"path": "packages/paperclip-runner/test-fixtures/pi-acp/profile-v11-identity.json",
|
||||
"sha256": "7b253e94b1672dff0e7a4f4a50f3bb2270038d14f1ef27857f8bef67b9800a7f",
|
||||
"version": 11,
|
||||
"digest": "sha256:5e276f48c8a87b3e6165369faac62d3925282c84b98934575b1b7b97ad50b309"
|
||||
"path": "packages/paperclip-runner/test-fixtures/pi-acp/profile-v12-identity.json",
|
||||
"sha256": "99a6dd48b3da1c555cfbec7f42fe0cb1c00afe437b65a78db92f6b14d7828168",
|
||||
"version": 12,
|
||||
"digest": "sha256:47306e6d2a9b59e8f9189f725ebb7a0a7f91826044d1739e1a35ab31f228ba1f"
|
||||
}
|
||||
},
|
||||
"candidateClosureDigests": {
|
||||
@@ -31,26 +31,30 @@
|
||||
"linux-x64": "sha256:a3d8f4367cfa1694d79e3f8b7930b6fbfe42a229c272b375b11951d631db8d07"
|
||||
},
|
||||
"pi": {
|
||||
"darwin-arm64": "sha256:a3e43c23f9603983a59fd256ca1dcf8bbd5cc2728dad095f7bcd3b3e62278597",
|
||||
"darwin-x64": "sha256:8c6e7802d47da9410af56e367f6cf7af572fcbf3e361627cee026e8cffc9bdd3",
|
||||
"linux-x64": "sha256:40e186c53db60d1ab5673ba34f33002e93cea518725a93395d241393b984caef"
|
||||
"darwin-arm64": "sha256:e17be4d27c589b8f8b5de7d00686c39873fe6f902a3f133bdaf1a9f94dee00a2",
|
||||
"darwin-x64": "sha256:03351f4a250a8db0e79411a9079b43a0ff05f72a2aff41fae17f1fc2de24bd41",
|
||||
"linux-x64": "sha256:29dfc829700c57392f1d56373078dcfc80674cbd50f65674ec64fced81dffb68"
|
||||
}
|
||||
},
|
||||
"additionalSourceFiles": {
|
||||
"packages/paperclip-runner/test/fixtures/copilot-message-identity-distributions-1.0.88.json": "7e1534d668a3db7acae15377b67d86fa80cc21ce699d021709b0b52ad314cfb2",
|
||||
"patches/pi-acp@0.0.33.patch": "429184a462f9acef5bef890f3537db3ef385ee7c06d9fe0c335158590005ab0a",
|
||||
"patches/pi-acp@0.0.33.patch": "93254de82ae779587611748cfb963475c3439e033f1582f06dfdcc7f7b937546",
|
||||
"packages/paperclip-runner/scripts/pi-distribution/package.json": "c14f4050ef27dca91d3650e7446bc9927296a926224dba50a0e02f5d9fd4d95b",
|
||||
"packages/paperclip-runner/scripts/pi-distribution/package-lock.json": "f4b04d51c4d91a6529dc97af2f3f84c28a220d93a54b5555826c4d9fd30f0939",
|
||||
"packages/paperclip-runner/scripts/materialize-pi-distribution.mjs": "39bd8cc48791f8e54a953c7803334b965b9998943b0402008438d565ea2230be",
|
||||
"packages/paperclip-runner/scripts/materialize-pi-distribution.mjs": "315e1123abdad82006caca1823e15ba2da0cb1cfe7f0f29534c3830e8bf0682b",
|
||||
"packages/paperclip-runner/scripts/build-node-startup-timeout.mjs": "545dbaf7062b1cd54176dafe3feb8877e5ccf19406f791172a3ad47dbfd73fc4",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-acp-runtime.ts": "a0ec66a03cd9fa32e9aaafc4ac6840115883333000fd079ccba230706b763ac0",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-acp-runtime.ts": "95f74eb44a2e498fbd9461d363fac5b4d5fdae05b9666dca1ac4a632229dfef4",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-runtime-extension.ts": "f3446be10d984c368c52202e768aefe9b4a25968e09922e98e3f016c9dc0198e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-verified-runtime.ts": "dfb5f6604da0abc307c4bb514e3dd47354f4b867be28fdeb64273c3c055b68c1",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-node-pins.ts": "ffc822aff64db315e461cbe44f03d704695de4a4296f643af79386c3556a1d46",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-closure-pins.ts": "0941f56ea13b8eb309a345d2a1513e0ec53d201c81c755b356565505bc1df905",
|
||||
"packages/paperclip-runner/src/drivers/acpx/qualified-profiles.ts": "20185223c77ce6d5b67b270abdc0debc26166c7d3dd412a1ecea83f980429027",
|
||||
"packages/paperclip-runner/package.json": "a87426b0184e114e46c6f4ddbf3f4a9727cd591cf0a5ca900efde42198190fd1",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-installation.ts": "d32de12609133c16bb78c559b98ac878fa792a1f336159d09f2c1948f887125e"
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-closure-pins.ts": "9fdfcd7d7b0bdcbb75fe7d8f7e9228642f32d83bead9813d494b3ee133c2fc5e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/qualified-profiles.ts": "9760d924815554cb0f438c444e1f23a3608fca4a245344b25d15199b743515e5",
|
||||
"packages/paperclip-runner/package.json": "b5be67eb3190ddaa9baf20a455fae62fc7722b764f89f2cdbdd02a6d6a9dcad3",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-installation.ts": "a9c4e9c3f003eebd38b35f89a112291ad0620aed57894c878ff6806593911fd7",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "943ba0fb2d27d08b9b0d36778f08c7c0dbdb78893c7564babd4e67f67cf065fe",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "3c90ed419b96a3484756954f3c2a8af8c4c8fc4ec5aa2a7725b9df36167dcd72",
|
||||
"packages/paperclip-runner/rust-toolchain.toml": "e4c6d27e48d71df0ae66d84f3bc2d5df8cbf5c3cbb3225475a7852d94361a0c5",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/bin/paperclip-runnerd.rs": "116d2641e844ba28cd7496d56b44fa405ea8def8940d9e50e9d6b30603602581"
|
||||
},
|
||||
"permissionPolicySources": {
|
||||
"packages/paperclip-runner/src/drivers/acpx/copilot-permission-context.ts": {
|
||||
@@ -174,8 +178,8 @@
|
||||
}
|
||||
},
|
||||
"localComparison": {
|
||||
"sourceRevision": "efe019a79f50440d7bd6c3bc6c75fb8f18953093",
|
||||
"sourceArchiveSha256": "587c0f60a30f0ca4b43361830cb611fcaa4a83f492cb7a9fd66b7908b45f72be",
|
||||
"sourceRevision": "b9e5d6ecdb05ab7244c90976e07c950f8d09b15b",
|
||||
"sourceArchiveSha256": "b32d8e32639e6527804fe11bfa1aaefcefa87fca31e9155499e6b9a28f69dbd4",
|
||||
"trackedLockSha256": "e11d69fa8702a906c293c1cb307c71df90d3b1f1617b3c23ebf17fa9a87fec79",
|
||||
"resolvedLockSha256": "38338a6867358440c5ab5993eaeb85fb501bc7df65d24acf52fbd63c880ee4ba",
|
||||
"nodeSha256": "7abcf39bd37ab251015337ff75304d7555f0d8e88c6e0fbf04bce8ce34636f49",
|
||||
@@ -183,7 +187,7 @@
|
||||
"piRuntimeVersion": "1.0.0",
|
||||
"piServerVersion": "0.0.33",
|
||||
"acpxVersion": "0.13.1",
|
||||
"note": "Fresh W TypeScript/sidecar/provider pack; exact retained native Intel daemon from5eba after complete declared-input equality. Historical local pack and S/V outputs are not current outputs. Original Apple SDK/linker details were not retained; no equivalence or fresh-compilation claim.",
|
||||
"note": "Fresh exact-source TypeScript, sidecar, native Intel Rust daemon and Pi12 provider pack. Historical local outputs are not current outputs; no old daemon import or native input-equivalence claim.",
|
||||
"historicalLocalOutputs": {
|
||||
"localPackDigest": "sha256:a7ae16b680c6fcaa9438faa46562ba31519bb0fb42e21b0a4b00e8936c462d89",
|
||||
"localDaemonSha256": "c0e9acdd461d5efe4a10f9fd751e8a67beceede4d43593951d7079edad657044",
|
||||
@@ -191,138 +195,33 @@
|
||||
"sourceRevision": "5eba61ece929dcfb2b0c1761825a2d9e557c2554"
|
||||
}
|
||||
},
|
||||
"nativeDaemonReuse": {
|
||||
"schema": "paperclip.native-daemon-reuse/v1",
|
||||
"originalBuildSource": "5eba61ece929dcfb2b0c1761825a2d9e557c2554",
|
||||
"nativeInputEquivalentTo": "efe019a79f50440d7bd6c3bc6c75fb8f18953093",
|
||||
"artifactRunId": "36933598154",
|
||||
"artifactId": "11197947432",
|
||||
"artifactName": "pi-native-intel-36933598154",
|
||||
"artifactWorkflowRevision": "b58907578ed590f11b049f794f01ce0368df2676",
|
||||
"artifactZipSha256": "2f9892bfa65318e3a61fd39f09fbe26374098eff6cfc3126fa1cba8df31b85c1",
|
||||
"artifactZipBytes": 1076265811,
|
||||
"sourceArchiveSha256": "f751f86b19701757d30501e39d47fe7d20a102e32f9e4c4df585074393e85237",
|
||||
"selectedFiles": {
|
||||
"source.tar": {
|
||||
"sha256": "f751f86b19701757d30501e39d47fe7d20a102e32f9e4c4df585074393e85237",
|
||||
"bytes": 141516800
|
||||
},
|
||||
"source-input-inventory.json": {
|
||||
"sha256": "f95181f36e02e84b309f3c7b21831bc9e6480d4431c0b57a31aa5764ec966695",
|
||||
"bytes": 1032076
|
||||
},
|
||||
"receipt.json": {
|
||||
"sha256": "900fdd6a89a190bd93501f650749c72c7417ea392febd0d0cb4645eae8381736",
|
||||
"bytes": 26160
|
||||
},
|
||||
"paperclip-runnerd": {
|
||||
"sha256": "a0c7ace7ad0be9ac5b5f9c58749d9c452cd3a1ef143a38d5120d8e4f2bf82d6a",
|
||||
"bytes": 32035568
|
||||
},
|
||||
"daemon-build.log": {
|
||||
"sha256": "eea04dc2986eb3f451a0b2eeb76942eab6affb8643a6cc90eace5274a1cd5bb2",
|
||||
"bytes": 18358
|
||||
},
|
||||
"daemon-metadata.log": {
|
||||
"sha256": "a1b957a6ee5fad916252ba629b3c2ad85af8b54c1f006d5901f1614eb700ebfe",
|
||||
"bytes": 452
|
||||
},
|
||||
"daemon-sign.log": {
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"bytes": 0
|
||||
},
|
||||
"rust-version-verbose.log": {
|
||||
"sha256": "87c09aab5ad89ed5a2636cd4f577eb178a3afba7a9f71490b240845245725f04",
|
||||
"bytes": 191
|
||||
}
|
||||
},
|
||||
"sourceDeltaAllowed": [
|
||||
"doc/evals.md",
|
||||
"packages/paperclip-runner/scripts/materialize-pi-distribution.test.mjs",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.test.ts",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-installation.ts",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-native-admission.test.ts",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-verified-runtime.test.ts",
|
||||
"packages/paperclip-runner/src/drivers/acpx/pi-verified-runtime.ts",
|
||||
"tests/runner-e2e/FIXTURES.md",
|
||||
"tests/runner-e2e/README.md",
|
||||
"tests/runner-e2e/catalog.test.ts",
|
||||
"tests/runner-e2e/catalog.ts",
|
||||
"tests/runner-e2e/failure-classifier.ts",
|
||||
"tests/runner-e2e/pi-controls-evidence.test.ts",
|
||||
"tests/runner-e2e/pi-file-evidence.test.ts",
|
||||
"tests/runner-e2e/pi-file-evidence.ts",
|
||||
"tests/runner-e2e/pi-native-cases.test.ts",
|
||||
"tests/runner-e2e/pi-native-cases.ts",
|
||||
"tests/runner-e2e/pi-native-flow.ts",
|
||||
"tests/runner-e2e/pi-native-provider-death-flow.test.ts",
|
||||
"tests/runner-e2e/pi-native-provider-death-flow.ts",
|
||||
"tests/runner-e2e/pi-native-restart-flow.test.ts",
|
||||
"tests/runner-e2e/pi-native-restart-flow.ts",
|
||||
"tests/runner-e2e/pi-provider-fault.py",
|
||||
"tests/runner-e2e/pi-provider-fault.test.py",
|
||||
"tests/runner-e2e/pi-provider-fault.test.ts",
|
||||
"tests/runner-e2e/remote-native-fixtures.test.ts",
|
||||
"tests/runner-e2e/remote-native-fixtures.ts",
|
||||
"tests/runner-e2e/runner.spec.ts",
|
||||
"tests/runner-e2e/server-stop.test.ts"
|
||||
"expectedDaemonBuildMetadata": {
|
||||
"binaryContractVersion": 2,
|
||||
"binaryName": "paperclip-runnerd",
|
||||
"durableSessionCapabilities": [
|
||||
"unlimited_runtime",
|
||||
"connection_lease_renewal"
|
||||
],
|
||||
"compiler": {
|
||||
"rustcSha256": "a1fd557be213adbcfe224e0e05e2903aa3f4b8d9294bed51d97a9d2a0b201e55",
|
||||
"cargoSha256": "07816976dbe29da10bf127b75edcb0f5cd8b8652051b9c79709f8c0898d125d2",
|
||||
"versionVerbose": "rustc 1.97.1 (8bab26f4f 2026-07-14)\nbinary: rustc\ncommit-hash: 8bab26f4f68e0e26f0bb7960be334d5b520ea452\ncommit-date: 2026-07-14\nhost: x86_64-apple-darwin\nrelease: 1.97.1\nLLVM version: 22.1.6\n",
|
||||
"jobs": 2
|
||||
"harnessDriverVersion": 1,
|
||||
"nativeExecutionVersion": 1,
|
||||
"packageName": "@paperclipai/paperclip-runner",
|
||||
"packageVersion": "0.0.0",
|
||||
"prp": {
|
||||
"maximumVersion": 2,
|
||||
"minimumVersion": 1,
|
||||
"name": "paperclip.runner"
|
||||
},
|
||||
"daemonBuildMetadata": {
|
||||
"binaryContractVersion": 2,
|
||||
"binaryName": "paperclip-runnerd",
|
||||
"durableSessionCapabilities": [
|
||||
"unlimited_runtime",
|
||||
"connection_lease_renewal"
|
||||
],
|
||||
"harnessDriverVersion": 1,
|
||||
"nativeExecutionVersion": 1,
|
||||
"packageName": "@paperclipai/paperclip-runner",
|
||||
"packageVersion": "0.0.0",
|
||||
"prp": {
|
||||
"maximumVersion": 2,
|
||||
"minimumVersion": 1,
|
||||
"name": "paperclip.runner"
|
||||
},
|
||||
"prpTransportModes": [
|
||||
"dial_ws_loopback",
|
||||
"dial_wss",
|
||||
"listen_ws"
|
||||
],
|
||||
"schema": "paperclip-runner/runnerd-build-metadata/v1"
|
||||
},
|
||||
"buildCommandTail": [
|
||||
"build",
|
||||
"--release",
|
||||
"--target",
|
||||
"x86_64-apple-darwin",
|
||||
"--manifest-path",
|
||||
"packages/paperclip-runner/runner/Cargo.toml",
|
||||
"--locked",
|
||||
"-p",
|
||||
"paperclip-runner-core",
|
||||
"--bin",
|
||||
"paperclip-runnerd",
|
||||
"-j",
|
||||
"2"
|
||||
"prpTransportModes": [
|
||||
"dial_ws_loopback",
|
||||
"dial_wss",
|
||||
"listen_ws"
|
||||
],
|
||||
"originalVerifierSha256": "7de772df5beabe6413e2d0d8dd287017ef2be8d3b3cdc76e8a23007ba547b509",
|
||||
"missingOriginalToolchainEvidence": [
|
||||
"Apple SDK version",
|
||||
"Apple linker/compiler identity",
|
||||
"full Rust compiler sysroot inventory"
|
||||
],
|
||||
"claims": {
|
||||
"freshNativeCompilation": false,
|
||||
"currentAppleToolchainEquivalent": false,
|
||||
"completeDeclaredNativeSourceConfigProtocolEquality": true,
|
||||
"originalCompilerProvenanceRetained": true
|
||||
}
|
||||
"schema": "paperclip-runner/runnerd-build-metadata/v1"
|
||||
},
|
||||
"freshNativeBuild": {
|
||||
"toolchain": "1.97.1",
|
||||
"target": "x86_64-apple-darwin",
|
||||
"jobs": 2,
|
||||
"deadlineSeconds": 1500
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,75 @@ import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import unittest
|
||||
import json
|
||||
import re
|
||||
from unittest.mock import Mock, patch
|
||||
from lifecycle import CleanupUncertain, Lifecycle, run_test_cases
|
||||
from retain_pack import retain_pack, verify_archive
|
||||
from closed_inventory import closed_tree
|
||||
from source_guard import tree, sha, capture_authority, verify_authority, verify_source, ASSETS
|
||||
from admit_evidence import admit_evidence, regular_bytes
|
||||
from verify import tool_invocation, verify_tool_invocation
|
||||
from verify import tool_invocation, verify_tool_invocation, build_native_daemon, INPUTS, SOURCE
|
||||
|
||||
class FreshNativeBuildRegression(unittest.TestCase):
|
||||
"""Mock command boundaries only: no compiler, platform tool or child launch."""
|
||||
def setUp(self):
|
||||
self.temp=tempfile.TemporaryDirectory();self.addCleanup(self.temp.cleanup)
|
||||
self.root=Path(self.temp.name).resolve();self.stage=self.root/'stage';self.stage.mkdir()
|
||||
self.scratch=self.root/'scratch';self.scratch.mkdir();self.sdk=self.root/'sdk';self.sdk.mkdir()
|
||||
(self.sdk/'SDKSettings.json').write_text('{"Version":"fixture"}')
|
||||
self.tools={}
|
||||
for name in ('rustup','cargo','rustc','clang','ld'):
|
||||
path=self.root/name;path.write_text('fixture-only '+name);path.chmod(0o700);self.tools[name]=path
|
||||
self.audit={'invocationPath':str(self.tools['rustup']),'resolvedTarget':str(self.tools['rustup']),'targetSha256':sha(self.tools['rustup'])}
|
||||
self.env={'CARGO_TARGET_DIR':str(self.scratch/'target')};self.receipt={};self.calls=[];self.fail_label=None;self.bad_metadata=False
|
||||
def run_command(self,command,label,timeout):
|
||||
self.calls.append((label,list(map(str,command)),timeout))
|
||||
if label==self.fail_label:raise RuntimeError('injected command failure')
|
||||
values={'cargo-path':str(self.tools['cargo']),'rustc-path':str(self.tools['rustc']),
|
||||
'rust-version-verbose':'rustc 1.97.1 fixture\nhost: x86_64-apple-darwin', 'cargo-version':'cargo 1.97.1 fixture',
|
||||
'sdk-path':str(self.sdk),'sdk-version':'fixture','clang-path':str(self.tools['clang']),'ld-path':str(self.tools['ld']),
|
||||
'daemon-architecture':'Mach-O 64-bit executable x86_64',
|
||||
'daemon-metadata':json.dumps({} if self.bad_metadata else INPUTS['expectedDaemonBuildMetadata'])}
|
||||
if label=='daemon-build':
|
||||
p=self.scratch/'target/x86_64-apple-darwin/release/paperclip-runnerd';p.parent.mkdir(parents=True);p.write_bytes(b'newly-compiled-fixture');p.chmod(0o755)
|
||||
return values.get(label,'')
|
||||
def build(self):
|
||||
with patch('verify.tool_invocation',return_value=(self.tools['rustup'],self.audit)):
|
||||
return build_native_daemon(self.stage,self.scratch,self.env,self.run_command,self.receipt,lambda:None)
|
||||
def test_fresh_compiler_recipe_and_output_provenance(self):
|
||||
daemon=self.build();self.assertEqual(daemon.read_bytes(),b'newly-compiled-fixture')
|
||||
self.assertTrue(self.receipt['nativeRecompiled']);self.assertFalse(self.receipt['nativeDaemonReuse']);self.assertEqual(self.receipt['nativeBuildSource'],SOURCE)
|
||||
build=next(c for c in self.calls if c[0]=='daemon-build')
|
||||
self.assertEqual(build[2],1500);self.assertEqual(build[1][1:],['build','--release','--target','x86_64-apple-darwin','--manifest-path','packages/paperclip-runner/runner/Cargo.toml','--locked','-p','paperclip-runner-core','--bin','paperclip-runnerd','-j','2'])
|
||||
self.assertEqual(self.env['RUSTC'],str(self.tools['rustc']))
|
||||
self.assertEqual(self.receipt['compiler']['sdk']['settings']['SDKSettings.json'],sha(self.sdk/'SDKSettings.json'))
|
||||
labels=[c[0] for c in self.calls];self.assertLess(labels.index('daemon-sign'),labels.index('daemon-signature-verify'));self.assertLess(labels.index('daemon-signature-verify'),labels.index('daemon-metadata'))
|
||||
def test_compiler_failure_cannot_produce_or_claim_fresh_daemon(self):
|
||||
self.fail_label='daemon-build'
|
||||
with self.assertRaisesRegex(RuntimeError,'injected'):self.build()
|
||||
self.assertNotIn('nativeRecompiled',self.receipt);self.assertFalse((self.stage/'packages/paperclip-runner/dist/bin/paperclip-runnerd').exists())
|
||||
self.assertNotIn('daemon-sign',[c[0] for c in self.calls])
|
||||
def test_wrong_metadata_cannot_claim_fresh_success(self):
|
||||
self.bad_metadata=True
|
||||
with self.assertRaisesRegex(RuntimeError,'build metadata'):self.build()
|
||||
self.assertNotIn('nativeRecompiled',self.receipt)
|
||||
def test_missing_sdk_evidence_stops_before_build(self):
|
||||
(self.sdk/'SDKSettings.json').unlink()
|
||||
with self.assertRaisesRegex(RuntimeError,'SDK settings'):self.build()
|
||||
self.assertNotIn('daemon-build',[c[0] for c in self.calls])
|
||||
def test_unreviewed_recipe_stops_before_commands(self):
|
||||
with patch.dict(INPUTS,{'freshNativeBuild':dict(INPUTS['freshNativeBuild'],jobs=8)}):
|
||||
with self.assertRaisesRegex(RuntimeError,'recipe'):self.build()
|
||||
self.assertEqual(self.calls,[])
|
||||
|
||||
def test_pack_verifier_and_source_profile_authorities_agree(self):
|
||||
verifier=(Path(__file__).parent/'verify-pack.mjs').read_text()
|
||||
block=re.search(r'const pins = \{([^}]+)\};',verifier)
|
||||
self.assertIsNotNone(block)
|
||||
pins=dict(re.findall(r"(\w+): '([a-f0-9]{64})'",block.group(1)))
|
||||
self.assertEqual({agent:'sha256:'+digest for agent,digest in pins.items()},
|
||||
{agent:profile['digest'] for agent,profile in INPUTS['profiles'].items()})
|
||||
|
||||
class CleanupRegression(unittest.TestCase):
|
||||
def test_first_cleanup_failure_prevents_second_test_and_scratch_removal(self):
|
||||
|
||||
@@ -47,7 +47,7 @@ export function verifyPack(pack, source, platform, architecture) {
|
||||
requireTrue(manifest.digest === `sha256:${createHash('sha256').update(canonicalJson(p)).digest('hex')}`, 'Canonical manifest mismatch');
|
||||
requireTrue(p.runnerSourceRevision === source, 'Wrong source');
|
||||
requireTrue(p.target.platform === platform && p.target.architecture === architecture, 'Wrong target');
|
||||
const pins = { cursor: '1df2a15b93bc3a14fa47fa3315344ba023fe2412048047cdc6f32096a6336564', copilot: '48cecd8dc77a5533240fcf2f29d19be05380da4a79f8e5061480f94241db75a8', pi: '5e276f48c8a87b3e6165369faac62d3925282c84b98934575b1b7b97ad50b309' };
|
||||
const pins = { cursor: '1df2a15b93bc3a14fa47fa3315344ba023fe2412048047cdc6f32096a6336564', copilot: '48cecd8dc77a5533240fcf2f29d19be05380da4a79f8e5061480f94241db75a8', pi: '47306e6d2a9b59e8f9189f725ebb7a0a7f91826044d1739e1a35ab31f228ba1f' };
|
||||
requireTrue(Object.keys(p.candidateProviders).sort().join(',') === Object.keys(pins).sort().join(','), 'Wrong candidate set');
|
||||
const closurePins = JSON.parse(readFileSync(new URL('./profile-inputs.json', import.meta.url))).candidateClosureDigests;
|
||||
const checks = [];
|
||||
|
||||
@@ -7,7 +7,6 @@ from owned_processes import OwnedProcesses,atomic_json
|
||||
import source_guard
|
||||
from lifecycle import Lifecycle, run_test_cases
|
||||
from retain_pack import retain_pack
|
||||
from native_daemon_reuse import import_daemon
|
||||
|
||||
HERE=Path(__file__).resolve().parent
|
||||
INPUTS=json.loads((HERE/'profile-inputs.json').read_text())
|
||||
@@ -29,6 +28,51 @@ def verify_tool_invocation(invocation, audit):
|
||||
require(str(invocation)==audit['invocationPath'] and str(invocation.resolve(strict=True))==audit['resolvedTarget'], 'Executable invocation or target changed')
|
||||
require(sha(invocation.resolve(strict=True))==audit['targetSha256'],'Executable target bytes changed')
|
||||
|
||||
def build_native_daemon(stage, scratch, env, run, receipt, save):
|
||||
"""Compile this source; historical artifact bytes are never an input."""
|
||||
pin=INPUTS['freshNativeBuild']
|
||||
require(pin=={'toolchain':'1.97.1','target':'x86_64-apple-darwin','jobs':2,'deadlineSeconds':1500},'Unexpected native build recipe')
|
||||
rustup,audit=tool_invocation('rustup')
|
||||
verify_tool_invocation(rustup,audit)
|
||||
receipt['rustup']=audit;save()
|
||||
run([rustup,'toolchain','install',pin['toolchain'],'--profile','minimal','--component','rustfmt'],'rust-install',600)
|
||||
verify_tool_invocation(rustup,audit)
|
||||
cargo=Path(run([rustup,'which','--toolchain',pin['toolchain'],'cargo'],'cargo-path',30).strip()).resolve(strict=True)
|
||||
rustc=Path(run([rustup,'which','--toolchain',pin['toolchain'],'rustc'],'rustc-path',30).strip()).resolve(strict=True)
|
||||
env['RUSTC']=str(rustc)
|
||||
version=run([rustc,'--version','--verbose'],'rust-version-verbose',30)
|
||||
require(version.startswith('rustc 1.97.1 '),'Wrong Rust')
|
||||
cargo_version=run([cargo,'--version'],'cargo-version',30)
|
||||
require(cargo_version.startswith('cargo 1.97.1 '),'Wrong Cargo')
|
||||
sdk=Path(run(['/usr/bin/xcrun','--show-sdk-path'],'sdk-path',30).strip()).resolve(strict=True)
|
||||
require(sdk.is_dir(),'Apple SDK missing')
|
||||
sdk_version=run(['/usr/bin/xcrun','--show-sdk-version'],'sdk-version',30).strip()
|
||||
require(sdk_version,'Apple SDK version missing')
|
||||
env['SDKROOT']=str(sdk)
|
||||
tools={}
|
||||
for name in ('clang','ld'):
|
||||
path=Path(run(['/usr/bin/xcrun','--find',name],name+'-path',30).strip()).resolve(strict=True)
|
||||
require(path.is_file() and os.access(path,os.X_OK),'Apple tool missing: '+name)
|
||||
tools[name]={'path':str(path),'sha256':sha(path)}
|
||||
settings={p.name:sha(p) for p in (sdk/'SDKSettings.json',sdk/'SDKSettings.plist') if p.is_file()}
|
||||
require(settings,'Apple SDK settings missing')
|
||||
receipt['compiler']={'rustcSha256':sha(rustc),'cargoSha256':sha(cargo),'versionVerbose':version,'cargoVersion':cargo_version,'jobs':2,
|
||||
'sdk':{'path':str(sdk),'version':sdk_version,'settings':settings},'appleTools':tools,
|
||||
'target':pin['target'],'rustflags':env.get('RUSTFLAGS',''),'targetDirectory':env['CARGO_TARGET_DIR']}
|
||||
save()
|
||||
run([cargo,'build','--release','--target',pin['target'],'--manifest-path','packages/paperclip-runner/runner/Cargo.toml','--locked',
|
||||
'-p','paperclip-runner-core','--bin','paperclip-runnerd','-j','2'],'daemon-build',pin['deadlineSeconds'])
|
||||
daemon=stage/'packages/paperclip-runner/dist/bin/paperclip-runnerd';daemon.parent.mkdir(parents=True,exist_ok=True)
|
||||
shutil.copy2(scratch/'target'/pin['target']/'release/paperclip-runnerd',daemon)
|
||||
run(['/usr/bin/codesign','--force','--sign','-',daemon],'daemon-sign',30)
|
||||
run(['/usr/bin/codesign','--verify','--strict',daemon],'daemon-signature-verify',30)
|
||||
require('Mach-O 64-bit executable x86_64' in run(['/usr/bin/file',daemon],'daemon-architecture',30),'Wrong daemon architecture')
|
||||
receipt['daemonBuildMetadata']=json.loads(run([daemon,'--build-metadata'],'daemon-metadata',30))
|
||||
require(receipt['daemonBuildMetadata']==INPUTS['expectedDaemonBuildMetadata'],'Wrong daemon build metadata')
|
||||
receipt.update(nativeRecompiled=True,nativeDaemonReuse=False,nativeBuildSource=SOURCE,daemonSha256=sha(daemon))
|
||||
save()
|
||||
return daemon
|
||||
|
||||
def execute(args):
|
||||
stage=args.source.resolve(strict=True);out=args.output.resolve()
|
||||
out.mkdir(mode=0o700,parents=True,exist_ok=False)
|
||||
@@ -45,7 +89,6 @@ def execute(args):
|
||||
inherited_path=os.environ['PATH']
|
||||
node=Path(shutil.which('node')).resolve(strict=True)
|
||||
pnpm=Path(shutil.which('pnpm')).resolve(strict=True)
|
||||
# Reused daemon bytes retain their original compiler provenance; no new compiler is invoked.
|
||||
env={'PATH':inherited_path,'HOME':str(scratch/'home'),'TMPDIR':str(scratch),'LANG':'en_US.UTF-8','CI':'true',
|
||||
'PAPERCLIP_TELEMETRY_ENABLED':'false','PAPERCLIP_RUNNER_SOURCE_REVISION':SOURCE,
|
||||
'CARGO_HOME':str(scratch/'cargo'),'RUSTUP_HOME':str(scratch/'rustup'),'CARGO_TARGET_DIR':str(scratch/'target'),
|
||||
@@ -129,13 +172,7 @@ def execute(args):
|
||||
run([pnpm,'install','--frozen-lockfile','--ignore-scripts','--package-import-method','copy'],'install',600)
|
||||
source_guard.verify_source(stage,SOURCE,PIN['resolvedLockSha256'])
|
||||
run([pnpm,'--filter','@paperclipai/paperclip-runner','build:typescript'],'typescript',600)
|
||||
daemon=stage/'packages/paperclip-runner/dist/bin/paperclip-runnerd'
|
||||
receipt['nativeDaemonReuse']=import_daemon(args.daemon_archive,out/'source.tar',out/'reused-native-evidence',daemon,INPUTS['nativeDaemonReuse']);save()
|
||||
run(['/usr/bin/codesign','--verify','--strict',daemon],'daemon-signature-verify',30)
|
||||
require(sha(daemon)==INPUTS['nativeDaemonReuse']['selectedFiles']['paperclip-runnerd']['sha256'],'Daemon changed during signature verification')
|
||||
require('Mach-O 64-bit executable x86_64' in run(['/usr/bin/file',daemon],'daemon-architecture',30),'Wrong daemon architecture')
|
||||
receipt['daemonBuildMetadata']=json.loads(run([daemon,'--build-metadata'],'daemon-metadata',30))
|
||||
require(receipt['daemonBuildMetadata']==INPUTS['nativeDaemonReuse']['daemonBuildMetadata'],'Reused daemon metadata changed')
|
||||
daemon=build_native_daemon(stage,scratch,env,run,receipt,save)
|
||||
pack=scratch/'provider-pack'
|
||||
run([node,'packages/paperclip-runner/scripts/build-provider-pack.mjs',pack,'--candidate-providers=pi,copilot,cursor'],'pack-build',600)
|
||||
verified=json.loads(run([node,HERE/'verify-pack.mjs',pack,SOURCE,'darwin','x64'],'pack-verify',180))
|
||||
@@ -143,7 +180,7 @@ def execute(args):
|
||||
authority=source_guard.capture_authority(stage,pack)
|
||||
source_guard.verify_source(stage,SOURCE,PIN['resolvedLockSha256'],pack,authority)
|
||||
atomic_json(out/'pack-inventory.json',closed_tree(pack));shutil.copy2(pack/'provider-pack.json',out/'provider-pack.json');shutil.copy2(daemon,out/'paperclip-runnerd')
|
||||
receipt.update(packVerification=verified,packManifestSha256=sha(out/'provider-pack.json'),packInventorySha256=sha(out/'pack-inventory.json'),daemonSha256=sha(daemon),nodeSha256=sha(node),declaredNativeInputsMatchOriginal=True,outputDigestsAssumedEqual=False)
|
||||
receipt.update(packVerification=verified,packManifestSha256=sha(out/'provider-pack.json'),packInventorySha256=sha(out/'pack-inventory.json'),daemonSha256=sha(daemon),nodeSha256=sha(node),outputDigestsAssumedEqual=False)
|
||||
receipt['providerPackArchive']=retain_pack(pack,out/'provider-pack.tar.gz',authority['pack'])
|
||||
save()
|
||||
test=stage/'packages/paperclip-runner/test/pi-closed-startup.test.mjs'
|
||||
@@ -175,4 +212,4 @@ def execute(args):
|
||||
receipt['finishedAt']=datetime.datetime.now(datetime.timezone.utc).isoformat();save()
|
||||
|
||||
if __name__=='__main__':
|
||||
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--source',type=Path,required=True);parser.add_argument('--output',type=Path,required=True);parser.add_argument('--daemon-archive',type=Path,required=True);execute(parser.parse_args())
|
||||
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--source',type=Path,required=True);parser.add_argument('--output',type=Path,required=True);execute(parser.parse_args())
|
||||
Reference in new issue
Block a user