Skip to content

Vendor the archive digest fix - #33

Open
adamXbot wants to merge 1 commit into
mainfrom
projectctl/archive-digest-fix
Open

adamXbot wants to merge 1 commit into
mainfrom
projectctl/archive-digest-fix

Conversation

@adamXbot

@adamXbot adamXbot commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

What

Re-vendors the project-command runtime from AdamXweb/dev main (which carries AdamXweb/dev#19) with dev's standards/project-management/install.py. Only .project/projectctl.py, its vendored test and .project/runtime.lock.json change; install.py and the justfile regenerate unchanged. The diff is line-for-line the same as AdamXweb/Orbari#121 and adamXbot/orgorbari#35.

Lock hashes now match the canonical copy: projectctl.py 904c215b… → de877635…, tests/test_projectctl.py 24da0579… → 485d1e5c…, install.py 68d74f7b… (unchanged).

Why

digest() hashed an .xcarchive's whole Info.plist. just upload runs xcodebuild -exportArchive with destination=upload, and Xcode records the distribution in that file (a Distributions key). The digest then changed and projectctl exited 1 with Upload modified the archive after a successful upload, so the central Apple build testflight operation would report a failed run for an upload that worked.

The runtime now hashes an archive's Info.plist by its archive keys and the app's identity keys (bundle ID, versions, signing identity, team, path, architectures), and every other file byte for byte. The previous vendoring (ruleset-only release gate fix) landed before dev#19, so this repository had the ruleset fix but not this one.

Local checks

just check (lint, security, docs and contract tests): 56 tests pass, 54 before; the two new ones are test_xcode_upload_bookkeeping_is_not_an_archive_change and test_upload_that_changes_the_app_or_its_identity_fails.

$ just check
...
Ran 56 tests in 6.387s

OK
Lint checked: command configuration, Python syntax and diff whitespace
Security checked: 337 tracked text files <=2 MiB for credential patterns; dependency/code audits are separate declared adapters
Documentation checked: 1 declared files; local links and visibility policy (remote links not fetched)
exit=0

CI drift step, run on the committed branch:

$ python3 .project/install.py . && git diff --exit-code -- justfile .project/runtime.lock.json
exit=0
$ git status --porcelain
(no output: working tree clean)
Full just check output
$ just check
.......................Security checked: 7 tracked text files <=2 MiB for credential patterns; dependency/code audits are separate declared adapters
Marketing version and release notes updated; review and commit the changes before release
Removed declared reproducible outputs; archive/release evidence retained
Artifact verified: 1.2.3 (2026.1001.0900)
Export ready: $TMPDIR/tmp7h5tmtrb/.project/output/export-20261016T034924920339Z/Export
Artifact verified: 1.2.3 (2026.1001.0900)
.......Export ready: $TMPDIR/tmp7h5tmtrb/.project/output/export-20261016T034924925170Z/Export
Generic artifact verified: 1.2.3 (2026.1016.0349)
Artifact verified: 1.2.3 (2026.1001.0900)
.Artifact verified: 1.2.3 (2026.1001.0900)
..................Generic artifact verified: 1.2.3 (2026.1001.0900)
Generic artifact verified: 1.2.3 (2026.1001.0900)
Executed required tests: 6
Artifact verified: 1.2.3 (2026.1001.0900)
External action: upload existing archive to App Store Connect; channel=testflight
Artifact verified: 1.2.3 (2026.1001.0900)
External action: upload existing archive to App Store Connect; channel=testflight
.Artifact verified: 1.2.3 (2026.1001.0900)
.....Marketing version and release notes updated; review and commit the changes before release
Artifact verified: 1.2.3 (2026.1001.0900)
External action: upload existing archive to App Store Connect; channel=testflight
Artifact verified: 1.2.3 (2026.1001.0900)
.
----------------------------------------------------------------------
Ran 56 tests in 6.387s

OK
Lint checked: command configuration, Python syntax and diff whitespace
Security checked: 337 tracked text files <=2 MiB for credential patterns; dependency/code audits are separate declared adapters
Documentation checked: 1 declared files; local links and visibility policy (remote links not fetched)
exit=0

CI on this PR

Observed after opening: this repository is public, so the Actions billing hold does not apply. The hosted Command contract (which runs just check and the drift step) and identity jobs pass; Native app compile (macos-latest), ci / Build & test (unsigned) and build + test (SPM) (macos-15) run on GitHub-hosted runners and were still in progress when this was written.

Not exercised: a real signed upload, because no signing credentials are available to this work.

🤖 Generated with Claude Code

digest() hashed an .xcarchive's whole Info.plist. A real `just upload`
runs xcodebuild -exportArchive with destination=upload, and Xcode
records the distribution in that file (a Distributions key), so the
digest changed and projectctl exited 1 with "Upload modified the
archive" after a successful upload.

The reviewed runtime from AdamXweb/dev#19 now hashes the archive's
Info.plist by its archive keys and the app's identity keys, and every
other file byte for byte. Vendored with dev's install.py; only
projectctl.py, its vendored test and the runtime lock change, and the
justfile regenerates unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant