ci(desktop): sign + notarize macOS, gate release behind environment#726
Conversation
Wires macOS code signing and notarization in the desktop release
pipeline (was previously commented out / shipping unsigned), switches
notarization to the App Store Connect API key, and puts the workflow
behind a GitHub Environment with required reviewers so triggers don't
ship until an approver clicks through.
- `osxSign` reads identity from env; `optionsForFile` sets hardened
runtime + entitlements (required for notarization).
- `osxNotarize` uses the .p8 API-key form instead of Apple ID + password.
- New `prerelease` workflow_dispatch input drives the publisher's
prerelease flag, so canary builds don't auto-ship to installed users.
- `apple-actions/import-codesign-certs@v2` handles the keychain dance
(incl. set-key-partition-list, which is the hang-the-job foot-gun).
- `updateElectronApp({ notifyUser: true })` so users see the native
"Restart to update" prompt rather than a silent swap on next launch.
- `onlyBuiltDependencies` in pnpm-workspace.yaml: pnpm 10 silently
skipped macos-alias / fs-xattr / electron native builds with a
--frozen-lockfile install, which would break the DMG maker in CI.
- Runbook at docs/contributing/desktop-release.md.
Verified end-to-end locally on Apple Silicon: codesign + spctl + xcrun
stapler all green; both .zip and .dmg artifacts produced.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
6 Skipped Deployments
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
WalkthroughAdds environment-driven macOS signing/notarization to Forge, updates the GitHub Actions release workflow (prerelease input, Apple Silicon runner, job-level signing/notarize gating), introduces macOS entitlements, restricts pnpm native builds, enables update notifications, and adds a maintainer release runbook. ChangesmacOS Desktop App Release with Code Signing and Notarization
Sequence Diagram(s)sequenceDiagram
participant GH as GitHub Actions
participant macOS as macOS Runner
participant ASC as App Store Connect
GH->>macOS: start macOS job (matrix includes Apple Silicon)
macOS->>macOS: import certificate (if HAS_SIGNING)
macOS->>macOS: write APPLE_API_KEY file and export (if HAS_NOTARIZE)
macOS->>ASC: submit notarization request with APPLE_API_KEY/key-id/issuer
ASC-->>macOS: notarization result
macOS->>GH: publish signed & notarized macOS artifact
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 @.github/workflows/realease-desktop-app.yml:
- Around line 29-31: The macOS publish is not gated by the signing/notarization
flags (HAS_SIGNING, HAS_NOTARIZE), so add a hard gate to prevent publishing
unless both are set: set the existing env variables HAS_SIGNING and HAS_NOTARIZE
as you have, then add an explicit conditional on the macOS publish job/step (the
job that performs the macOS publish/publish-artifact actions) using GitHub
Actions expression logic (e.g. if: env.HAS_SIGNING == 'true' && env.HAS_NOTARIZE
== 'true') so the publish only runs when both APPLE_CERTIFICATE_P12 and
APPLE_API_KEY_P8 are present.
- Line 63: Replace the mutable tag "apple-actions/import-codesign-certs@v2" with
a pinned full commit SHA reference for that action (e.g.,
"apple-actions/import-codesign-certs@<full-commit-sha>") to ensure immutability;
locate the workflow step using the action name and update the uses field to the
specific commit SHA obtained from the action's repository to hardpin the exact
code version.
In `@docs/contributing/desktop-release.md`:
- Around line 1-5: The new runbook titled "Contributing to Grida | Desktop
release" (desktop-release.md) must be moved out of docs/contributing into one of
the approved actively maintained directories (docs/wg/ or docs/reference/);
relocate the file to the appropriate docs/wg or docs/reference location, update
any internal links or TOC entries that referenced the old path, and remove the
original docs/contributing copy so the repository follows the docs maintenance
policy.
- Around line 61-68: The fenced code block containing the Apple secrets (lines
with APPLE_CERTIFICATE_P12, APPLE_CERTIFICATE_PASSWORD, APPLE_SIGNING_IDENTITY,
APPLE_API_KEY_P8, APPLE_API_KEY_ID, APPLE_API_ISSUER) is missing a language tag;
update the opening triple-backtick to include a tag (e.g., ```text) so the block
becomes ```text ... ``` to satisfy markdown linting and keep the exact secret
lines unchanged.
🪄 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: a55d0c39-f84f-407b-a7d2-567d7f0e8319
📒 Files selected for processing (6)
.github/workflows/realease-desktop-app.ymldesktop/build/entitlements.mac.plistdesktop/forge.config.tsdesktop/pnpm-workspace.yamldesktop/src/main.tsdocs/contributing/desktop-release.md
|
|
||
| - name: Import Apple signing certificate | ||
| if: ${{ matrix.os.name == 'macos-apple-silicon' && env.HAS_SIGNING == 'true' }} | ||
| uses: apple-actions/import-codesign-certs@v2 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if the workflow file exists and inspect the relevant section
find . -name "realease-desktop-app.yml" -o -name "*release*desktop*"Repository: gridaco/grida
Length of output: 102
🏁 Script executed:
# Also check the exact path provided
cat -n ".github/workflows/realease-desktop-app.yml" | sed -n '55,70p'Repository: gridaco/grida
Length of output: 802
🏁 Script executed:
# Get the full file to understand context
wc -l ".github/workflows/realease-desktop-app.yml"Repository: gridaco/grida
Length of output: 104
🌐 Web query:
GitHub Actions pinning commit SHA security best practice
💡 Result:
Pinning GitHub Actions to a full-length commit SHA is a critical security best practice to prevent supply chain attacks, such as those involving hijacked tags or compromised maintainer accounts [1][2][3][4]. Why Pinning to SHA is Required Standard version tags (e.g., @v1, @v4) are mutable labels in Git that can be moved, deleted, or force-pushed to point to different, potentially malicious commits [5][3][6]. If a repository hosting an action is compromised, an attacker can modify a tag to inject malicious code while the workflow continues to use the same tag name [7][5][8]. Pinning to an immutable, full-length commit SHA ensures that the exact code you have audited and validated is executed every time, regardless of subsequent changes to the action's repository [9][2][3][6]. Implementation Best Practices - Use Full-Length SHAs: Always use the complete commit SHA (e.g., actions/checkout@f43a0e5) [2]. - Maintain Human Readability: Include the original version tag as a comment to keep workflows understandable (e.g., uses: actions/checkout@f43a0e5 # v3.6.0) [9][3][10]. - Automate Updates: Use dependency management tools like Dependabot or Renovate to help manage and update these pinned SHAs, balancing security with the need to receive bug fixes and features [1][10][4]. - Adopt "Cooldowns": Even with pinning, consider introducing a delay (e.g., 7–14 days) before adopting new action versions to mitigate exposure to zero-day supply chain attacks [4]. Enforcement Policies GitHub now provides organization and repository-level policies that allow administrators to enforce SHA pinning [11][9][2]. When enabled, any workflow attempting to use a mutable reference (like a branch or tag) will fail, forcing compliance across the environment [11][3][4]. Administrators can also use blocking policies (using the! prefix) to instantly blacklist specific compromised actions or versions organization-wide [11][9][4]. It is important to note that pinning provides defense against repository compromise but does not protect against vulnerabilities present in the specific version of the code you have pinned [6][4]. Always prioritize using verified, official actions and periodically review your dependencies [2][6][4].
Citations:
- 1: Pin all GitHub Actions to immutable SHA commits for supply chain security github/gh-aw#5691
- 2: https://docs.github.com/en/actions/reference/security/secure-use
- 3: https://www.romainlespinasse.dev/posts/github-actions-commit-sha-pinning/
- 4: https://www.wiz.io/blog/github-actions-security-guide
- 5: https://dev.to/kanywst/hacking-github-from-tag-rewrites-to-dangling-commits-where-the-git-protocol-trusts-you-without-2o4h
- 6: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 7: https://gbhackers.com/compromised-github-action/
- 8: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 9: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 10: Pin GitHub Actions to commit SHAs in release workflow github/gh-aw#10021
- 11: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
🌐 Web query:
apple-actions/import-codesign-certs v2 commit SHA
💡 Result:
For apple-actions/import-codesign-certs v2, I could not retrieve the specific underlying commit SHA with the evidence available from the sources I searched. What I found: - The repository exists and is currently documented/used with major tags like v3, but the sources I retrieved don’t include the mapping of v2 -> exact commit SHA (they show the default branch and the current release listing without enough detail captured to identify the v2 tag’s commit) [1][2]. - A third-party/fork page explicitly shows usage with @v2 in its “Usage” section, but it still does not provide the commit SHA for that tag [3]. If you want, tell me whether you mean: 1) the commit SHA that the git tag “v2” points to in Apple-Actions/import-codesign-certs, or 2) the commit SHA that GitHub Actions resolves when you write uses: apple-actions/import-codesign-certs@v2. Either way, I can then do one more targeted lookup on the release/tag page details for v2 to extract the exact SHA.
Citations:
- 1: https://github.com/Apple-Actions/import-codesign-certs
- 2: https://github.com/Apple-Actions/import-codesign-certs/releases
- 3: https://github.com/Simpplr/import-apple-codesign-certs
🏁 Script executed:
# Attempt to fetch the apple-actions/import-codesign-certs repository to find the v2 tag's commit SHA
# We can use git or curl to check the GitHub API
curl -s "https://api.github.com/repos/Apple-Actions/import-codesign-certs/git/refs/tags/v2" | jq '.object.sha' 2>/dev/null || echo "API call may have failed or tag not found"Repository: gridaco/grida
Length of output: 100
🏁 Script executed:
# Alternative: check if git can resolve the tag
# Since we may not have direct internet access, let's verify the recommendation is sound
# by checking if there are any other GitHub Actions in this workflow that ARE pinned to commit SHAs
grep -n "uses:" ".github/workflows/realease-desktop-app.yml" | head -20Repository: gridaco/grida
Length of output: 227
Pin apple-actions/import-codesign-certs to a full commit SHA.
Using @v2 is mutable and weakens workflow supply-chain integrity. Version tags can be rewritten or compromised to inject malicious code.
Suggested fix
- uses: apple-actions/import-codesign-certs@v2
+ uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uses: apple-actions/import-codesign-certs@v2 | |
| uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2 |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 63-63: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for 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.
In @.github/workflows/realease-desktop-app.yml at line 63, Replace the mutable
tag "apple-actions/import-codesign-certs@v2" with a pinned full commit SHA
reference for that action (e.g.,
"apple-actions/import-codesign-certs@<full-commit-sha>") to ensure immutability;
locate the workflow step using the action name and update the uses field to the
specific commit SHA obtained from the action's repository to hardpin the exact
code version.
There was a problem hiding this comment.
Skipping — repo convention is version tags across every workflow (actions/checkout@v4, pnpm/action-setup@v4, dtolnay/rust-toolchain@stable, etc.). Pinning just this one action to a SHA would be inconsistent. Cross-cutting supply-chain hygiene is worth a separate org-wide policy decision.
There was a problem hiding this comment.
@softmarshmallow, fair point — piecemeal SHA pinning on a single action while the rest of the repo uses version tags would be inconsistent and potentially confusing for maintainers. Acknowledging and dropping the suggestion.
Would you like me to open a follow-up issue to track the org-wide supply-chain hygiene / SHA-pinning policy decision so it doesn't get lost?
✏️ Learnings added
Learnt from: softmarshmallow
Repo: gridaco/grida PR: 726
File: .github/workflows/realease-desktop-app.yml:63-63
Timestamp: 2026-05-20T10:38:25.230Z
Learning: In the gridaco/grida repository, the convention across all GitHub Actions workflows is to reference actions using mutable version tags (e.g., `actions/checkoutv4`, `pnpm/action-setupv4`, `dtolnay/rust-toolchainstable`) rather than pinning to full commit SHAs. Do not flag individual unpinned action references as issues; supply-chain hygiene via SHA pinning is an intentional org-wide policy decision deferred to a separate track.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| --- | ||
| format: md | ||
| --- | ||
|
|
||
| # Contributing to Grida | Desktop release |
There was a problem hiding this comment.
Move this runbook to an actively maintained docs path.
This file is added under docs/contributing/, which conflicts with the repository docs maintenance policy.
As per coding guidelines, docs/**/*.md: "the root ./docs directory is the source of truth, and only actively maintain docs/wg/** and docs/reference/** directories".
🤖 Prompt for 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.
In `@docs/contributing/desktop-release.md` around lines 1 - 5, The new runbook
titled "Contributing to Grida | Desktop release" (desktop-release.md) must be
moved out of docs/contributing into one of the approved actively maintained
directories (docs/wg/ or docs/reference/); relocate the file to the appropriate
docs/wg or docs/reference location, update any internal links or TOC entries
that referenced the old path, and remove the original docs/contributing copy so
the repository follows the docs maintenance policy.
There was a problem hiding this comment.
Skipping — docs/contributing/ is an established path with siblings (billing.md, react.md, wpt.md), and this page was explicitly requested under it. The "actively maintained" list in docs/AGENTS.md governs broad SEO/frontmatter churn priorities, not which paths can hold new content.
There was a problem hiding this comment.
@softmarshmallow, thanks for the clarification — that's a good distinction. The docs/AGENTS.md "actively maintained" list governs broad SEO/frontmatter maintenance priorities, not a restriction on which paths may receive new content. docs/contributing/ is an established directory and placing this runbook there alongside its existing siblings is entirely appropriate.
✏️ Learnings added
Learnt from: softmarshmallow
Repo: gridaco/grida PR: 726
File: docs/contributing/desktop-release.md:1-5
Timestamp: 2026-05-20T10:38:25.655Z
Learning: In the gridaco/grida repository, the "actively maintained" list in `docs/AGENTS.md` (covering `docs/wg/**` and `docs/reference/**`) governs SEO/frontmatter churn priorities only — it does not restrict which paths can hold new content. `docs/contributing/` is an established directory with sibling files (`billing.md`, `react.md`, `wpt.md`) and is a valid location for new contributor documentation such as runbooks.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
…t set Address CodeRabbit review on #726. - Expand HAS_SIGNING / HAS_NOTARIZE to check all required secrets (was only checking one per group, would have allowed builds with partial creds). - Add `env.HAS_SIGNING == 'true' && env.HAS_NOTARIZE == 'true'` to the macOS publish step so an env misconfiguration produces a skipped step instead of an unsigned darwin artifact on the Releases page. - Add `text` language tag to the secrets reference fence in the contributing doc.
Summary
.p8) form, replacing Apple ID + app-specific password.releaseGitHub Environment with required reviewers — triggering doesn't ship until an approver clicks through.prereleaseworkflow_dispatch input so canary builds don't auto-ship to installed users viaupdate.electronjs.org.macos-alias,fs-xattr,electron) with--frozen-lockfile— would have broken the DMG maker step in CI exactly where it broke locally.docs/contributing/desktop-release.md.What this preserves
gridaco/gridaGitHub Release.update.electronjs.orgfeed. No custom update server. Plain semver tags (required by the feed'ssemver.valid()check).Out-of-band setup completed (not in repo)
releaseGitHub Environment created withsoftmarshmallowas required reviewer.APPLE_CERTIFICATE_P12,APPLE_CERTIFICATE_PASSWORD,APPLE_SIGNING_IDENTITY,APPLE_API_KEY_P8,APPLE_API_KEY_ID,APPLE_API_ISSUER.Test plan
pnpm typecheckpasses indesktop/pnpm make --arch=arm64builds clean (.zip + .dmg)codesign --verify --deep --strict: valid on disk, satisfies DRflags=0x10000(runtime): hardened runtime appliedspctl -a -t install: accepted, source=Notarized Developer IDxcrun stapler validate: ticket stapledgh secret list --env release --repo gridaco/grida: all 6 presentdesktop/package.jsonversion off0.0.1(current value collides with existing tag)curl https://update.electronjs.org/gridaco/grida/darwin-arm64/0.0.0returns 200 + JSON pointing at the new.zipReviewer notes
A5K7JMXG3Z(personal Developer ID for Woojoo Jung). Future switch to an organization team will require existing users to manually reinstall — Squirrel.Mac rejects updates across Team ID boundaries. See the "Apple credential rotation" callout in the runbook.realease-) left alone to avoid breaking external references; harmless.Summary by CodeRabbit
New Features
Improvements
Documentation