feat(desktop): add a sandboxed Mac App Store build variant - #1581
Conversation
Adds a `mas` build of GeoLibre Desktop that satisfies App Store review rules (App Sandbox, guideline 2.5.2), alongside the existing Developer ID notarized builds: - `mas` cargo feature compiles out everything that downloads or spawns external code: the Python sidecar, Jupyter server, martin tile server, the uv bootstrap, and external plugin installation. Invoke-compatible stubs keep the command registry unchanged; `compile_error!` forbids combining with `native-duckdb` (runtime extension loading). - `GEOLIBRE_MAS_BUILD=1` frontend flag (Vite define, DCE-friendly) hides the sidecar-only UI (AI Segmentation, PostgreSQL, File Geodatabase, plugin marketplace) and routes processing dialogs to their client-side engines (Whitebox WASM, browser conversions, client raster tools, Turf/Pyodide vector tools, CereusDB SQL). The Notebook panel keeps working via JupyterLite, which stays in the MAS bundle. - App Sandbox entitlements template + render script, tauri.mas.conf.json overlay (Apple Distribution signing, embedded provisioning profile, no sidecar resources), and `npm run tauri:build:mas`. - Shapefile companions: the sandbox denies reading unselected siblings of a picked .shp, so the MAS build matches .dbf/.prj/.shx/.cpg out of the same multi-selection instead. - mas-store.yml workflow (manual dispatch, mirrors msix-store.yml): builds the universal sandboxed app, verifies the sandbox entitlement, and uploads a signed .pkg artifact for Transporter submission. - docs/mac-app-store.md documents the variant, its limitations, the required secrets, and the submission steps. Also fixes cargo check --features native-duckdb, broken by the locked duckdb crate making Value non-exhaustive.
📝 WalkthroughWalkthroughThis PR adds a sandboxed Mac App Store build for GeoLibre Desktop. It adds signing and packaging automation, compiles out process-dependent features, hides unsupported UI, uses browser-based fallbacks, handles sandboxed shapefile access, and documents the build and submission process. ChangesMac App Store build variant
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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 |
🔍 Cloudflare PR preview
|
🔍 GitHub Pages PR preview
|
|
Both inline comments posted. Now finalizing the summary. Code reviewBugs
Security
Performance
Quality
CLAUDE.md
No high-confidence bugs or security issues were found. The two items above were posted as inline comments; everything else checked out against both the diff and the surrounding source. |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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/mas-store.yml:
- Around line 43-44: Update the “Checkout repository” actions/checkout step to
disable credential persistence by setting persist-credentials to false, before
the subsequent npm ci step runs.
- Around line 46-62: Update the “Verify signing secrets are configured” step to
include APPLE_MAS_CERTIFICATE_PASSWORD in its env mapping and in the for-loop
validation list, ensuring all six documented secrets are checked before the
build proceeds.
- Around line 71-72: Update the “Install Rust stable” workflow step to pin
dtolnay/rust-toolchain to a specific full commit SHA instead of the moving
stable reference, preserving the stable-channel intent and optional comment.
In `@apps/geolibre-desktop/src-tauri/mas/Entitlements.mas.plist.template`:
- Around line 21-22: Update the persisted-scope integration associated with the
files.user-selected.read-write entitlement to persist security-scoped bookmarks
for selected files and folders, resolve each bookmark on application launch, and
call startAccessingSecurityScopedResource before access. If native bookmark
handling is not available, replace the persisted access flow with paths that do
not require permissions to survive relaunches.
In `@apps/geolibre-desktop/src/components/layout/TopToolbar.tsx`:
- Line 97: Use IS_MAS_BUILD in the ADD_DATA_KIND_COMMANDS registration to omit
postgres and gdb entries for MAS builds, and update the OPEN_ADD_DATA_EVENT
handler to reject those kinds before calling setAddDataKind(). Preserve all
existing sources and event behavior outside MAS builds.
In `@apps/geolibre-desktop/src/components/processing/SegmentationDialog.tsx`:
- Around line 82-89: Update the SegmentationDialog form controls to use the
unavailable status for IS_MAS_BUILD, preventing image selection and edits while
the MAS unavailable message is shown. Preserve the existing enabled behavior for
available builds and the web-unavailable path.
In `@scripts/render-mas-entitlements.sh`:
- Around line 18-20: Add an explicit gettext/envsubst installation step before
the rendering command that invokes envsubst, using brew install gettext --HEAD
--no-quarantine followed by brew link --force gettext. Update the related local
usage documentation to state the same prerequisite, while preserving the
existing restricted APPLE_TEAM_ID substitution behavior.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 48e5b301-23a8-4e3b-b0d8-02104e7ee0ed
📒 Files selected for processing (39)
.github/workflows/mas-store.yml.gitignoreapps/geolibre-desktop/package.jsonapps/geolibre-desktop/src-tauri/Cargo.tomlapps/geolibre-desktop/src-tauri/Info.plistapps/geolibre-desktop/src-tauri/mas/Entitlements.mas.plist.templateapps/geolibre-desktop/src-tauri/src/lib.rsapps/geolibre-desktop/src-tauri/src/native_duckdb.rsapps/geolibre-desktop/src-tauri/tauri.mas.conf.jsonapps/geolibre-desktop/src/components/layout/SettingsDialog.tsxapps/geolibre-desktop/src/components/layout/TopToolbar.tsxapps/geolibre-desktop/src/components/layout/add-data/sources/GdbSource.tsxapps/geolibre-desktop/src/components/layout/add-data/sources/PostgresSource.tsxapps/geolibre-desktop/src/components/layout/toolbar/AddDataMenu.tsxapps/geolibre-desktop/src/components/layout/toolbar/ProcessingMenu.tsxapps/geolibre-desktop/src/components/panels/LayerPanel.tsxapps/geolibre-desktop/src/components/panels/NotebookPanel.tsxapps/geolibre-desktop/src/components/processing/ConversionDialog.tsxapps/geolibre-desktop/src/components/processing/ProcessingDialog.tsxapps/geolibre-desktop/src/components/processing/RasterToolsDialog.tsxapps/geolibre-desktop/src/components/processing/SegmentationDialog.tsxapps/geolibre-desktop/src/components/processing/VectorToolsDialog.tsxapps/geolibre-desktop/src/hooks/useBrowserTree.tsapps/geolibre-desktop/src/i18n/locales/en.jsonapps/geolibre-desktop/src/lib/build-flags.tsapps/geolibre-desktop/src/lib/jupyter.tsapps/geolibre-desktop/src/lib/martin.tsapps/geolibre-desktop/src/lib/mas-build.tsapps/geolibre-desktop/src/lib/sedona-workspace.tsapps/geolibre-desktop/src/lib/sidecar.tsapps/geolibre-desktop/src/lib/tauri-io.tsapps/geolibre-desktop/src/vite-env.d.tsapps/geolibre-desktop/vite.config.tsdocs/mac-app-store.mdmkdocs.ymlpackage.jsonscripts/render-mas-entitlements.shscripts/tauri-build.mjstests/mas-build.test.ts
- Widen the MAS shapefile companion set to the Rust command's full 16-extension list and make companion extensions selectable in the MAS file dialog (they were excluded by the vector filter, so the multi-select fallback could not receive them). - Add APPLE_MAS_CERTIFICATE_PASSWORD to the workflow's secrets check so a missing password fails with the actionable message. - Set persist-credentials: false on the mas-store.yml checkout. - Pin dtolnay/rust-toolchain to the current stable commit SHA; the job later holds signing certificates in its keychain. - Replace envsubst with sed in render-mas-entitlements.sh (gettext is not preinstalled on macOS runners) and validate the team ID format. - Filter MAS-hidden Add Data kinds from the command palette and reject them in the OPEN_ADD_DATA_EVENT handler. - Disable the Segmentation form inputs under MAS via formUnavailable, kept separate from webUnavailable so the desktop-download CTA does not appear on a desktop build. - Move the hardcoded raster sidecar message into i18n (toolbar.rasterTool.needsDesktopSidecar). - Document the security-scoped-bookmark project-restore limitation in docs/mac-app-store.md.
Code reviewI read through the full diff (Rust Bugs: None found. Notably verified as correct rather than assumed:
Security: None found. The Performance: None found. Quality: The CLAUDE.md: No violations — new user-facing strings go through I did not find anything worth flagging as an inline comment; the implementation is careful and its safety claims (sandbox degradation behavior, mirrored constant lists) check out against the actual source rather than just the comments. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
.github/workflows/mas-store.yml (3)
94-107: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winMake signing-material cleanup failure-safe.
When code in
Import signing certificates into a temporary keychainfails beforeMAS_KEYCHAINis written, the cleanup step cannot find$MAS_KEYCHAIN. SetMAS_KEYCHAINimmediately after keychain creation and use an error trap to remove bothmas-signing.keychain-dbandmas.p12on failure.🤖 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/mas-store.yml around lines 94 - 107, Update the temporary keychain setup in the “Import signing certificates into a temporary keychain” step so MAS_KEYCHAIN is exported to GITHUB_ENV immediately after security create-keychain succeeds. Add an error trap covering the remaining setup commands that deletes both the keychain referenced by MAS_KEYCHAIN and the temporary mas.p12 file, ensuring cleanup also runs when any command fails before normal completion.
134-135: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winRequire the sandbox entitlement to be
true.This check only looks for the entitlement key, so
<key>com.apple.security.app-sandbox</key><false/>would still pass. Parse and assert thatcom.apple.security.app-sandboxequalstruebefore packaging.🤖 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/mas-store.yml around lines 134 - 135, Update the entitlement validation in the codesign check to parse the XML output and assert that com.apple.security.app-sandbox is explicitly true, rather than only checking for the key. Preserve the existing error and exit behavior when the entitlement is missing or false before packaging.
100-102: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winRestrict signing-key access to the signing tools.
security import -Aallows any application to access the imported private key without prompting. The-T /usr/bin/codesignand-T /usr/bin/productbuildentries are already present, so remove-Aand handle only the remaining signing-key ACL needs, if any.🤖 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/mas-store.yml around lines 100 - 102, Update the security import command in the MAS signing workflow to remove the -A option, preserving the explicit /usr/bin/codesign and /usr/bin/productbuild tool permissions. Only add other ACL entries if required for the signing flow.
🤖 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.
Outside diff comments:
In @.github/workflows/mas-store.yml:
- Around line 94-107: Update the temporary keychain setup in the “Import signing
certificates into a temporary keychain” step so MAS_KEYCHAIN is exported to
GITHUB_ENV immediately after security create-keychain succeeds. Add an error
trap covering the remaining setup commands that deletes both the keychain
referenced by MAS_KEYCHAIN and the temporary mas.p12 file, ensuring cleanup also
runs when any command fails before normal completion.
- Around line 134-135: Update the entitlement validation in the codesign check
to parse the XML output and assert that com.apple.security.app-sandbox is
explicitly true, rather than only checking for the key. Preserve the existing
error and exit behavior when the entitlement is missing or false before
packaging.
- Around line 100-102: Update the security import command in the MAS signing
workflow to remove the -A option, preserving the explicit /usr/bin/codesign and
/usr/bin/productbuild tool permissions. Only add other ACL entries if required
for the signing flow.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0004a2b2-ec5e-4a18-9bec-a5d01f5eb968
📒 Files selected for processing (9)
.github/workflows/mas-store.ymlapps/geolibre-desktop/src/components/layout/TopToolbar.tsxapps/geolibre-desktop/src/components/processing/RasterToolsDialog.tsxapps/geolibre-desktop/src/components/processing/SegmentationDialog.tsxapps/geolibre-desktop/src/i18n/locales/en.jsonapps/geolibre-desktop/src/lib/mas-build.tsapps/geolibre-desktop/src/lib/tauri-io.tsdocs/mac-app-store.mdscripts/render-mas-entitlements.sh
Summary
Adds a Mac App Store (MAS) build of GeoLibre Desktop that satisfies App Store review rules, alongside the existing Developer ID notarized builds. The App Store requires the App Sandbox and forbids downloading or executing code (guideline 2.5.2), which the default build's runtime bootstrap of uv, Python, and martin cannot pass. The MAS variant compiles those services out and keeps every client-side engine.
What is included
Rust (
mascargo feature,apps/geolibre-desktop/src-tauri)generate_handler!list unchanged;load_external_plugin_bundlesreturns an empty result so startup degrades silently.compile_error!forbidsmas+native-duckdb(DuckDB loads its spatial extension as unsigned native code at runtime).Frontend (
GEOLIBRE_MAS_BUILD=1->__GEOLIBRE_MAS_BUILD__define)IS_STORE_BUILDpattern; Rollup dead-code-eliminates the gated branches.sidecar.ts,jupyter.ts,martin.ts) throw a translated message under MAS as a safety net; the in-app updater is stripped via the existingGEOLIBRE_STORE_BUILDflag (Store apps update only through the Store)..shpcannot be read, so the MAS build matches.dbf/.shx/.prj/.cpgcompanions out of the same multi-selection (shapefileCompanionPathsFromSelection, unit tested).Packaging and CI
tauri.mas.conf.jsonoverlay: App Sandbox entitlements, embedded provisioning profile, Apple Distribution signing, hardened runtime off, no bundled sidecar resources.scripts/render-mas-entitlements.sh;Info.plistaddsITSAppUsesNonExemptEncryption.npm run tauri:build:maswires the cargo feature, config overlay, and frontend flags; it refuses--native-duckdb..github/workflows/mas-store.yml(manual dispatch, mirrorsmsix-store.yml): temporary keychain, universal build, verifies the app-sandbox entitlement in the signature, produces a signed.pkgartifact for Transporter submission.docs/mac-app-store.mddocuments the variant, its limitations, required secrets, and submission steps.Also fixes
cargo check --features native-duckdbon main, broken by the locked duckdb crate markingValue#[non_exhaustive].Test plan
cargo check/cargo testpass with and without--features mas(28 and 10 tests respectively), zero new warnings; themas,native-duckdbcombination fails with the intendedcompile_error!.dateinappimage-update-info/metainfo-generatedon macOS runners only).tests/mas-build.test.ts(9 tests) covers the hide helpers and shapefile companion matching.tsc -bclean; smoke build withGEOLIBRE_MAS_BUILD=1succeeds and the bundle drops the gated commands (DCE verified); default build re-verified unchanged.APPLE_MAS_*secrets, documented in the new doc).Follow-ups
APPLE_MAS_*secrets and the App Store Connect app record, then run themas-store.ymlworkflow..shpis picked without its companions under MAS.Summary by CodeRabbit