Skip to content

ci(desktop): sign + notarize macOS, gate release behind environment#726

Merged
softmarshmallow merged 2 commits into
mainfrom
feature/hopeful-brattain-e0bac6
May 20, 2026
Merged

ci(desktop): sign + notarize macOS, gate release behind environment#726
softmarshmallow merged 2 commits into
mainfrom
feature/hopeful-brattain-e0bac6

Conversation

@softmarshmallow
Copy link
Copy Markdown
Member

@softmarshmallow softmarshmallow commented May 20, 2026

Summary

  • Wires macOS code signing and notarization in the desktop release pipeline (was commented out / shipping unsigned).
  • Switches notarization to the App Store Connect API key (.p8) form, replacing Apple ID + app-specific password.
  • Puts the release workflow behind a new release GitHub Environment with required reviewers — triggering doesn't ship until an approver clicks through.
  • Adds a prerelease workflow_dispatch input so canary builds don't auto-ship to installed users via update.electronjs.org.
  • Fixes pnpm 10 silently skipping native module builds (macos-alias, fs-xattr, electron) with --frozen-lockfile — would have broken the DMG maker step in CI exactly where it broke locally.
  • Adds runbook at docs/contributing/desktop-release.md.

What this preserves

  • Same-repo unified release (opencode-shaped). No separate releases repo; all desktop artifacts attach to the same gridaco/grida GitHub Release.
  • Free update.electronjs.org feed. No custom update server. Plain semver tags (required by the feed's semver.valid() check).
  • electron-forge. No migration to electron-builder.

Out-of-band setup completed (not in repo)

  • release GitHub Environment created with softmarshmallow as required reviewer.
  • 6 secrets uploaded to the environment: APPLE_CERTIFICATE_P12, APPLE_CERTIFICATE_PASSWORD, APPLE_SIGNING_IDENTITY, APPLE_API_KEY_P8, APPLE_API_KEY_ID, APPLE_API_ISSUER.

Test plan

  • pnpm typecheck passes in desktop/
  • Local pnpm make --arch=arm64 builds clean (.zip + .dmg)
  • codesign --verify --deep --strict: valid on disk, satisfies DR
  • flags=0x10000(runtime): hardened runtime applied
  • spctl -a -t install: accepted, source=Notarized Developer ID
  • xcrun stapler validate: ticket stapled
  • gh secret list --env release --repo gridaco/grida: all 6 present
  • First dispatched workflow run after merge — requires bumping desktop/package.json version off 0.0.1 (current value collides with existing tag)
  • Post-release verify: curl https://update.electronjs.org/gridaco/grida/darwin-arm64/0.0.0 returns 200 + JSON pointing at the new .zip

Reviewer notes

  • The Team ID baked into the first signed build is 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.
  • Workflow filename typo (realease-) left alone to avoid breaking external references; harmless.

Summary by CodeRabbit

  • New Features

    • Added workflow control for prerelease desktop app builds and added Apple Silicon publishing support.
  • Improvements

    • Conditional macOS signing/notarization flow for stronger, more flexible release signing.
    • Updated macOS entitlements to support required execution behaviors.
    • Limited native dependency build set for more reliable builds.
    • Enabled user notifications for app updates.
  • Documentation

    • Added a detailed desktop release runbook and troubleshooting guide.

Review Change Stack

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.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment May 20, 2026 10:40am
6 Skipped Deployments
Project Deployment Actions Updated (UTC)
backgrounds Ignored Ignored Preview May 20, 2026 10:40am
blog Ignored Ignored Preview May 20, 2026 10:40am
code Ignored Ignored May 20, 2026 10:40am
grida Ignored Ignored Preview May 20, 2026 10:40am
legacy Ignored Ignored May 20, 2026 10:40am
viewer Ignored Ignored Preview May 20, 2026 10:40am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0f2984de-58de-4130-a6c9-0ed5dae09387

📥 Commits

Reviewing files that changed from the base of the PR and between 5307489 and 5ac796c.

📒 Files selected for processing (2)
  • .github/workflows/realease-desktop-app.yml
  • docs/contributing/desktop-release.md
✅ Files skipped from review due to trivial changes (1)
  • docs/contributing/desktop-release.md

Walkthrough

Adds 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.

Changes

macOS Desktop App Release with Code Signing and Notarization

Layer / File(s) Summary
macOS Signing and Entitlements Foundation
desktop/build/entitlements.mac.plist, desktop/forge.config.ts
Adds macOS security entitlements (JIT, unsigned memory, disable library validation) and refactors Forge to derive osxSign/osxNotarize from environment variables; prerelease for the GitHub publisher is now driven by process.env.PRERELEASE === "true".
Release Workflow Orchestration
.github/workflows/realease-desktop-app.yml
Workflow adds a workflow_dispatch prerelease input, expands matrix with an Apple Silicon runner, computes job-level HAS_SIGNING/HAS_NOTARIZE from secrets, conditionally imports the signing certificate and stages the App Store Connect API key on macOS, and threads PRERELEASE into Windows/Linux publish steps.
Update Notifications and Dependency Constraints
desktop/pnpm-workspace.yaml, desktop/src/main.ts
Adds onlyBuiltDependencies allowlist (electron, electron-winstaller, fs-xattr, macos-alias) to the pnpm workspace and changes updateElectronApp invocation to notifyUser: true.
Release Process Documentation and Runbook
docs/contributing/desktop-release.md
New maintainer guide documenting stable vs prerelease flows, required release secrets, immutable constraints, troubleshooting, local signed+notarized test steps, and Apple credential rotation guidance.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • gridaco/grida#666: Modifies macOS signing/notarization environment variable handling in desktop/forge.config.ts, related to this PR's Forge env-driven notarization changes.
  • gridaco/grida#330: Related to desktop/pnpm-workspace.yaml and workspace native dependency handling; both PRs touch desktop workspace configuration.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: enabling macOS code signing and notarization, and gating the release workflow behind GitHub Environment approval.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/hopeful-brattain-e0bac6

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.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between d36528b and 5307489.

📒 Files selected for processing (6)
  • .github/workflows/realease-desktop-app.yml
  • desktop/build/entitlements.mac.plist
  • desktop/forge.config.ts
  • desktop/pnpm-workspace.yaml
  • desktop/src/main.ts
  • docs/contributing/desktop-release.md

Comment thread .github/workflows/realease-desktop-app.yml Outdated

- name: Import Apple signing certificate
if: ${{ matrix.os.name == 'macos-apple-silicon' && env.HAS_SIGNING == 'true' }}
uses: apple-actions/import-codesign-certs@v2
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 20, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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:


🌐 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:


🏁 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 -20

Repository: 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.

Suggested change
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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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.

Comment on lines +1 to +5
---
format: md
---

# Contributing to Grida | Desktop release
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 20, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@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.

Comment thread docs/contributing/desktop-release.md Outdated
…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.
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.

1 participant