Skip to content

Add tooling and CI integration to sign Mac CLI#77

Open
mokagio wants to merge 14 commits into
mainfrom
ainfra-2466-sign-and-notarize
Open

Add tooling and CI integration to sign Mac CLI#77
mokagio wants to merge 14 commits into
mainfrom
ainfra-2466-sign-and-notarize

Conversation

@mokagio

@mokagio mokagio commented Jun 8, 2026

Copy link
Copy Markdown

See AINFRA-2466.

  • Set up Buildkite CI, where Mac code signing can happen with better security
  • Add required Ruby fastlane tooling to fetch code signing certificate
  • Add dedicated script to codesign and notarize in Buildkite CI
  • Removes GHA macOS build steps. Less infra to maintain.

Generated by Claude (Opus 4.8) on behalf of @mokagio with approval.


Checkout the artifacts in the latest Buidkite build to confirm the code signing and notarization: https://buildkite.com/automattic/bridge-manager/builds/9/list?jid=019eaf13-4942-499d-a1df-f7821a5f5dcf&tab=artifacts

Scaffolds the `set_up_signing` lane that fetches the Developer ID Application
certificate via `fastlane match` (readonly) from Automattic's S3 bucket on the
Buildkite signing agents. This is cert delivery only — the build/sign/notarize
steps land separately. Part of AINFRA-2466.

The `Gemfile` pins `multi_json` because the latest released fastlane (2.235.0)
crashes at startup on Ruby 3.3+ without it; see the comment for the upstream
cause and the fastlane fix that makes it removable.

Generated by the apple:ruby-fastlane-setup Claude Code skill.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mokagio mokagio self-assigned this Jun 8, 2026
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Fastlane-based code signing automation and a Buildkite macOS signing pipeline: pins Fastlane gems and multi_json, configures Bundler, defines Fastlane signing constants and lanes to fetch signing artifacts from S3, and adds CI scripts/pipeline steps to build, sign, notarize, and publish macOS binaries.

Changes

Fastlane Code Signing Automation

Layer / File(s) Summary
Ruby dependencies, bundler config, and ignores
Gemfile, .bundle/config, .gitignore
Pins fastlane (~> 2.235) and fastlane-plugin-wpmreleasetoolkit (~> 14.6); adds multi_json; sets Bundler config (vendor/bundle) and ignores vendor/bundle/.
Fastlane constants, env vars, and plugin wiring
fastlane/Fastfile, fastlane/.gitignore
Enforces Bundler; defines APPLE_TEAM_ID and CODE_SIGNING_STORAGE_OPTIONS; declares CODE_SIGNING_ENV_VARS and ASC_API_KEY_ENV_VARS; requires fastlane/plugin/wpmreleasetoolkit and aliases EnvManager; adds README.md/report.xml ignores.
Initialization and set_up_signing lane
fastlane/Fastfile
Adds before_all hook to run setup_ci and initialize EnvManager with bridge-manager.env; implements :set_up_signing lane that validates env vars (App Store Connect keys conditional) and calls sync_code_signing to retrieve a macOS Developer ID certificate from S3 storage.

Buildkite macOS signing pipeline

Layer / File(s) Summary
Pipeline shared vars
.buildkite/shared-pipeline-vars
Exports IMAGE_ID='xcode-26.3', pins CI_TOOLKIT_PLUGIN_VERSION, and constructs CI_TOOLKIT_PLUGIN.
Buildkite pipeline step
.buildkite/pipeline.yml
Adds a mac agent pipeline step that runs the signing script, attaches the CI toolkit plugin, uploads signed artifacts, and reports GitHub status context.
Sign and notarize script
.buildkite/commands/sign-macos-binaries.sh
Builds darwin amd64 and arm64 binaries, ensures Go toolchain, runs install_gems and fastlane set_up_signing, signs and notarizes artifacts, and generates SHA-256 checksums.
sequenceDiagram
  participant Developer
  participant Buildkite
  participant Script
  participant Fastlane
  participant EnvManager
  participant S3
  Developer->>Buildkite: push / trigger pipeline (mac)
  Buildkite->>Script: run .buildkite/commands/sign-macos-binaries.sh
  Script->>Fastlane: install_gems && bundle exec fastlane set_up_signing
  Fastlane->>EnvManager: initialize(env_file_name: 'bridge-manager.env')
  Fastlane->>EnvManager: validate CODE_SIGNING_ENV_VARS / ASC_API_KEY_ENV_VARS (conditional)
  Fastlane->>S3: sync_code_signing(request: macOS Developer ID, team: PZYM8XX95Q, storage options)
  S3-->>Fastlane: certificate & provisioning profiles
  Script->>Script: build binaries (amd64, arm64), sign & notarize
  Script-->>Buildkite: upload artifacts (bbctl-macos-*, sha256sums.txt)
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately describes the main objective of the PR—adding tooling and CI integration for macOS binary signing—which aligns with the core changes across Gemfile dependencies, Fastfile configuration, and Buildkite pipeline setup.
Description check ✅ Passed The PR description directly addresses the changeset by referencing a Linear issue, explaining the purpose of adding Buildkite CI setup with code signing, fastlane tooling, and dedicated scripts while removing GHA infrastructure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ainfra-2466-sign-and-notarize

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread fastlane/Fastfile Outdated
mokagio and others added 4 commits June 8, 2026 13:41
Add `.bundle/config` (mirroring platform-imessage): `BUNDLE_PATH: vendor/bundle`
keeps gems in the repo and out of the agent's system Ruby, and
`BUNDLE_FORCE_RUBY_PLATFORM` pins the pure-Ruby gem variants so the lock is
portable between a developer's Mac and the CI agents.
Regenerate `Gemfile.lock` from a fresh resolve so its `PLATFORMS` is `ruby` only,
and gitignore the vendored `vendor/bundle/`.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin `~> 2.235` (fastlane) and `~> 14.6` (wpmreleasetoolkit), the current latest
on RubyGems, instead of stale floors. Lock regenerated from a fresh resolve.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds the macOS binaries with Go, fetches the Developer ID cert via
`fastlane set_up_signing`, and signs + notarizes them with the a8c-ci-toolkit
`sign_and_notarize` command (no entitlements — plain CLI under hardened
runtime). Signed `bbctl-macos-{amd64,arm64}` + `sha256sums.txt` are uploaded as
build artifacts; runs on every build, mirroring the platform-imessage pipeline.

Publishing the signed binaries to GitHub Releases is intentionally out of scope
here: `gh` auth on the mac agents is unverified, and whether release publishing
moves off GitHub Actions is still open. The toolkit pin is temporary (a branch)
until the command ships in a tagged release.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.buildkite/shared-pipeline-vars:
- Around line 11-12: CI_TOOLKIT_PLUGIN_VERSION is set to a mutable branch and
used to build CI_TOOLKIT_PLUGIN, which makes CI behavior changeable; update
CI_TOOLKIT_PLUGIN_VERSION from the branch name (mokagio/macos-sign-and-notarize)
to an immutable ref (a released tag or specific commit SHA) and ensure
CI_TOOLKIT_PLUGIN is constructed from that pinned value so
.buildkite/pipeline.yml always uses a fixed plugin revision.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c385ee50-6f86-43b4-b969-46b5baf107db

📥 Commits

Reviewing files that changed from the base of the PR and between b93c694 and 3ecf2b3.

📒 Files selected for processing (3)
  • .buildkite/commands/sign-macos-binaries.sh
  • .buildkite/pipeline.yml
  • .buildkite/shared-pipeline-vars
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-docker (amd64)
  • GitHub Check: lint
  • GitHub Check: build-docker (amd64)
  • GitHub Check: build-docker (arm64)
  • GitHub Check: lint
🔇 Additional comments (2)
.buildkite/pipeline.yml (1)

4-21: LGTM!

.buildkite/commands/sign-macos-binaries.sh (1)

3-30: LGTM!

Comment thread .buildkite/shared-pipeline-vars Outdated
mokagio and others added 2 commits June 8, 2026 15:03
Track the latest macOS signing image (26.5) via `.xcode-version` instead of
hardcoding it, matching the platform-imessage convention. bbctl is a Go CLI
with no Xcode dependency — the image only supplies codesign/notarytool and the
WWDR intermediates — so it tracks latest rather than pinning a version.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Xcode 26.5 image's clang cannot compile `nokogiri` 1.19.3 from source
(`gumbo.c: 'nokogiri_gumbo.h' file not found`), which fails `bundle install` in
the signing job. `fastlane` pulls `nokogiri` in transitively via
`wpmreleasetoolkit`, and `.bundle/config` forces the Ruby (source) platform, so
the precompiled darwin gem is never used.

Pin to 26.3, which compiles it, and record the reason as a note in
`.xcode-version`. The `shared-pipeline-vars` parser now skips comment and blank
lines so the note doesn't leak into `IMAGE_ID`.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread .buildkite/commands/sign-macos-binaries.sh Outdated
Comment thread .buildkite/commands/sign-macos-binaries.sh Outdated
Comment thread .buildkite/commands/sign-macos-binaries.sh Outdated
Comment thread .buildkite/shared-pipeline-vars Outdated
mokagio and others added 3 commits June 8, 2026 15:26
Co-authored-by: Gio Lodi <giovanni.lodi42@gmail.com>
`mokagio/macos-sign-and-notarize` is a moving ref; pinning the current tip
(`8a67edf`, "Log applied cert/team after signing") makes the signing build
reproducible until the command ships in a released tag.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mokagio mokagio changed the title Add fastlane match scaffold for code signing Add tooling and CI integration to sign Mac CLI Jun 8, 2026
Buildkite now compiles, signs, and notarizes the macOS binaries on a real Mac
and publishes them under the same names. The unsigned GitHub Actions copies were
redundant and unused, so stop building and uploading them.

---

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mokagio mokagio marked this pull request as ready for review June 8, 2026 05:51
Copilot AI review requested due to automatic review settings June 8, 2026 05:51
@indent

indent Bot commented Jun 8, 2026

Copy link
Copy Markdown
PR Summary

Adds a Buildkite pipeline that builds, signs (Developer ID via fastlane match from Automattic's S3), and notarizes macOS bbctl binaries on a real Mac agent so users get trusted artifacts. The pre-existing unsigned macOS uploads from GitHub Actions are removed; publishing the signed binaries to GitHub Releases is intentionally deferred.

  • Add .buildkite/pipeline.yml, .buildkite/commands/sign-macos-binaries.sh, and .buildkite/shared-pipeline-vars (the latter parses .xcode-version into IMAGE_ID and pins the a8c-ci-toolkit plugin to a SHA on mokagio/macos-sign-and-notarize).
  • Pin .xcode-version to 26.3 because Xcode 26.5's clang can't compile nokogiri 1.19.3 from source (required since .bundle/config sets BUNDLE_FORCE_RUBY_PLATFORM=true and the lockfile uses PLATFORMS: ruby).
  • Scaffold Ruby tooling: Gemfile (fastlane ~> 2.235, fastlane-plugin-wpmreleasetoolkit ~> 14.6, plus a temporary multi_json workaround for the 2.235.0/Ruby 3.3+ startup crash), Gemfile.lock, .bundle/config, and gitignore vendor/bundle/.
  • Add fastlane/Fastfile with a set_up_signing lane that runs setup_ci, initialises EnvManager, and sync_code_signing (readonly Developer ID from a8c-fastlane-match S3 bucket, team PZYM8XX95Q).
  • Remove macOS targets from ci-build-all.sh and the corresponding artifact uploads from .github/workflows/go.yaml.

Issues

2 potential issues found:

  • .buildkite/pipeline.yml depends on shared-pipeline-vars being sourced before buildkite-agent pipeline upload so $IMAGE_ID and $CI_TOOLKIT_PLUGIN get interpolated — but nothing in-repo does that, it has to be configured in the Buildkite dashboard bootstrap. If that step isn't set up the same way as platform-imessage, both values render as empty: the plugin list becomes [""] and IMAGE_ID is blank, so install_gems/sign_and_notarize won't exist and the job fails with command-not-found errors. Worth a comment in pipeline.yml (or a guard in the shell script) calling this out so the next reader understands the env block isn't self-contained. → Autofix
  • README still advertises GitHub Actions (nightly.link) and GitHub Releases as macOS download paths, but this PR removes the unsigned macOS uploads from GH Actions and the new signed/notarized Buildkite binaries aren't published anywhere user-visible yet — macOS users following README step 1's alternative download path will get nothing, and if the beeper/tap/bbctl Homebrew tap pulls from Releases it likely breaks too. Either land a follow-up that publishes the Buildkite artifacts to Releases (or another public location) alongside this change, or update README to note signed macOS builds aren't externally available yet. → Autofix
1 issue already resolved
  • Typo in comment: sing_and_notarize should be sign_and_notarize. (fixed by commit 6135daa)

CI Checks

All CI checks passed on commit a477420.


⚡ Autofix All Issues

Copilot AI left a comment

Copy link
Copy Markdown

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 introduces a Ruby/fastlane-based macOS code-signing and notarization workflow executed in Buildkite, and removes the macOS build artifact path from GitHub Actions to reduce CI surface area there.

Changes:

  • Add fastlane tooling (Gemfile/Gemfile.lock + Fastfile) to fetch Developer ID signing material in CI.
  • Add a Buildkite pipeline + command script to build, sign, notarize, and publish macOS CLI artifacts with checksums.
  • Remove macOS binary builds/uploads from the existing GitHub Actions workflow and build-all script.

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Gemfile.lock Adds locked Ruby dependencies needed for fastlane/code-signing tooling.
Gemfile Declares fastlane + plugin dependencies and documents the multi_json workaround.
fastlane/Fastfile Defines the set_up_signing lane to sync Developer ID signing certs in CI.
fastlane/.gitignore Ignores fastlane-generated outputs within the fastlane/ directory.
ci-build-all.sh Removes macOS builds from the GitHub Actions “build all” script.
.xcode-version Pins the Buildkite macOS/Xcode image version used for building/signing.
.gitignore Ignores Bundler’s vendor/bundle/ directory used for CI installs.
.github/workflows/go.yaml Removes upload steps for macOS artifacts in GitHub Actions.
.bundle/config Configures Bundler to install into vendor/bundle and to use the Ruby platform.
.buildkite/shared-pipeline-vars Exports image/plugin variables used when uploading the Buildkite pipeline.
.buildkite/pipeline.yml Adds Buildkite step to build + sign + notarize macOS binaries and upload artifacts.
.buildkite/commands/sign-macos-binaries.sh Implements the macOS build/sign/notarize command sequence for Buildkite.

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

Comment on lines +8 to +12
# TEMPORARY: pinned to a commit on the `mokagio/macos-sign-and-notarize` branch
# adding the macOS `sign_and_notarize` command. Revert to a released tag
# (>= the version that ships it) before merging.
CI_TOOLKIT_PLUGIN_VERSION='8a67edfc19a7fd04de7033d790bd1a7a4f0f8b4d'
export CI_TOOLKIT_PLUGIN="automattic/a8c-ci-toolkit#$CI_TOOLKIT_PLUGIN_VERSION"
Comment thread .buildkite/commands/sign-macos-binaries.sh
Comment thread Gemfile Outdated
Comment on lines +7 to +12
# fastlane <= 2.235.0 crashes at startup on Ruby 3.3+ with "multi_json is not
# part of the bundle": Google stopped pulling multi_json transitively
# (googleapis/google-api-ruby-client#26611) and fastlane eagerly loads its
# Google Play actions, which require it through representable. fastlane re-added
# it as a direct dependency for 2.236.0 (fastlane/fastlane#30062) — drop this
# line once the lock is on fastlane >= 2.236.0.
Comment thread .xcode-version Outdated
Comment thread .buildkite/commands/sign-macos-binaries.sh Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@mokagio mokagio enabled auto-merge June 9, 2026 00:48
@mokagio mokagio disabled auto-merge June 9, 2026 00:48
@mokagio mokagio enabled auto-merge June 9, 2026 00:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants