Skip to content

Add macos_verify_code_signing action#757

Open
mokagio wants to merge 7 commits into
trunkfrom
mokagio/ainfra-2709-macos-verify-signing
Open

Add macos_verify_code_signing action#757
mokagio wants to merge 7 commits into
trunkfrom
mokagio/ainfra-2709-macos-verify-signing

Conversation

@mokagio

@mokagio mokagio commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What does it do?

Extracts the verify_code_signing lane written for the WordPress Contributor Toolkit (WordPress/experimental-wp-dev-env#22) into a reusable action, so every app we build for macOS can assert its artifacts are signed and notarized. electron-builder, in particular, only warns when it can't find a valid signing identity — it skips signing and produces an artifact that looks fine until a user tries to launch it.

Two deliberate additions over the original lane:

  • xcrun stapler validate, so a missing notarization ticket fails the job (thanks @iangmaia for the suggestion).
  • verify_notarization (default true), so callers can verify at a point where the app is signed but not yet notarized — e.g. from an electron-builder afterSign hook.

See https://linear.app/a8c/issue/AINFRA-2709.

How to test

bundle exec rspec spec/macos_verify_code_signing_spec.rb.

I also smoke-ran it against a real notarized app, which exercises the actual codesign/spctl/stapler invocations the specs mock:

bundle exec fastlane run macos_verify_code_signing app_path:/Applications/Claude.app \
  "expected_authority:Developer ID Application: Anthropic PBC (Q6L2SF6YDW)"

It passes, and fails as expected when given a different expected_authority.

Checklist before requesting a review

  • Run bundle exec rubocop to test for code style violations and recommendations.
  • Add Unit Tests (aka specs/*_spec.rb) if applicable.
  • Run bundle exec rspec to run the whole test suite and ensure all your tests pass.
  • Make sure you added an entry in the CHANGELOG.md file to describe your changes under the appropriate existing ### subsection of the existing ## Trunk section.
  • If applicable, add an entry in the MIGRATION.md file to describe how the changes will affect the migration from the previous major version and what the clients will need to change and consider.

Extracted from the `verify_code_signing` lane in the WordPress Contributor
Toolkit (WordPress/experimental-wp-dev-env#22), so that the other apps we
build for macOS can assert their artifacts are signed and notarized instead
of shipping a silently unsigned build.

`electron-builder` only warns when it can't find a valid signing identity —
it skips signing and produces an artifact that looks fine until users try to
launch it. That's what motivated the original lane.

On top of the original lane's checks, this also runs `xcrun stapler validate`
so a missing notarization ticket fails the job, and makes the notarization
checks opt-out for callers that verify at a point where the app is signed
but not yet notarized, such as an `electron-builder` `afterSign` hook.

Part of AINFRA-2709.

---

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

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
@mokagio mokagio self-assigned this Jul 19, 2026
---

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

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
Disk images can't reuse the app bundle's checks. Measured against two real
`.dmg`s, including one of our own electron-builder artifacts: neither is
codesigned — electron-builder signs only the app inside — and Gatekeeper
rejects an unsigned image with `no usable signature` even when it does carry
a notarization ticket. So `codesign --verify` and `spctl` are the wrong
checks for our images; the stapled ticket is the only one that means
anything.

Hence dispatch on the extension rather than a separate action per artifact
type: callers pass paths and get the checks that apply. An unsigned image
warns and skips its signature checks, while a *broken* signature still
fails — a distinction worth keeping, since the first is expected and the
second never is.

`app_path` becomes `artifact_path` now that it takes more than app bundles.

---

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

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
Comment thread lib/fastlane/plugin/wpmreleasetoolkit/actions/macos/macos_verify_code_signing.rb Outdated
Comment thread lib/fastlane/plugin/wpmreleasetoolkit/actions/macos/macos_verify_code_signing.rb Outdated
Comment thread lib/fastlane/plugin/wpmreleasetoolkit/actions/macos/macos_verify_code_signing.rb Outdated
The check claimed to accept "an Array of Strings" but only checked for an
Array, so `[42]` reached `File.exist?` and raised a bare TypeError instead of
the message the check exists to produce.

---

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

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
Comment thread lib/fastlane/plugin/wpmreleasetoolkit/actions/macos/macos_verify_code_signing.rb Outdated
mokagio and others added 3 commits July 20, 2026 10:08
Co-authored-by: Gio Lodi <giovanni.lodi42@gmail.com>
`sh` logs a non-zero exit in red whether or not the caller handles it, so the
unsigned-image path painted a passing job's log with what looks like a
failure. The explanation came after, by which point a reader skimming for red
has already stopped.

---

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

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
@mokagio
mokagio marked this pull request as ready for review July 20, 2026 10:02
@mokagio
mokagio requested a review from a team as a code owner July 20, 2026 10:02
Copilot AI review requested due to automatic review settings July 20, 2026 10:02

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 introduces a reusable Fastlane action to verify that macOS build artifacts are properly code signed and (optionally) notarized, addressing cases where tooling like electron-builder may silently skip signing and produce unusable artifacts.

Changes:

  • Added macos_verify_code_signing Fastlane action to verify signature validity, optional signing authority, Gatekeeper acceptance, and stapled notarization tickets for .app and .dmg artifacts.
  • Added comprehensive RSpec coverage for the action’s behavior across signed/unsigned images, authority matching, and failure modes.
  • Documented the new action in CHANGELOG.md under ## Trunk.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
lib/fastlane/plugin/wpmreleasetoolkit/actions/macos/macos_verify_code_signing.rb New action implementing macOS artifact signature / Gatekeeper / stapler validation logic.
spec/macos_verify_code_signing_spec.rb New unit tests validating the action’s command invocations and error handling.
CHANGELOG.md Adds a ## Trunk “New Features” entry for the new action.

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

Comment on lines +1 to +5
# frozen_string_literal: true

module Fastlane
module Actions
class MacosVerifyCodeSigningAction < Action
Comment on lines +44 to +46
# Said up front because `sh` logs a non-zero exit in red regardless of it being handled,
# which reads as a broken build in the CI log of an otherwise passing job.
UI.message("Checking whether #{path} is signed. Disk images usually aren't, so a `codesign` failure below is expected and tolerated.")
Comment on lines +95 to +97
- `.app` — the signature is valid and satisfies its designated requirement, Gatekeeper accepts
the bundle for execution, and a notarization ticket is stapled to it.
- `.dmg` — a notarization ticket is stapled to the image.
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.

2 participants