chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:08:41 +08:00
commit bcc7968ff6
9988 changed files with 1016778 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
# macOS Development & Distribution
Guides for building and signing the Presenton Electron app on macOS.
| Guide | Description |
|-------|-------------|
| [Direct distribution](./direct-distribution.md) | Developer ID signing, notarization, DMG verification, and release commands for distribution outside the Mac App Store |
| [Mac App Store setup](./mac-app-store-setup.md) | Certificates, provisioning profiles, MAS build commands, and submission notes |
## Quick reference
All commands below run from the `electron/` directory on a Mac.
**First-time setup**
```bash
cd electron
npm run setup:env
```
**Run locally (development)**
```bash
npm run dev
```
**Build a DMG (local distribution, not App Store)**
```bash
npm run build:all
```
Output is written to `electron/dist/`. The default macOS target is a DMG built via `electron/build.js`.
For public releases outside the Mac App Store, use the signed and notarized direct distribution flow:
```bash
export APPLE_KEYCHAIN_PROFILE="presenton-notary"
npm run build:all:mac:signed
```
That assumes the release Mac already has a **Developer ID Application** certificate and a stored `notarytool` profile named `presenton-notary`. See [Direct distribution](./direct-distribution.md) for the exact one-time setup and verification commands.
**Build for the Mac App Store**
See [Mac App Store setup](./mac-app-store-setup.md) for certificates, provisioning profiles, and signing. Summary:
```bash
npm run build:all:mas-dev # development / TestFlight-style testing
npm run build:all:mas # distribution / App Store submission
```
## Related docs
- [Electron dependency strategy](../../electron-dependency-strategy.md) — bundled Chromium, ImageMagick, and export runtime
- [Project README — Electron section](../../../README.md) — prerequisites and high-level build steps
+300
View File
@@ -0,0 +1,300 @@
# macOS Direct Distribution
Use this guide to ship Presenton as a signed and notarized macOS app outside the Mac App Store. This is the correct path for a downloadable DMG from GitHub Releases or presenton.ai.
This is not a Mac App Store build. Do not use MAS provisioning profiles, App Store Connect upload, or App Review for this flow.
If you are building from a Mac that is already registered in Apple Developer, that is fine, but the registration is not what makes the public DMG trusted. Registered devices are for development provisioning and MAS-style testing. Public direct distribution requires a Developer ID Application certificate and Apple notarization.
## What This Produces
The signed release build creates:
```text
electron/dist/
Presenton-<version>.dmg
```
Users should be able to open the DMG and launch Presenton without macOS warning that the app is from an unidentified developer.
## How This Differs From MAS
| Area | Direct distribution | Mac App Store |
|------|---------------------|---------------|
| Certificate | Developer ID Application | Apple Distribution / 3rd Party Mac Developer |
| Provisioning profile | Not used | Required |
| App Sandbox | Not required by this build | Required |
| Notarization | Required | App Store processing handles distribution review |
| Output | DMG for download | PKG for App Store Connect |
The MAS guide in the referenced gist is still useful for the Apple Developer account and signing concepts, but this repo's direct distribution flow deliberately stops before MAS provisioning and App Store submission.
## Repository Configuration
Direct macOS distribution is configured in `electron/build.js`:
- `mac.hardenedRuntime` is enabled for non-MAS macOS builds.
- `mac.entitlements` uses `electron/build/entitlements.mac.plist`.
- `mac.entitlementsInherit` uses `electron/build/entitlements.mac.inherit.plist`.
- `mac.notarize` is enabled unless `PRESENTON_SKIP_NOTARIZATION=1`.
- `dmg.sign` is disabled because the app bundle is signed and notarized; signing the DMG itself is not required.
Use the release scripts in `electron/package.json`:
```bash
npm run build:all:mac:signed
npm run build:electron:mac:signed
npm run dist:mac:signed
```
The `:mac:signed` scripts set `PRESENTON_REQUIRE_MAC_SIGNING=1`, so they fail before packaging if a Developer ID certificate or notarization credentials are missing.
## Exact Setup For A Release Mac
Run this once on the Mac that will build releases.
### 1. Install Apple Command Line Tools
```bash
xcode-select --install
xcrun notarytool --version
```
`notarytool` must be available. If it is missing, install or update Xcode.
### 2. Install The Developer ID Certificate
In Xcode:
1. Open **Xcode** -> **Settings** -> **Accounts**.
2. Select the Apple Developer team.
3. Click **Manage Certificates**.
4. Click **+**.
5. Select **Developer ID Application**.
Do not choose **Apple Development** for public distribution. Do not choose **Apple Distribution** unless you are building for the Mac App Store. For a downloadable DMG, the certificate must be **Developer ID Application**.
Confirm the certificate is visible to `codesign`:
```bash
security find-identity -v -p codesigning | grep "Developer ID Application"
```
Expected shape:
```text
Developer ID Application: Your Company Name (TEAMID)
```
Most release Macs only have one Developer ID Application certificate, so you usually do not need to export a signing identity. If multiple Developer ID certificates are installed, set the exact identity before building:
```bash
export PRESENTON_MAC_SIGN_IDENTITY="Developer ID Application: Your Company Name (TEAMID)"
```
### 3. Store Notarization Credentials Once
Create an app-specific password for the Apple ID, then store notarization credentials in the local Keychain:
```bash
xcrun notarytool store-credentials "presenton-notary" \
--apple-id "apple-id@example.com" \
--team-id "TEAMID" \
--password "app-specific-password"
```
After that, the only notarization environment variable needed for normal local release builds is:
```bash
export APPLE_KEYCHAIN_PROFILE="presenton-notary"
```
To avoid exporting it manually every shell session, add it to your shell profile:
```bash
echo 'export APPLE_KEYCHAIN_PROFILE="presenton-notary"' >> ~/.zshrc
source ~/.zshrc
```
If you store the profile in a non-default keychain, also set:
```bash
export APPLE_KEYCHAIN="/path/to/keychain"
```
Do not commit notarization credentials, app-specific passwords, `.p8` keys, or real certificates to the repo.
## Build A Signed DMG
After the one-time setup, a normal release build is:
```bash
cd electron
npm run build:all:mac:signed
```
If `APPLE_KEYCHAIN_PROFILE` is not in your shell profile, run it inline:
```bash
cd electron
APPLE_KEYCHAIN_PROFILE="presenton-notary" npm run build:all:mac:signed
```
For the very first build on a fresh checkout, run setup first:
```bash
cd electron
npm run setup:env
npm run build:all:mac:signed
```
If the app resources are already built and you only need to re-run Electron packaging:
```bash
cd electron
npm run dist:mac:signed
```
The signed DMG is written to `electron/dist/`.
## What You Do Not Need
For public direct distribution, you do not need:
- A registered test device.
- A `.provisionprofile` file.
- `PRESENTON_MAS_DEV_IDENTITY`.
- `PRESENTON_MAS_DISTRIBUTION_IDENTITY`.
- `PRESENTON_APP_STORE_VERSION`.
- App Store Connect upload.
- App Review approval.
Those are MAS or development-provisioning concerns. The public DMG path is Developer ID signing plus notarization.
## Other Credential Options
The local Keychain profile above is the recommended flow for a human-operated release Mac. CI can use App Store Connect API keys instead:
```bash
export APPLE_API_KEY="/secure/path/AuthKey_XXXXXXXXXX.p8"
export APPLE_API_KEY_ID="XXXXXXXXXX"
export APPLE_API_ISSUER="00000000-0000-0000-0000-000000000000"
npm run build:all:mac:signed
```
You can also pass Apple ID credentials directly, but this is less convenient than a stored Keychain profile:
```bash
export APPLE_ID="apple-id@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="TEAMID"
npm run build:all:mac:signed
```
## Verify The Release
Run these checks before publishing the DMG.
### 1. Check Code Signature
Replace the app path if the architecture-specific output folder differs.
```bash
codesign --verify --deep --strict --verbose=2 "dist/mac/Presenton.app"
codesign -dv --verbose=4 "dist/mac/Presenton.app" 2>&1 | grep -E "Authority|TeamIdentifier|Runtime"
```
Expected:
- `Authority=Developer ID Application: ...`
- `TeamIdentifier=S6W5C54KL6`
- Hardened Runtime is present.
### 2. Check Notarization Stapling
```bash
xcrun stapler validate "dist/mac/Presenton.app"
```
Expected:
```text
The validate action worked!
```
### 3. Check Gatekeeper
```bash
spctl --assess --type execute --verbose=4 "dist/mac/Presenton.app"
spctl --assess --type open --verbose=4 "dist/Presenton-0.8.8-beta.dmg"
```
Expected shape:
```text
accepted
source=Notarized Developer ID
```
### 4. Test On Another Mac
Download the DMG on a Mac that did not build it, mount it, drag Presenton to `/Applications`, and launch it normally. This catches quarantine and Gatekeeper behavior that local build machines can hide.
## Troubleshooting
**The signed release build says the Developer ID identity is missing**
Install a Developer ID Application certificate in Keychain Access, or set:
```bash
export PRESENTON_MAC_SIGN_IDENTITY="Developer ID Application: Your Company Name (TEAMID)"
```
**The build says notarization credentials are missing**
Set one complete credential group:
```bash
export APPLE_KEYCHAIN_PROFILE="presenton-notary"
```
or:
```bash
export APPLE_ID="apple-id@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="TEAMID"
```
or:
```bash
export APPLE_API_KEY="/secure/path/AuthKey_XXXXXXXXXX.p8"
export APPLE_API_KEY_ID="XXXXXXXXXX"
export APPLE_API_ISSUER="00000000-0000-0000-0000-000000000000"
```
**macOS still says the app is damaged or cannot be opened**
Run the verification commands above. If stapling fails, rebuild with valid notarization credentials and do not publish the DMG until `spctl` reports `source=Notarized Developer ID`.
**You need a local unsigned build**
Use the generic build script instead of the signed release script:
```bash
npm run build:all
```
For release artifacts, always use:
```bash
npm run build:all:mac:signed
```
## References
- Steve Crickmore's Electron MAS release gist: https://gist.github.com/steve981cr/def310670dfd9ed1439bf31cc734f941
- Electron signing and notarization docs: https://www.electronjs.org/docs/latest/tutorial/code-signing
- electron-builder notarization docs: https://www.electron.build/code-signing-mac.html#notarize
- Apple notarization docs: https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
+201
View File
@@ -0,0 +1,201 @@
# Mac App Store Setup
Presentons Electron app is configured for Mac App Store (MAS) distribution with App Sandbox. MAS builds must be produced on macOS because Apples signing tools are required.
This guide covers Apple Developer setup, local provisioning files, and the npm scripts in `electron/package.json` on `main`.
## Overview
| Target | Purpose | npm script |
|--------|---------|------------|
| `dmg` | Direct download outside the App Store | `npm run build:all:mac:signed` |
| `mas-dev` | Sandbox testing on registered Macs | `npm run build:all:mas-dev` |
| `mas` | App Store submission | `npm run build:all:mas` |
Build configuration lives in `electron/build.js`. Key identifiers:
- **Bundle ID:** `com.presenton.presenton`
- **Team ID:** `S6W5C54KL6` (set in `extendInfo.ElectronTeamID`)
## MAS files in the repo
These are checked in under `electron/build/`:
| File | Role |
|------|------|
| `entitlements.mas.plist` | Main app entitlements (sandbox, network, JIT, user-selected files) |
| `entitlements.mas.inherit.plist` | Inherited entitlements for helper processes and bundled binaries |
| `MacAppStore.provisionprofile.replace_me` | Placeholder — replace with your real App Store profile (see below) |
`build.js` validates provisioning profiles with `security cms -D` before building. Invalid or placeholder files will fail the build with a clear error.
## What you need from Apple
### 1. Apple Development certificate
Xcode → **Settings****Accounts** → your team → **Manage Certificates****+** → **Apple Development**.
Used for `mas-dev` builds and local sandbox testing.
### 2. Apple Distribution certificate
Same path as above, but choose **Apple Distribution**.
Used for `mas` (App Store submission) builds.
### 3. App ID (Bundle ID)
[Apple Developer Portal](https://developer.apple.com/account) → **Certificates, Identifiers & Profiles****Identifiers****App IDs**.
Create a macOS App ID that matches `com.presenton.presenton` (or update `APP_ID` in `electron/build.js` if you use a different ID).
### 4. Provisioning profiles
Create **two** macOS profiles and download each `.provisionprofile` file into `electron/build/`:
#### AppleDevelopment (MAS development)
- Portal → **Profiles****+**
- Type: **macOS App Development**
- App ID: your Presenton app ID
- Certificate: **Apple Development**
- Save as `electron/build/AppleDevelopment.provisionprofile`
`build.js` also accepts `AppleDev.provisionprofile` or `AppDev.provisionprofile` as fallbacks.
#### MacAppStore (MAS distribution)
- Portal → **Profiles****+**
- Type: **Mac App Store**
- App ID: your Presenton app ID
- Certificate: **Apple Distribution**
- Save as `electron/build/MacAppStore.provisionprofile`
Verify profiles decode correctly:
```bash
cd electron
security cms -D -i build/AppleDevelopment.provisionprofile
security cms -D -i build/MacAppStore.provisionprofile
```
Provisioning profiles expire (typically yearly). Regenerate and replace the local files when they do.
## Environment variables
| Variable | Used for | Description |
|----------|----------|-------------|
| `PRESENTON_MAC_TARGET` | Set by npm scripts | `mas-dev` or `mas` (do not set manually when using the scripts below) |
| `PRESENTON_MAS_DEV_IDENTITY` | `mas-dev` | Signing identity, e.g. `Apple Development: Your Name (TEAMID)` |
| `PRESENTON_MAS_DISTRIBUTION_IDENTITY` | `mas` | Distribution identity, e.g. `Apple Distribution: Your Org (TEAMID)` |
| `PRESENTON_MAS_IDENTITY` | `mas` | Alias for distribution identity |
| `CSC_NAME` | Either target | Fallback if the target-specific identity vars are unset |
| `PRESENTON_APP_STORE_VERSION` | `mas` | App Store **version** (`x.y.z`). Required when `package.json` version contains a suffix like `-beta` |
| `PRESENTON_APP_STORE_BUILD` | `mas` | App Store **build** number. Defaults to the short version if unset |
Example for a beta package version:
```bash
export PRESENTON_APP_STORE_VERSION=1.0.0
export PRESENTON_APP_STORE_BUILD=42
```
## Build commands
Run from `electron/` after `npm run setup:env` (first time only).
### Full build (recommended)
Rebuilds Next.js, FastAPI, export runtime, and packages the app:
```bash
# MAS development — runs on Macs in your dev provisioning profile
PRESENTON_MAS_DEV_IDENTITY="Apple Development: Your Name (TEAMID)" \
npm run build:all:mas-dev
# MAS distribution — for App Store Connect upload
PRESENTON_MAS_DISTRIBUTION_IDENTITY="Apple Distribution: Your Org (TEAMID)" \
PRESENTON_APP_STORE_VERSION=1.0.0 \
npm run build:all:mas
```
### Package only (resources already built)
If you already ran `build:all` and only need to re-run the Electron packager:
```bash
PRESENTON_MAS_DEV_IDENTITY="Apple Development: Your Name (TEAMID)" \
npm run dist:mac:mas-dev
PRESENTON_MAS_DISTRIBUTION_IDENTITY="Apple Distribution: Your Org (TEAMID)" \
PRESENTON_APP_STORE_VERSION=1.0.0 \
npm run dist:mac:mas
```
### Direct DMG (not App Store)
For a signed and notarized macOS disk image that does not go through App Store approval:
```bash
npm run build:all:mac:signed
```
See [Direct distribution](./direct-distribution.md) for the Developer ID certificate, notarization, and verification flow.
Artifacts land in `electron/dist/`.
## Output locations
Exact paths depend on architecture (`arm64` vs `x64`). Typical layouts:
```text
electron/dist/
mas-dev-arm64/ # unsigned or dev-signed .app (mas-dev)
mas-arm64/ # distribution-signed .app (mas)
Presenton-<version>.pkg # MAS installer for upload
Presenton-<version>.dmg # signed and notarized direct distribution DMG
```
Upload the `.pkg` from the `mas` build to [App Store Connect](https://appstoreconnect.apple.com).
## Icons
The default macOS icon is `electron/resources/ui/assets/images/presenton_short_filled.png`.
For App Store packaging you can provide a proper `.icns` at `electron/build/icon.icns` and update the `mac.icon` field in `electron/build.js` if you want a custom store icon.
Source PNGs for generating an icon set are in `electron/build/icon.iconset/`.
## Testing notes
- **MAS-signed apps** require the MAS Electron build (`mas` or `mas-dev` targets). A normal DMG build is not sandbox-compatible in the same way.
- **`mas-dev` builds** run only on Macs registered in your development provisioning profile.
- **`mas` (distribution) builds** usually do not launch locally; they are meant for App Store Connect processing and release.
- **`mas-dev` signing** uses `--timestamp=none` in `build.js` so local signing works without a network timestamp server.
## Troubleshooting
**“Missing MAS development/distribution provisioning profile”**
Place the correct `.provisionprofile` in `electron/build/` (see [Provisioning profiles](#4-provisioning-profiles)).
**“macOS could not decode” a profile**
Re-download the profile from the Developer Portal. Do not commit real profiles to git.
**“Cannot derive an App Store version from package version”**
Set `PRESENTON_APP_STORE_VERSION` to three integers, e.g. `1.0.0`.
**“MAS builds must be run on macOS”**
Run the build on a Mac with Xcode command-line tools installed (`xcode-select --install`).
**FastAPI or export binary not executable inside the .app**
`build.js` runs an `afterPack` hook that sets executable permissions on bundled binaries. If signing fails afterward, check that entitlements allow the sandboxed app to execute helpers in `Contents/Resources/app/resources/`.
## See also
- [macOS dev README](./README.md) — quick reference for all macOS build targets
- [Electron dependency strategy](../../electron-dependency-strategy.md) — what gets bundled in the app