Repository navigation
Restore Sparkle notarization credentials from Fleet-OPS Infisical - #257
Conversation
This comment has been minimized.
This comment has been minimized.
🤔 Insufficient Task ContextI found a task linked to this PR, but it only contains minimal information (title only, no description or acceptance criteria). To perform a meaningful business rules validation, I need more details. 🔍 What I need to validate:
💡 How to improve the task context:
|
This comment has been minimized.
This comment has been minimized.
The Mac release has failed on every merge to main since 2026-10-11. The cause was the credential source and not the release script: the workflow read the App Store Connect key from repository secrets that are a revoked copy (HTTP 401, CC-163), while the working key lives in Fleet-OPS prod /asc. This replaces the bare `infisical secrets get` calls with the repository's own safe fetch, in both workflows that read the key. - ASC_KEY_ID and ASC_ISSUER_ID come from the infisical-secrets composite action, which masks every line of every value and never prints one. - ASC_KEY_P8 is fetched by scripts/infisical-fetch-asc-key.mjs and streamed into scripts/ios-stage-asc-key.sh over a pipe, so the private key still never becomes an environment variable and only its mode-600 file path crosses the step boundary. The project id is read from the INFISICAL_FLEET_OPS_PROJECT_ID secret instead of being written into a public workflow, where it was also interpolated into the job log on every failed notarization. A dispatch-only job, "Verify the release credentials load", now fetches the key and checks that Apple's notarization service still accepts it, and publishes nothing. A green pull-request check proves the workflow's own contract, not that a credential can be fetched, which is why this outage ran for nine days without a check noticing. Fixes CC-177
A workflow_dispatch run of the release job would fetch the signing key and publish a build from whatever branch it was dispatched on. The new verify-only input stops the release job at the publish gate, so the credential check can be run against a branch before a merge without putting a build in front of users. The contract test pins the input and the gate that honours it.
A manual run can now be asked to fetch the release credentials and check that Apple's notarization service still accepts them without publishing anything. The contract test pins the input and the gate that honours it, so the two cannot drift apart.
This comment has been minimized.
This comment has been minimized.
|
Both findings are addressed in Finding 1 — the hard-coded project id (Rule 60). The id is no longer in this public file. It now comes from the
Finding 2 — the bare
The open gate. One thing added beyond the findings. A dispatch-only job, "Verify the release credentials load", fetches the key and checks that Apple's notarization service still accepts it, and publishes nothing. A green pull-request check proves the workflow's own contract, not that a credential can be fetched, which is why this outage ran for nine days without a check noticing. It can be run against a branch before a merge, and the contract test pins its loader to the release job's so the two cannot drift. Gates. 32 contract tests and the key-handoff shell test pass, including a new offline test that drives the real fetch script and the real staging script against a stubbed Infisical and checks that the key reaches a mode-600 file and never the environment or the log. Fixes CC-177 |
loginUniversalAuth returns the token string directly, so `const { token }`
set it to undefined and the list call went out as `Bearer undefined`.
Assign the return value straight to `token`, matching the other caller in
scripts/infisical-fetch.mjs, and assert the bearer header in the contract
test so the stub can no longer accept an unauthenticated call.
Co-Authored-By: Claude Code <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
Addressed: every finding in this round was answered on a later commit and its thread is resolved. This review-level state is stale.
Security finding: the mask helper reveals short values in fullThe review summary on this pull request says the two critical findings in
mask() {
local value="$1"
if [ "${#value}" -lt 5 ]; then
printf '%s' "<redacted>"
return
fi
printf '%s…%s' "${value:0:8}" "${value: -4}"
}A first-eight and last-four preview shows the whole value whenever the value is eleven characters or shorter, because the two pieces overlap. Measured on the branch's own function:
The redacted branch only covers four characters or fewer, so the guard is off by the whole range that matters. The second finding, that a rejected short credential can still be exposed in full, is this same defect seen from the rejected-token path, because the rejected value goes through this same helper. The helper's own comment says the preview "is not reversible, because the ellipsis stands for the part that is not shown" and that "a value too short to have a hidden middle is shown as redacted instead". With this threshold neither statement is true. The fixKeep the first-eight and last-four shape and move the threshold to where the hidden middle is at least as long as the prefix that is shown, so the preview can never be reconstructed: if [ "${#value}" -lt 20 ]; then
printf '%s' "<redacted>"
return
fi
printf '%s…%s' "${value:0:8}" "${value: -4}"A value of twenty characters shows the first eight and the last four and hides eight characters in the middle. A value of nineteen or shorter is redacted outright. The test moves with it
What is not wrong hereThe mac-release gate, the vault-only credential path and the ASC staging flow all pass on this head. The gate is the credential-free half of the release, and the read-only notarization preflight against the Fleet-OPS prod key succeeds. |
Update: the owning session has this fixed in progress, and its threshold is better than the one aboveThe release branch's working tree already carries the fix for this finding, and it uses the exact boundary rather than the one proposed above. The helper now redacts a value of twelve characters or fewer, and a new test drives four lengths through it: ten, twelve, thirteen and thirty two, asserting both the shape of the preview and that the hidden middle stays hidden. The reasoning matches the measurement here: the two slices the preview is built from cover twelve characters between them, so a value of twelve characters or fewer is reconstructed in full by the two of them together, and thirteen is the first length with a middle left over. That is the boundary, and it is tighter than the threshold proposed above, which is withdrawn. No further change is needed from this seat, and this seat will not touch the branch or the lane the owning session is working in. |
The masked preview was built from the first eight characters and the last four of a value, and those two slices cover twelve characters between them. A credential of twelve characters or fewer was therefore reconstructed in full by the two of them together, and the ellipsis between them was only decoration. The preview now applies to a value of thirteen characters or more, and a shorter one is redacted outright. The message that carries the preview names the credential, so an operator still knows which value was refused: the universal auth login failure now says that the client id or the client secret was refused, and the reader failure already says that the minted access token was rejected. The test that asserted the preview for a ten character token asserted the leak it was meant to prevent, because that preview was the whole value. It now asserts the redaction, and a new test walks the boundary: ten and twelve character values are redacted, thirteen and thirty character values carry a preview, and no slice of any of them reaches the log. Refs CC-177
Three faults in the credential path, each with a test that fails without its fix. The replacement in a bash substitution is a pattern, and in bash 5.2 and later an ampersand in it expands to the text that matched, which here was the credential itself: a value whose preview carried an ampersand was written back into the log in full. The option is now turned off where the shell has it, and the preview refuses a value that could put an ampersand in it. The client id and the client secret stand for months and the runner masks them by value, so a preview of either defeated that masking and put twelve characters of a standing credential in a public log. Both are redacted whole now, and only the access token, which is minted for one job, may carry a preview. The proof read a name the environment carries as resolved, so a name the environment held as whitespace was skipped and the vault was not asked for it, while the consumer trims a value and refuses one that holds nothing but whitespace. The proof now ignores a value with nothing but whitespace in it, so the vault still has to carry the name. Refs CC-177
Every finding in this round was answered on a later commit and its thread is resolved, so this review-level state is stale for the current head.
Code Review Completed! 🔥The code review was successfully completed based on your current configurations. Kody Guide: Usage and ConfigurationInteracting with Kody
Current Kody ConfigurationReview OptionsThe following review options are enabled or disabled:
|
|
The release gate's cancelled-run recovery on this branch cannot work, and it is armed on the release path, so it will burn the release window instead of saving it. The mechanism, in scripts/await-swift-ci-checks.sh lines 121 to 127 on the current head. When the Swift CI checks report a cancelled run, the gate posts a replacement to the workflow dispatch endpoint with the commit sha as the ref: The dispatch endpoint takes a branch or a tag name and rejects a commit sha. The request answers 422 No ref found, so no run is created and the run count does not move. This was proved live rather than read: the same request against a branch ref resolves, and the same request against a workflow without the trigger answers Workflow does not have workflow_dispatch trigger, so the failure is the ref and not the permission and not the trigger. The armed path is mac-release.yml line 106, which sets REDISPATCH_ON_CANCEL to 1 on the await-swift-ci job. The job has actions: write, so the request is permitted. The gate therefore takes the dispatch branch, the request fails, and three things follow. The budget is spent on failures. The counter moves at line 119 before the request is made at line 121, so an attempt is counted even when nothing was created. With MAX_REDISPATCH at 3, the gate can burn all three attempts on three requests that never produce a run. The failure is swallowed. The request result goes to /dev/null and the else branch at line 126 prints a warning that the replacement could not be dispatched and that the gate keeps waiting for a run to appear. The gate then keeps polling for a run that was never created, until the deadline at DEADLINE_SECONDS, which is 5100 seconds. The release job has a 90 minute timeout, so the gate spends about 85 of those minutes waiting for a run that does not exist and then fails with a timeout rather than with the real reason. There is also a concurrency half, and it matters for what happens next. The swift-ci.yml on this branch already gives a push to main one group per commit, so a later merge cannot cancel the compile run an earlier merge's release is waiting on. That is the right shape and it stays. But the pull request path is still one shared group per pull request with cancel-in-progress on, so every new push to this pull request cancels the compile run that the previous head started. The run 39661660828 at head 7dc639b is one of those cancellations, and the head moved six times today. That cancellation is exactly the condition the broken dispatch path is there to recover from, so the gate and the cancellation are going to meet. The fix is ready on minimax/ci-redispatch at f6e8f6e, pushed and green. It changes the mechanism from dispatching a new run to re-running the cancelled one, through the run rerun endpoint: A re-run keeps the exact commit and the exact event of the run it replaces and produces a new, higher check run id, which is what lets the gate see that the replacement arrived. The dispatch endpoint cannot do that, because it cannot name a commit. The attempt counter now moves only after the re-run succeeds, so a token without actions: write cannot turn the gate into a loop, and a cancelled run with the flag off is a fast failure instead of a stall. Verification on that branch: python3 scripts/compile_gate_checks_test.py ran 30 tests and passed, and node --test over the workflow test files ran 31 tests and passed, with 0 failures. The new scripts/swift-ci-workflow.test.mjs asserts that the re-run endpoint is used and that the dispatch endpoint and its ref=sha form are absent. This seat is not pushing to this branch, for the same reason as the earlier finding about the credential preview: another session owns it and has been moving the head today. The change is the gate script, the workflow and their tests. One correction to an earlier note from this seat, which said the concurrency half of this work was already in this branch. It is there for a push to main, in the shape described above. The pull request path still shares one group, and the event is not part of the group, so two runs at the same commit started by different events can cancel one another. The branch on minimax/ci-redispatch adds the event to the group for everything that is not a pull request, so a push to main and a manual dispatch at the same commit do not share a group. Sequencing: the pull request for this work opens after the release publishes, and the branch is moved onto the main that this pull request leaves behind before it opens. Merging it will conflict with this branch in all five changed files, and the resolution keeps this branch's per-commit group and takes the re-run gate. Refs CC-185 |
#257 merged to main as ad87965 and it reworked the iOS ship workflow's credential path: the tooling and the staging script now run in one step and the private key never enters a step environment. This branch still carried the pre-#257 copy of that workflow, with the inline Infisical login and a hard-coded project id, so merging it as it stood would have reverted the release fix for the iOS ship path. Merging main brings the hardened step back. No branch work was lost: the branch had no changes to the files this merge replaced.
The squash of PR 271 landed while the credential handoff fix was still on the branch, so main has the hardened credential load from #257 and the old standing-testers step that calls the removed asc-key.mjs exec subcommand. Merging main in picks up the squashed work and leaves the loader change as the only difference.
| # operator to fix vault permissions when the fault is the tooling. This check | ||
| # names the fault instead. | ||
| # | ||
| # The proof is read-only and lists names, never values: `fleet-secrets names |
There was a problem hiding this comment.
Login failure message blames the client credentials for any non-zero exit
A non-zero infisical login --method=universal-auth exit can reflect a network or API failure, so blaming the credentials may prompt an unnecessary secret rotation. Change say to present the client ID, client secret, and API as checks rather than asserting that either credential was refused.
Prompt for LLM
File scripts/install-release-secrets-tooling.sh, line 227:
Login failure message blames the client credentials for any non-zero exit
The login-failure message now asserts a specific cause ("the client id or the client secret was refused") for every non-zero exit of `infisical login --method=universal-auth`, but that exit code is also produced by network failures, HTTP 429 and 5xx responses, none of which are a refused credential. A transient API or network fault during the release now tells the operator that the standing client credential was rejected, sending them to rotate a long-lived secret for a failure that has nothing to do with it, and the previous wording (exit code plus the CLI's own detail) let the detail speak for itself. Name the credential as the thing to check without claiming it was the cause, e.g. `say "the Infisical CLI refused the universal auth login (exit $status); check the client id, the client secret and the API: $(scrub "$(cat "$work/login.err")")"`.
Suggested code:
say "the Infisical CLI refused the universal auth login (exit $status); check the client id, the client secret and the API: $(scrub "$(cat "$work/login.err")")"
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.
| const cases = [ | ||
| { token: 'abcdefghij', preview: '<redacted>', hidden: 'abcdefghij' }, | ||
| { token: 'abcdefghijkl', preview: '<redacted>', hidden: 'abcdefghijkl' }, | ||
| { token: 'abcdefghZijkl', preview: 'abcdefgh…ijkl', hidden: 'Z' }, |
There was a problem hiding this comment.
Preview test's one-character hidden middle makes it depend on error wording
A one-character hidden value makes the result.stderr.includes check depend on unrelated error wording and can fail without any credential leakage. Use a multi-character hidden value and scope the assertion to the rendered preview.
Prompt for LLM
File scripts/stage-asc-key.node-test.mjs, line 714:
Preview test's one-character hidden middle makes it depend on error wording
The 13-character preview case uses the single character `Z` as its "hidden middle" and asserts `!result.stderr.includes('Z')`, which scans the entire failure message rather than the credential preview. The case is coupled to the exact wording of the script's error text, so any future rewording that happens to contain a capital "Z" fails the contract test even though nothing leaked, and it contradicts the case list's own stated invariant that the hidden middle "does not occur in the prose around it" (the other three cases use multi-character strings for exactly that reason). Use a multi-character hidden middle so a collision with prose is implausible, and scope the assertion to the preview instead of the whole stderr.
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.
Summary
Restores Sparkle release notarization by sourcing the App Store Connect credentials from Fleet-OPS Infisical (
prod/asc) instead of the stale repositoryASC_*secrets, and adds a read-only notarization preflight plus credential-free contract tests.What changed
.github/workflows/mac-release.ymlASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8from the job environment, so the workflow no longer readssecrets.ASC_*.SPARKLE_ED_PRIVATE_KEYis now only supplied outside pull-request runs.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID/_SECRET, falling back toINFISICAL_CLIENT_ID/_SECRET, and fails if neither is configured.ASC_KEY_IDandASC_ISSUER_ID, rejects empty or multiline values, masks them, and writes them toGITHUB_ENV.ASC_KEY_P8and pipes it toscripts/ios-stage-asc-key.sh, so only a mode-600 file path (ASC_KEY_PATH) crosses the step boundary.ASC_KEY_PATHand to reject a missing or empty staged key file.NOTARY_KEY_PATH,NOTARY_KEY_ID, andNOTARY_ISSUER_IDfrom the loadedenv.ASC_*values.$ASC_KEY_PATHinstead of a hardcoded temporary key.assets/AppIcon.icns,scripts/check-notary-access.sh,scripts/mac-release-workflow.test.mjs, andscripts/ios-stage-asc-key.sh.node --test scripts/mac-release-workflow.test.mjs) before the Swift CI wait.scripts/check-notary-access.sh(new)ASC_KEY_ID,ASC_ISSUER_ID, and a non-emptyASC_KEY_PATH.xcrun notarytool historyand reports only a fixed failure category (authentication rejected, service access rejected, connectivity failure, key format/issuer failure, or unclassified) without printing Apple's raw response or credential values.scripts/mac-release-workflow.test.mjs(new)secrets.ASC_*, uses the fleet loader, passes the staged key path through, cleans up the key, blocks PR notarization/publication, and orders the preflight before the release build.check-notary-access.shcovering success, sanitized failure categories, raw-error withholding, temporary-file cleanup, and failure when credentials are missing before Apple is invoked.docs/AUTO-UPDATE.mdprod/asc, the staging script, and the read-only notarization preflight.Open review findings
infisical secrets getcall was added to a public workflow; it should be routed throughscripts/infisical-secrets-safe.sh.Summary
Updates the failure diagnostics in the macOS release workflow so that missing Sparkle/notarization credential errors point to the Fleet-OPS prod
/ascpath rather than referencing the internal Infisical project ID variable.Changes
/asc: it now instructs that the CI identity must have read access to Fleet-OPS prod/asc, dropping the reference to${ASC_INFISICAL_PROJECT_ID}.Impact
Summary
Routes the App Store Connect credentials used by the Sparkle macOS notarization release back to Fleet-OPS Infisical
prod /asc. The workflows now use a secret-backed Fleet-OPS project ID, the existinginfisical-secretscomposite action for the key ID and issuer ID, and a protected handoff for the multiline private key.The same credential-loading flow is applied to both
mac-release.ymlandios-ship.yml, with offline contract coverage and a manual release-credential verification path.Functional changes
INFISICAL_FLEET_OPS_PROJECT_ID.scripts/infisical-fetch-asc-key.mjsto authenticate to Infisical, retrieveASC_KEY_P8from Fleet-OPSprod /asc, mask the token and key material, and stream the key intoscripts/ios-stage-asc-key.sh.ASC_KEY_PATHis published, with the key stored in a protected directory and file.verify-release-credentialsjob that loads the release credentials, checks Apple notarization access withscripts/check-notary-access.sh, and removes the staged key without publishing.verify-onlyworkflow-dispatch option that marks the release as non-publishing./codecaps-sparkle-ed25519-keyasCODECAPS_SPARKLE_ED25519, with temporary export files deleted afterward.Validation
prodenvironment, and/ascpath.0600key files, mode-0700staging directories, and absence of key material from the environment or logs.node --test scripts/ios-ship-workflow.test.mjs scripts/infisical-fetch-asc-key.node-test.mjsbash scripts/ios-fleet/test-asc-credentials.shnode --test scripts/ios-fleet/test-asc-credentials.mjsOutstanding automated-review findings
The listed review findings remain unresolved:
Bearer undefinedduring the vault request.verify-onlyrelease path can still perform a full build without running the intended notarization preflight.contents: writerather than read-only repository permissions.Authorizationheader, so authenticated vault access is not exercised.The review summary also reports two additional critical findings that could not be posted and are not included in the available list.
Summary
Restores Sparkle release notarization credentials by loading
ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8from Fleet-OPS Infisical (prod/asc) in the Mac Release and iOS Ship workflows. This replaces direct vault access with the approvedinfisical-secretsaction and addresses the revoked repository key that caused Apple notarization to return HTTP 401 and blocked Mac publishing.Key changes
scripts/stage-asc-key.mjsto securely stage the multiline private key:ASC_KEY_PATHfor downstream steps.ASC_KEY_P8from the job environment after staging.verify-onlydispatch option to the Mac Release workflow. It fetches the credentials and asks Apple’s notarization service whether they remain valid, but does not build or publish a release.contents: readso it cannot push commits, tags, or releases while holding the signing key.Validation
CI runs the Mac and iOS workflow contract tests, the new ASC key staging tests, and the existing App Store Connect credential checks.
Automated review follow-ups
ASC_KEY_P8.node scripts/stage-asc-key.mjsthrough its CLI entry point, matching how CI executes it.Summary
Restores and verifies the Fleet-OPS/Infisical credential flow used by the Sparkle/macOS notarization release. The App Store Connect signing key is retrieved from Fleet-OPS prod
/asc, staged in a protected file, and exposed to later CI steps only through its file path.Changes
ASC_KEY_P8secret and its Fleet-OPS prod/asclocation.0600permissions.ASC_KEY_PATHis published to subsequent steps.ASC_KEY_P8is cleared from the environment after the path is published.infisical-secretsaction to the staging script and onward release steps.Testing
Targeted Node test suites:
scripts/infisical-fetch-expand.node-test.mjsscripts/stage-asc-key.node-test.mjsThe review identified that the new bearer-token test is not currently invoked by any CI workflow; wiring it into CI remains necessary for continuous regression protection.
Summary
/ascpath rather than stale repository copies, and notarization access is checked with read-onlynotarytool historybefore a publishing build.verify-onlydispatches from the release job. Credential verification now runs in a dedicated read-only job without loading Developer ID, Sentry, and Sparkle release material, installing the build toolchain, producing a release bundle, or publishing.main. Manual runs from other branches remain build-and-verify only and are stopped by the publication gate before release credentials are processed.UsageAlertPreferences.swiftnow also trigger the iOS ship workflow.infisical-fetch*.mjshelpers.stage-asc-key.mjs, and is cleared from the environment in the same step, leaving only the file path for later steps.CI Coverage
scripts/infisical-fetch-expand.node-test.mjsalongside the existing workflow, ASC key-staging, and Fleet-OPS credential tests.Outstanding Review Notes
verify_onlybranch after the release job was isolated, while the contract test still treats that branch as live.Summary
Restores the App Store Connect credentials used by Sparkle notarization and iOS TestFlight from Fleet-OPS Infisical (
prod,/asc) without using the stale repositoryASC_*secrets.The iOS and macOS release workflows now pass their Infisical connection settings directly to
scripts/stage-asc-key.mjs. The script authenticates with Infisical, retrieves the ASC key, key ID, and issuer ID, and stages the private key in a protected file for subsequent signing and notarization steps.Changes
infisical-secretsloading steps fromios-ship.ymland both credential-loading paths inmac-release.yml.stage-asc-key.mjsto:ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8from the configured secret path.ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_PATHto later workflow steps.ASC_KEY_P8value supplied through the environment./ascaccess failures are reported.Review Findings
The automated review identified unresolved concerns:
ASC_KEY_IDandASC_ISSUER_IDare written toGITHUB_ENVwithout being masked.Restore Sparkle notarization credentials from Fleet-OPS Infisical
Summary
ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8from Fleet-OPS Infisicalprod/asc. The workflows continue to avoid stale repository signing secrets.ASC_KEY_PATHand the masked key and issuer identifiers.fleet-secrets getfallback. Missing, malformed, and unresolved Infisical values are rejected before staging.Test coverage
node --test scripts/ios-ship-workflow.test.mjs scripts/stage-asc-key.node-test.mjs scripts/infisical-fetch-expand.node-test.mjs scripts/ios-enable-apns.test.mjsbash scripts/ios-fleet/test-asc-credentials.shnode --test scripts/ios-fleet/test-asc-credentials.mjsnode --test scripts/mac-release-workflow.test.mjsCoverage includes environment-first credential resolution, removal of loader-written key entries, malformed and unresolved-value handling, staging and interruption failures, and verification that key material does not survive into later steps.
Known review follow-ups
infisical-fetch.mjsafter it writesASC_KEY_P8but before staging begins can still leave the key in$GITHUB_ENV.Summary
This PR restores access to the App Store Connect credentials required for Sparkle notarization from Fleet-OPS Infisical (
prod,/asc) and aligns the iOS TestFlight workflow with the same credential flow. The notarization and publication logic is unchanged; only credential installation, retrieval, and staging are updated.Changes
scripts/install-release-secrets-tooling.shto:fleet-secretsreader from a pinned AI-Fleet-Coordinator commit./ascsecret folder.scripts/stage-asc-key.mjstogether in one step.infisical-fetch.mjsloader for ASC credentials.ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8secrets.fleet-secrets.Automated Review Findings
The review reports seven outstanding concerns in the current patch:
infisical-fetch.mjs.scripts/install-release-secrets-tooling.shis not included in the workflow path filters.Summary
/ascpath instead of stale repository signing secrets.scripts/install-release-secrets-tooling.shown the complete credential lifecycle: it installs the pinned Infisical CLI and Fleet reader, mints a universal-auth session, and runsscripts/stage-asc-key.mjsin the same process tree.release-secrets-env.shhandoff. The Infisical token and project ID now pass directly through the staged consumer’s environment without being persisted by the workflow.GITHUB_ENV; the installer error is returned when setup fails, otherwise the staging error is returned.Test Coverage
Updated workflow tests verify the single-command credential bootstrap, removal of the token file handoff, token/project export to the consumer, cleanup and exit-status behavior, and inclusion of the tooling script in release path filters. Staging tests cover environment-first credential resolution, reader fallback, key removal, malformed-entry cleanup, and fail-closed handling of truncated entries.
Outstanding Review Findings
/asccredential rather than a tooling failure.printf; output emitted throughsayorfailis not covered.docs/AUTO-UPDATE.mdstill misstates the ASC credential source and key-handling behavior.Summary
Test Infrastructure
curlstub to avoid network access.Coverage Notes
Summary
gitleaks:allow, preventing GitLeaks from misidentifying test data as leaked notarization credentials.Review Note
Summary
Restores Sparkle notarization credential retrieval from Fleet-OPS production Infisical (
/asc) through the fleet’sfleet-secretsreader rather than stale repository credentials.Changes
fleet-secrets names --path /ascbefore App Store Connect credentials are staged.check-readermode for testing an existing token and reader without running a release.$GITHUB_ENV.Summary
ASC_KEY_IDandASC_ISSUER_IDare not preloaded into the job environment and no Infisical token is available.Fleet-OPS prod /ascis exercised without relying on local secrets.ASC_KEY_ID, does not stage anASC_KEY_PATH, removes the key-bearing entry from the job environment, and never exposes the key in output.Summary
Restores the Sparkle release’s App Store Connect notarization credentials from the Fleet-OPS production Infisical
/ascfolder throughfleet-secrets. Credential retrieval is validated before staging, while failures are classified so CI operators can distinguish missing vault entries, unresolved secret references, invalid tokens, and reader/tooling problems.Functional changes
/ascfolder contains all required credential names:ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8.INFISICAL_TOKENfor standalonecheck-readerruns and distinguishes an absent token from a rejected token.docs/AUTO-UPDATE.mdwith the credential source, validation behavior, failure distinctions, and standalone verification instructions.Outstanding review findings
Summary
Restores Sparkle notarization credential setup through Fleet-OPS Infisical and makes the release gate more resilient to canceled Swift CI jobs.
Changes
ASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8supplied through the job environment are no longer incorrectly reported as missing from the vault.swift-ci.ymlrun for the same commit when a compile check is canceled, with a maximum of three replacement attempts. The release workflow grants the gateactions: writeto enable this behavior.mainSwift CI concurrency groups by commit SHA, preventing a later merge from canceling another merge’s compile run.Security review follow-up
The automated review reported three critical findings in
scripts/install-release-secrets-tooling.shat line 94:These findings remain outstanding and should be addressed before merge.
Summary
Updates the Fleet-OPS Infisical release-secrets tooling used by the Sparkle notarization workflow to improve credential-failure diagnostics and align reader validation with credential resolution.
Changes
<redacted>.check_readersoASC_KEY_ID,ASC_ISSUER_ID, andASC_KEY_P8do not need corresponding vault entries when they are already supplied through the job environment, matchingstage-asc-key.mjsenvironment-first resolution.Automated Review Findings
The automated review reported six unresolved findings:
&into the full secret.Summary
Hardens the Fleet-OPS Infisical credential checks used by the Sparkle notarization workflow so authentication failures remain actionable without exposing complete secrets.
Changes
&back into logs.&; longer tokens retain only an eight-character prefix and four-character suffix.Fixes CC-177