Skip to content

Adopt a8c-secrets in WordPress iOS - #25772

Draft
mokagio wants to merge 18 commits into
trunkfrom
ainfra-1538-adopt-the-solution-in-wordpress-ios
Draft

Adopt a8c-secrets in WordPress iOS#25772
mokagio wants to merge 18 commits into
trunkfrom
ainfra-1538-adopt-the-solution-in-wordpress-ios

Conversation

@mokagio

@mokagio mokagio commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Description

Migrate the WordPress/Jetpack iOS secrets store off .configure/mobile-secrets onto a8c-secrets (AINFRA-1538), part of the fleet-wide rollout. wpios is outside-only, so this is a decrypt-mechanism swap — no app-code changes.

The branch opens with a tidy (Extract secret install into shared script) that centralised the ~17 configure_apply call sites into .buildkite/commands/install-secrets.sh, so the mechanism swap here touches that one file instead of every pipeline. The build phase now reads the three Swift secrets from ~/.a8c-secrets/<repo-id>/ (decrypted from the committed *.age blobs), and a new configure_secrets fastlane lane wraps a8c-secrets decrypt behind a tool preflight.

Follows the woocommerce-ios (AINFRA-1534) and pocket-casts-ios (AINFRA-1541) adoptions, carrying over the pcios refinements: FastlaneCore::CommandExecutor.which, the is_ci graceful-skip, and a before_all decrypt.

Validated in CI

Build #33110 is green (26/26). The build-for-testing log confirms the full path on real agents: install a8c-secretsa8c-secrets decrypt writes the three secrets to ~/.a8c-secrets/wordpress-ios@github.com@wordpress-mobile/ → the build reads them and compiles. A8C_SECRETS_IDENTITY is already provisioned on the agents (the decrypt succeeded), so there is no outstanding provisioning work.

Gotchas

  • Stacked on ainfra-2643-... — merge that first; this PR targets it, not trunk.
  • The before_all decrypt must skip in CI: install-secrets.sh exports a8c-secrets onto PATH in its own process, which does not reach the later fastlane build_* process, so an unguarded decrypt hard-fails every CI build (pcios probe My Profile Network logic #4732). The is_ci branch handles this — confirmed firing in #33110 with the AINFRA-2681 message, the secrets already on disk from install-secrets.sh.

How to test

bundle exec fastlane configure_secrets locally (with the dev age identity imported) decrypts the three files into ~/.a8c-secrets/wordpress-ios@github.com@wordpress-mobile/. CI exercises the CI path end-to-end (see build #33110).

Copilot AI review requested due to automatic review settings July 12, 2026 05:49
@mokagio
mokagio requested a review from a team as a code owner July 12, 2026 05:49
@mokagio mokagio self-assigned this Jul 12, 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.

Pull request overview

This PR deduplicates the Buildkite “install CI secrets” step by extracting the repeated bundle exec fastlane run configure_apply invocation into a shared script, so the upcoming secrets migration can be done by updating a single file.

Changes:

  • Introduces .buildkite/commands/install-secrets.sh to centralize secrets installation.
  • Updates Buildkite command scripts and release pipelines to call the shared script instead of inlining the Fastlane step.
  • Unifies pipeline log labeling around “Installing Secrets”.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.buildkite/release-pipelines/update-app-store-strings.yml Swaps inline secrets install for shared script call in the pipeline command block.
.buildkite/release-pipelines/publish-release.yml Swaps inline secrets install for shared script call in the pipeline command block.
.buildkite/release-pipelines/new-hotfix.yml Swaps inline secrets install for shared script call in the pipeline command block.
.buildkite/release-pipelines/new-beta-release.yml Swaps inline secrets install for shared script call in the pipeline command block.
.buildkite/release-pipelines/code-freeze.yml Swaps inline secrets install for shared script call in the pipeline command block.
.buildkite/commands/release-build-wordpress.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/release-build-jetpack.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/prototype-build-wordpress.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/prototype-build-jetpack.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/promote-nightly.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/promote-build-to-public.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/install-secrets.sh New shared script that performs the secrets install via Fastlane with a consistent log label.
.buildkite/commands/gather-testflight-candidates.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/finalize-release.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/finalize-hotfix.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/complete-code-freeze.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/build-for-testing.sh Replaces duplicated secrets install lines with shared script invocation.
.buildkite/commands/build-and-upload-testflight.sh Replaces duplicated secrets install lines with shared script invocation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .buildkite/release-pipelines/update-app-store-strings.yml
@wpmobilebot

wpmobilebot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number33615
VersionPR #25772
Bundle IDcom.jetpack.alpha
Commit3064fe3
Installation URL4tavtjrogbl5g
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number33615
VersionPR #25772
Bundle IDorg.wordpress.alpha
Commit3064fe3
Installation URL1cv7johfdvro0
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@mokagio mokagio changed the title Extract CI secret install into a shared script Adopt a8c-secrets in WordPress iOS Jul 12, 2026
Comment thread .a8c-secrets/keys.pub
# dev
age1srcq3hl92ym9jk3ezj5prwhche3w8szc0cssy8t7afrkjmtkxu2qkjsdfn
# ci
age1a7xcr6qzwnzgcxq95sq33p58xdzsmful8w7mp2zktvuy5434yuss9rmv8f

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.

CI public key validated by using age-keygen -y on the private key stored in our CI secrets 👍

Comment thread .buildkite/commands/install-secrets.sh Outdated
Comment on lines +6 to +8
curl -fsSL https://raw.githubusercontent.com/Automattic/a8c-secrets/main/install.sh | bash
# The installer puts the binary in ~/.local/bin, which isn't on the agent's PATH.
export PATH="$HOME/.local/bin:$PATH"

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.

Note that in the future we plan to have a8c-secrets pre-installed and provisionned on all of our CI agents.
We're not there yet so for the iterative migration we might still need this curl and export PATH calls indeed, but that's good to keep in mind and track in some Linear issue to remember to follow-up on this later.

Alternatively, maybe we could start deploying the a8c-secrets (and PATH addition) to our CI agents first, so that PRs that will start to adopt it don't have to do that curl themselves already?

Comment on lines +3 to +5
${HOME}/.configure/wordpress-ios/secrets/WordPress-Secrets.swift
${HOME}/.configure/wordpress-ios/secrets/WordPress-Secrets-Internal.swift
${HOME}/.configure/wordpress-ios/secrets/WordPress-Secrets-Alpha.swift
${HOME}/.configure/wordpress-ios/secrets/Jetpack-Secrets.swift
${HOME}/.configure/wordpress-ios/secrets/Reader-Secrets.swift
${HOME}/.a8c-secrets/wordpress-ios@github.com@wordpress-mobile/WordPress-Secrets.swift
${HOME}/.a8c-secrets/wordpress-ios@github.com@wordpress-mobile/Jetpack-Secrets.swift
${HOME}/.a8c-secrets/wordpress-ios@github.com@wordpress-mobile/Reader-Secrets.swift

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.

Is it intentional that the 3 .swift files that were previously split per configuration (Secrets/Secrets-Internal/Secrets-Alpha) for the WordPress target are now unified into a single one, not making the distinction anymore?

I looked at the current 3 files in our ~/.mobile-secrets (the ones used by configure_apply currently) and their content seem to be distinct for all 3:

  • Mostly the same between Secrets.swift and Secrets-Alpha.swift… except a different SentryDSN
  • Different SentryDSN but also different ClientID for Internal

So in practice this migration to have a single WordPress-Secrets.swift file for all 3 Xcode configurations of the WordPress target changes this behavior. Just wanted to check if this was intentional and had been discussed with the team (e.g. we wouldn't want crashes from Internal and Alpha to be reported in the same Sentry project as prod)

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.

Ok reading the current content of the GenerateCredentials.sh script file I see that maybe the Secrets-Internal and Secrets-Alpha files were in practice never used (never copied by the script in the first place)? Or am I missing something? Or maybe this has been the case for ages and we never cleaned things up?
(I couldn't find a mention of this important gotcha in the PR description, so asking here so we can confirm a common understanding to be sure)

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 no longer need any of the differentiated secrets files – everything can use the same credentials.

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.

We no longer need any of the differentiated secrets files – everything can use the same credentials.

Consolidation to a single secret file felt like a bigger change than what this PR ought to hold. I reverted this to draft and opened #25803 to work on it.

Base automatically changed from ainfra-2643-remove-unused-env-vars-from-wordpress-ios-tooling to trunk July 18, 2026 02:53
@dangermattic

dangermattic commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator
1 Message
📖 This PR is still a Draft: some checks will be skipped.

Generated by 🚫 Danger

@mokagio mokagio added this to the 27.1 milestone Jul 18, 2026
@mokagio
mokagio marked this pull request as draft July 20, 2026 09:57
@mokagio

mokagio commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Converted to draft as explain in #25772 (comment)

@wpmobilebot wpmobilebot modified the milestones: 27.1, 27.2 Jul 21, 2026
@wpmobilebot

Copy link
Copy Markdown
Contributor

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

@mokagio
mokagio force-pushed the ainfra-1538-adopt-the-solution-in-wordpress-ios branch from 4346fee to dd98fd5 Compare August 4, 2026 10:50
@wpmobilebot

wpmobilebot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🤖 Build Failure Analysis

This build has failures. Claude has analyzed them - check the build annotations for details.

mokagio and others added 9 commits August 5, 2026 10:59
Centralize the `configure_apply` invocation, duplicated across 12 command
scripts and 5 release pipelines, into `install-secrets.sh`.
The upcoming move to a8c-secrets then swaps a single call site instead of
seventeen.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Swap the secrets store from `.configure`/`.configure-files` to `.a8c-secrets`,
tracking the encrypted `*.age` blobs alongside the already-committed repo id
and public keys, and updating the `.gitignore` allowlist, `.gitattributes`,
and `CODEOWNERS` to match.

Part of AINFRA-1538.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hint named a `configure_secrets` fastlane lane this repo never gained.
Name the tool's own command and link its repo, so the message stays correct
as the setup steps evolve.

Part of AINFRA-1538.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI installs the tool through the toolkit's `install_a8c-secrets_binary`, added
in plugin 6.2.0, which pins the version and checksums the download.

`rake dependencies` now decrypts only when the tool is present, so external
contributors bootstrap unchanged, and treats a failed decrypt as a warning:
the build phase already falls back to the example secrets and errors on its
own for Release builds.

Part of AINFRA-1538.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The flow existed to get developers a key for the `mobile-secrets` repo.
a8c-secrets authenticates with age identities instead, so nothing in this
repo reads a GPG key any more.

Part of AINFRA-1538.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neither file now names the tool's storage layout: the input list declares the
committed `.age` blob it can actually see, and the script asks
`a8c-secrets which` for the plaintext. A repo id or decrypt root spelled out
here would silently rot the day either changes.

`copy_if_changed` keeps a checkout that only bumps an input's mtime from
forcing a recompile of `Secrets.swift`.

Mirrors Gravatar-SDK-iOS `2fc29684`.

Part of AINFRA-1538.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`a8c-secrets decrypt` produces a single file, `Secrets.swift`, and its only
consumer is the `GenerateCredentials.sh` Xcode build phase.
The release-management and TestFlight-promotion jobs never invoke a compile,
and take their App Store Connect, GitHub, and Slack credentials from Buildkite
environment variables, so the decryption was dead weight in those jobs.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Xcode collapses `..` when it resolves an `.xcfilelist`, so the entry landed in
the processed list rooted at the repo while the script still held the
`WordPress/..` form of the same path.
`ensure_is_in_input_files_list` compares the two as strings, so every target
running `Generate Credentials` failed the phase.

The paths this replaced were rooted at `${HOME}` and had no `..` to collapse,
which is why the check held up until now.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
mokagio and others added 2 commits August 5, 2026 10:59
The `Generate Credentials` build phase resolves `a8c-secrets` off PATH, and the
CI agents don't carry `~/.local/bin` — the installer says so itself on the way
past. Running the script as a child process left the export behind with it, so
the phase would have fallen through to the example secrets and failed every
Release build.

Gravatar-SDK-iOS keeps the install and the export inline in the job script for
the same reason; sourcing gets there without copying them into six callers.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
A comment asking callers to source the script is easy to miss, and the failure
mode is silent: the build phase falls through to the example secrets and only
breaks later, in a Release build.
The guard matches the one in `set-up-git-to-fetch-private-packages.sh` in
`dayone-apple` and `use-bot-for-git.sh` in the CI agent images.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
@mokagio
mokagio force-pushed the ainfra-1538-adopt-the-solution-in-wordpress-ios branch from 34b18e2 to 448b9a2 Compare August 5, 2026 01:05
mokagio and others added 7 commits August 5, 2026 11:09
Matches the shape agreed in Gravatar-SDK-iOS, whose build phase copies without
comparing first: Automattic/Gravatar-SDK-iOS#824
The phase declares its inputs and output, so Xcode already skips it when
nothing changed.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Each call site already echoes which secrets it is applying.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
The AI use `xcodeproj` to make its changes, which smudged some of the
comments and references. I opened the project in Xcode and added then
removed a file to trigger a project file update.
The script installs the `a8c-secrets` binary; naming it after the secrets
invited reading it as the step that puts them on disk.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
The `Decrypt Secrets` aggregate target now decrypts once per build, so doing it
here as well only repeated the work.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
The `Decrypt Secrets` build phase covers local builds too, and it cannot go
stale between a bootstrap and a build the way this task could.

---

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

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants