diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f174cf..703ba22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - New `storebound shots copy translate --to de,es` fills store/shots/copy.json per language (agent translation or `--translate ""`), machine-translated until `shots copy reviewed`, caption limits respected; `shots copy check`; `check` / `shots render` name this command for a missing language. - `apple game-center --locale-map en=en-GB` (config `games.localeMap`) maps a config language to the live Game Center locale; the dry run suggests it when the live Game Center has exactly one variant of the language. - `play games`: a 403 SERVICE_DISABLED / accessNotConfigured names the Google Play Game Services Publishing API, the enable URL for the project and `console plan play-games-services`. + +- `apple build --platform macos --tauri [--path-free] [--unsigned]`: Mac App Store build of a Tauri app. Generated sandbox entitlements, Mac App Store profile (installed, `--profile` or from the API), Apple Distribution / Mac Installer Distribution identities, `tauri build --bundles app`, checks of the .app, `productbuild` to a signed .pkg, `pkgutil --check-signature`; `--path-free` remaps local paths in rustc and fails on any leak of the home folder or user name. `apple upload --platform macos` uploads it (platform MAC_OS). New certificate types MAC_APP_DISTRIBUTION and MAC_INSTALLER_DISTRIBUTION. Pitfall 119. - Desktop dev update channel: Settings > Updates offers Stable (the npm releases) and Dev (every push to main). Each push to main builds Windows and Linux bundles stamped `-dev.` (above the current stable, below the next one); the VDS signs them and serves `storebound.dev/desktop/dev/latest.json` (newest 5 kept). The app checks its channel at start, every 15 minutes, after sleep and right after a channel switch, installs a strictly newer build quietly (deferred while the window is in use), never downgrades (dev back to stable waits for a newer stable), rewrites its logon autostart on every start, and the tray card shows version and channel. No macOS dev builds (Developer ID signing needs the Mac's GUI session). - `shots` full-bleed layout: `shots.layout: "bleed"` fills the image with the app's own frame (no device, no background) and puts the scene title as a short corner caption (`corner`, `captionScale`, `captionWidth`, `captionInset` per scene) legible through a text shadow and soft glow only; the Play feature graphic shows the logo or `shots.feature.title`. Frames are captured per size at its own pixels in `///.png` (feature: `/feature/`), including the `mac` and `chromebook` sizes; `shots check` / `capture --source folder` verify aspect and native width. `shots.orientation: "landscape"` (bleed only) renders every portrait size landscape (stores accept it). Templates `template-bleed.html`, `feature-bleed.html`. - `storebound desktop`: "opened" now also works when a hidden tray copy is already running. On Windows it prints "opened" only when a process of the installed path has a visible window (the running copy shows it through the single-instance handoff); on macOS / Linux a visible start or a handoff to the running copy counts, otherwise it says only a tray copy runs. The graceful stop waits 3 s on Windows (closing a tray app only hides it), then stops by PID. diff --git a/skills/build-and-sign/SKILL.md b/skills/build-and-sign/SKILL.md index 274ab8e..e54b8e9 100644 --- a/skills/build-and-sign/SKILL.md +++ b/skills/build-and-sign/SKILL.md @@ -85,6 +85,32 @@ when the owner said so for this run). The MCP server offers only `apple_signing_ 6. **Next.** `storebound apple encryption --build N`, `storebound apple testflight distribute --build N --group Team --dry` (`testflight` skill), `storebound apple listing --version x.y.z --build N` (`appstore-first-release`). +## Mac App Store: a Tauri app (.pkg) + +`storebound apple build --platform macos --tauri apps/desktop/src-tauri --path-free --dry` prints the plan; without +`--dry` it runs in the Mac's logged-in GUI session (not over SSH: write a .command file and `open -a Terminal` it): + +1. Version from `--version` or tauri.conf.json, build number from `--build-number` or the next free one on App Store + Connect for macOS (CFBundleVersion must increase). `tauri.conf.json` `identifier` must equal `apple.bundleId`. +2. Identities from the login keychain: "Apple Distribution" (or "3rd Party Mac Developer Application") and "Mac + Installer Distribution" (or "3rd Party Mac Developer Installer"). The installer one is its own certificate type + (`apple.signing.certificateType: 'MAC_INSTALLER_DISTRIBUTION'`, `apple signing ensure`). +3. Provisioning profile: `--profile`, else an installed one, else the active `MAC_APP_STORE` profile from the API + (`apple.signing.profileType: 'MAC_APP_STORE'`). +4. `tauri build --bundles app --target universal-apple-darwin --config `: the overlay sets the signing identity, + the generated entitlements (app sandbox, application / team identifier, your own keys kept), the embedded profile, + hardened runtime and `bundleVersion`. Set `bundle.category` and `bundle.macOS.minimumSystemVersion` in + tauri.conf.json (the build stops without LSApplicationCategoryType / LSMinimumSystemVersion). +5. Checks of the .app (sandbox, no get-task-allow, profile inside, version / build), `codesign --verify`, then + `productbuild --component /Applications --sign ` and `pkgutil --check-signature`. +6. `--path-free`: rustc remaps the checkout, CARGO_HOME, RUSTUP_HOME and the home folder, no debug info, symbols stripped; + the build fails when the home folder or user name is still in the app. `--unsigned` runs the pipeline without + identities (not uploadable). Result: `/export/-/.pkg` (`--out`, default `build/macos`). + +Then `storebound apple upload --platform macos --dry` and without `--dry` (App Store Connect build upload, +platform MAC_OS, or `--via altool`). The .pkg is checked first (installer signature); the build shows up in the macOS +version's build list once processed. Details and fixes: `store-pitfalls` 119. + ## Android 1. **Upload key.** Generate once (`keytool -genkeypair -v -storetype PKCS12 -keystore upload.p12 -alias upload diff --git a/skills/store-pitfalls/SKILL.md b/skills/store-pitfalls/SKILL.md index 70aa528..1e06369 100644 --- a/skills/store-pitfalls/SKILL.md +++ b/skills/store-pitfalls/SKILL.md @@ -1415,3 +1415,29 @@ that fails without the fix (see CONTRIBUTING.md). `storebound shots copy reviewed --lang de`. Captions over the limit (eyebrow 30, title line 28, sub 90, badge 18) are not kept. - **Guard:** `cli/shots/copy.ts`, `cli/lib/shots-copy-translate.ts`, `tests/shots-copy-translate.test.ts`. + +## 119. A Tauri Mac App Store build was refused (no sandbox, no profile, wrong installer certificate) or carried the builder's home folder + +- **What happens:** a plain `tauri build` makes a Developer ID app: no `com.apple.security.app-sandbox`, no + `embedded.provisionprofile`, signed with the wrong certificate, packed as a .dmg. Packed by hand into a .pkg it fails at + upload with ITMS-90xxx (sandbox, missing profile, CFBundleVersion not above the last one, no LSApplicationCategoryType) or + the .pkg is signed with an application certificate. Separately, rustc panic messages and debug info carry the + checkout, `~/.cargo` and the user name (`/Users//...`, hundreds of times in a release binary), which ends up + in a public download. +- **Do:** `storebound apple build --platform macos --tauri --path-free --dry`, then the same without `--dry` + in the Mac's logged-in GUI session (codesign over SSH fails with errSecInternalComponent). It needs, on the Mac: an + "Apple Distribution" (or "3rd Party Mac Developer Application") and a "Mac Installer Distribution" (or "3rd Party Mac + Developer Installer") identity in the login keychain, and an active MAC_APP_STORE profile + (`apple.signing.profileType: 'MAC_APP_STORE'`, `apple signing ensure`, or `--profile `). The installer certificate + is a separate certificate type (`MAC_INSTALLER_DISTRIBUTION`). `--unsigned` checks the whole pipeline without them + (never uploadable). A universal build needs both Rust targets (rustup); with Homebrew Rust only the host + target exists: pass `--target aarch64-apple-darwin`. +- **Guard:** `cli/lib/mas.ts` / `cli/apple/build-mas.ts`: entitlements generated with sandbox and application / team + identifier (the project's own keys kept), profile embedded through a tauri.conf overlay, bundleVersion from App Store + Connect (platform MAC_OS) so it increases, the built app checked (sandbox entitlement, no get-task-allow, category, + minimum OS, profile inside, version / build), `--path-free` remaps checkout / CARGO_HOME / RUSTUP_HOME / home in rustc, + drops debug info and symbols and fails when the home folder or user name is still in the app (without the flag the + same scan warns). `cargo trim-paths` is not used: it is nightly-only (cargo 1.98 refuses it). `apple upload + --platform macos` refuses an unsigned .pkg or one not signed with an installer certificate before any network call + (`tests/mas-build.test.ts`). The `com.apple.pkg` file type of the API build upload is taken from Apple's schema and has not + been exercised against the live API yet; `--via altool` (`--type macos`) is the fallback.