Skip to content

DRY the CI secrets install into a dedicated script - #4857

Merged
mokagio merged 2 commits into
trunkfrom
ainfra-2774-a8c-secrets-prep-in-pocket-casts-ios-dry-ci-scripts
Aug 4, 2026
Merged

DRY the CI secrets install into a dedicated script#4857
mokagio merged 2 commits into
trunkfrom
ainfra-2774-a8c-secrets-prep-in-pocket-casts-ios-dry-ci-scripts

Conversation

@mokagio

@mokagio mokagio commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Part of AINFRA-2774, under AINFRA-1541.

I was looking at #4731 to get it ready for a new round of review and grew overwhelmed by the various changes and the open question of how it should flow in CI vs local (when and how to call the decrypt?) and for internal vs external contributors.

As such, I've decided to extract the parts that are already good in dedicated PRs, and to rework the remainder in isolation.

This PR DRYs how CI decrypts secrets.

AI-generated Details

Twelve CI call sites each spelled out their own bundle exec fastlane run configure_apply step, so changing how secrets are decrypted means touching all twelve.
This collapses them onto .buildkite/commands/install-secrets.sh.

The mechanism is unchanged — it still calls configure_apply.
The point is that #4731, which swaps .configure for a8c-secrets, shrinks to the parts that still need discussion once this lands.

One behaviour change: build.sh had no explicit secrets step and leaned on configure_apply in fastlane's before_all.
It now decrypts through shared_setup.sh like the other build scripts — same net effect, one extra visible Buildkite step.

To test

This PR's own build exercises the new shared path (build.shshared_setup.shinstall-secrets.sh).

The six release pipelines under .buildkite/release-pipelines/ and the prototype/tvOS upload scripts only run on demand, so CI here does not cover them.
Their edit is the same two-line-for-one-line substitution in every file — worth an eyeball, not a manual trigger.

Checklist

  • I have considered if this change warrants user-facing release notes and have added them to CHANGELOG.md if necessary.
  • I have considered adding unit tests for my changes.
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics.

Twelve call sites each spelled out their own secrets step, so changing how
secrets are decrypted meant touching all twelve.
This is groundwork for swapping `configure_apply` for `a8c-secrets`
(AINFRA-1541): afterwards that swap is a one-file diff.

`build.sh` is the only behaviour change — it had no explicit secrets step and
leaned on `configure_apply` in fastlane's `before_all`.
It now decrypts through `shared_setup.sh` like the other build scripts.

Extracted from #4731 to
shrink it to the parts still under discussion.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mokagio
mokagio requested a review from a team as a code owner July 30, 2026 03:48
Copilot AI review requested due to automatic review settings July 30, 2026 03:48
@mokagio mokagio self-assigned this Jul 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Claude finished @mokagio's task in 1m 53s —— View job


PR Review: DRY the CI secrets install into a dedicated script

  • Read the full diff (git diff origin/trunk...HEAD)
  • Trace every call site and check for double-installs / ordering changes
  • Verify no configure_apply stragglers remain
  • Post review

This is a clean, low-risk refactor. All twelve call sites now route through install-secrets.sh, either directly (upload scripts + the six release pipelines) or via shared_setup.sh (the build scripts). I verified there are no stragglers — the only remaining configure_apply is the single one inside install-secrets.sh (plus the unrelated before_all in fastlane/Fastfile, which is out of scope here).

Things I checked that are correct:

  • No double-decryption. The four build scripts (build.sh, prototype-build.sh, release-build.sh, build-tvos-app-store.sh) each call shared_setup.sh and had their inline step removed, so secrets run exactly once. The two upload scripts don't source shared_setup.sh, so calling install-secrets.sh directly is correct.
  • Executable bit + shebang. install-secrets.sh is 100755 and uses set -euo pipefail — actually stricter than the callers' -eu, which is a small improvement.
  • Subprocess bundle exec works. install-secrets.sh runs as a child process, but bundler config is file-based (.bundle/config) and BUNDLE_* env vars are inherited, so bundle exec resolves the same as when the command was inline. install_gems still runs before it in every path.

Minor observations (non-blocking):

  1. The explicit step is redundant with before_all. fastlane/Fastfile:211 already calls configure_apply in before_all for every lane, so any script that ends up running a fastlane lane decrypts secrets a second time. This is pre-existing (the old inline step had the same redundancy) and configure_apply is idempotent with negligible overhead per the Fastfile comment, so it's not a regression — just worth being aware of as you land #4731.

  2. Log-header label change in the release pipelines. The six .yml files previously printed --- 🔐 Access Secrets; they'll now show --- :closed_lock_with_key: Installing Secrets from inside the script. Purely cosmetic, and arguably more consistent with the rest of the pipeline — flagging only so it's an intentional change, not a surprise in the Buildkite UI.

  3. Ordering nudge in shared_setup.sh. Secrets now decrypt before install_swiftpm_dependencies rather than after (in prototype-build.sh/release-build.sh the inline step used to run last). No dependency exists between the two, so no functional impact — noting for completeness.

Nothing here needs changing before merge. The build.shshared_setup.shinstall-secrets.sh behavior change is accurately described in the PR body, and this PR's own CI exercises that path. LGTM. 👍

@mokagio mokagio added this to the 8.18 milestone Jul 30, 2026
@mokagio mokagio added the [Type] Tooling Issues related to tooling: build tools, ruby, scripts, etc. label Jul 30, 2026
@mokagio
mokagio enabled auto-merge July 30, 2026 03:54
Comment on lines -12 to -14
echo "--- :closed_lock_with_key: Installing Secrets"
bundle exec fastlane run configure_apply

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need secrets like GoogleServices-Info.plist to build the prototype build? Is it because Prototype Builds are signed and distributed with the Enterprise Apple account not App Store and we don't want to use the Firebase production credentials for those?

nvm, I see this is handled by shared_setup.sh now.

Comment on lines -11 to -13
echo "--- :closed_lock_with_key: Installing Secrets"
bundle exec fastlane run configure_apply

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need some secrets like GoogleServices-Info.plist to build the tvOS app? Or is the tvOS app not using Firebase / Google Services at all and it's only used by the iOS app and that's why you didn't decrypt them here?

nvm, I see this is handled by shared_setup.sh now.

Comment on lines -16 to -18
echo "--- :closed_lock_with_key: Installing Secrets"
bundle exec fastlane run configure_apply

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need e.g. GoogleServices-Info.plist secret for building the release?

nvm, I see this is handled by shared_setup.sh now.

Comment thread .buildkite/commands/release-upload.sh Outdated

echo "--- :closed_lock_with_key: Installing Secrets"
bundle exec fastlane run configure_apply
"$(dirname "${BASH_SOURCE[0]}")/install-secrets.sh"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What secrets from .configure do we actually need for the upload of the release?
Now that CI secrets like the ASC API keys have been moved to Buildkite secret env vars instead of .configure-files, do we still need any remaining secret for just the upload?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — you're right, and I've dropped it in db32838.

I traced every lane these stages invoke against the four files .configure actually manages (fastlane_match_pwd.txt, pocket_casts_credentials.json, GoogleService-Info.plist, GoogleSignIn.txt). None of them are read here:

Stage Lanes Credentials
release-upload.sh create_release_on_github, symbols_upload, upload_app_store_connect_build_to_testflight[_tvos], notify_release_on_slack GITHUB_TOKEN, SENTRY_AUTH_TOKEN, ASC API key — all Buildkite env
prototype-upload.sh upload_enterprise FIREBASE_APP_DISTRIBUTION_ACCOUNT_KEY — Buildkite env
code-freeze, finalize-release, finalize-hotfix-release, publish-release code_freeze, finalize_release, finalize_hotfix_release, publish_release GITHUB_TOKEN, ASC API key
new-beta-release, new-hotfix-release new_beta_release, new_hotfix_release none — git and GlotPress only

I removed the last two as well even though you didn't flag them, since they have the same property.

Worth noting the removal is a no-op today: Fastfile's before_all calls configure_apply for every lane regardless, so the explicit step was only ever buying a visible Buildkite step. It becomes load-bearing in #4731, where before_all goes away — at which point these stages stop decrypting secrets they never read, rather than silently keeping the dependency.

install-secrets.sh stays, now with shared_setup.sh as its only caller. That's still the seam #4731 swaps for a8c-secrets, so I'd rather not inline it back.


Posted by Claude Code (Opus 5) on behalf of @mokagio with approval.

Comment thread .buildkite/commands/prototype-upload.sh Outdated
Comment thread .buildkite/release-pipelines/code-freeze.yml Outdated
Comment thread .buildkite/release-pipelines/finalize-hotfix-release.yml Outdated
Comment thread .buildkite/release-pipelines/finalize-release.yml Outdated
Comment thread .buildkite/release-pipelines/publish-release.yml Outdated
The four files in `.configure` are build-time inputs: the `match` password,
the Google Service plist, the Google Sign-In key and the credentials JSON.
None of the lanes these eight stages invoke read them — the upload lanes and
the release-management lanes authenticate purely through Buildkite `env`
secrets (`GITHUB_TOKEN`, the ASC API key, `SENTRY_AUTH_TOKEN`,
`FIREBASE_APP_DISTRIBUTION_ACCOUNT_KEY`), and the `new_*` lanes only touch git
and GlotPress.

Olivier raised this on #4857. Decrypting stays harmless today because
`before_all` calls `configure_apply` for every lane regardless, so this only
removes the redundant explicit step. It becomes load-bearing with #4731, where
`before_all` goes away and these stages would otherwise pay for secrets they
never read.

`install-secrets.sh` is left in place with `shared_setup.sh` as its only
caller: it stays the named seam that #4731 swaps for `a8c-secrets`.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude finished @mokagio's task in 2m 59s —— View job


Re-review: secrets dropped from non-build stages

  • Read the new commit (db32838 Drop secrets install from non-build CI stages)
  • Verify the claim that non-build lanes don't read the .configure files
  • Confirm the before_all safety net still applies today
  • Check for stragglers / broken call sites

The new commit directly answers @AliSoftware's review questions ("do these upload / release-management stages actually need .configure secrets?"). I traced it end-to-end and the reasoning holds up. 👍

What the four .configure files are, and who reads them

.configure decrypts exactly four files — fastlane_match_pwd.txt, pocket_casts_credentials.json, GoogleService-Info.plist, GoogleSignIn.txt. Grepping the repo, they're consumed only at Xcode build time:

  • scripts/build-phases/generate-credentials.sh reads SECRETS_PATH (credentials) + FIREBASE_SECRETS_PATH (GoogleService-Info.plist)
  • scripts/build-phases/post-build-update-plist.sh reads GOOGLE_SIGN_IN_SECRETS_PATH
  • the match password is only used by sync_code_signing, which is invoked exclusively from the configure_code_signing_* lanes — and those are only called by the build lanes (build_app_store_connect, build_app_store_connect_tvos, build_enterprise)

None of the upload lanes (upload_enterprise, upload_app_store_connect_build_to_testflight_tvos, create_release_on_github) or the release-management lanes (code_freeze, finalize_release, finalize_hotfix_release, new_beta_release, new_hotfix_release, publish_release) build the app or touch code signing. They authenticate through Buildkite env secrets. So dropping the explicit install from those eight stages is correct. ✅

Safety net is real

fastlane/Fastfile:203-214 runs configure_apply in before_all for every lane, so even if some lane did quietly depend on a .configure file, it would still be decrypted today. That's the belt-and-suspenders the commit message points to — nothing breaks now, and this only becomes load-bearing in #4731 when before_all goes away.

Stragglers

Clean. Within .buildkite/, install-secrets.sh is now reachable only via shared_setup.sh, and the sole configure_apply lives inside install-secrets.sh. Build stages still get secrets (build scripts → shared_setup.shinstall-secrets.sh); the named seam is preserved intact for #4731 to swap for a8c-secrets, exactly as intended.

One thing worth carrying into #4731

Because before_all currently masks any missing-secret mistake, this PR's own CI can't prove the eight non-build stages truly don't need the files — the audit above is what backs that claim, not a red/green signal. When #4731 removes before_all, that's the moment the assumption gets exercised for real, so keep an eye on the first live run of an upload / release-management stage there (esp. anything that turns out to re-sign or re-fetch profiles). The static analysis says they're fine; that's just the spot where it'd surface if it weren't.

Nothing to change here. The commit is well-scoped, the message is accurate, and it resolves the reviewer's questions. LGTM.

@mokagio
mokagio requested a review from AliSoftware August 3, 2026 05:51
@pocketcasts pocketcasts modified the milestones: 8.18, 8.19 Aug 4, 2026
@pocketcasts

Copy link
Copy Markdown
Contributor

Version 8.18 has now entered code-freeze, so the milestone of this PR has been updated to 8.19.

@mokagio
mokagio merged commit 19880fe into trunk Aug 4, 2026
18 of 19 checks passed
@mokagio
mokagio deleted the ainfra-2774-a8c-secrets-prep-in-pocket-casts-ios-dry-ci-scripts branch August 4, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Tooling Issues related to tooling: build tools, ruby, scripts, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants