The product name is GitRonimo. The crate is still gitronimo-desktop; the binary and .app executable are GitRonimo so the macOS application menu title matches.
Product version 2.0.5 is the string shown in About GitRonimo (APP_VERSION in apps/desktop/src/views/about.rs) and in the bundle ([package.metadata.packager] version in apps/desktop/Cargo.toml). Bump both after each release. They are independent of the Cargo workspace crate version.
Local .app bundles are unsigned. Gatekeeper will not treat them as a distributable release.
The app identifier is com.gitronimo.desktop. The dock and Finder icon is assets/gitronimo.icns, generated from icon.png. About GitRonimo shows assets/gitronimo-icon.png. Local unsigned builds: ./bin/build. Keep certificate material, team IDs, and notary credentials in the release environment or CI secrets; never in this repository.
./bin/buildThe script installs cargo-packager 0.11.8 if needed, builds the native architecture, and writes target/release-arm/GitRonimo.app (Apple Silicon) or target/release-intel/GitRonimo.app (Intel).
export PATH="$HOME/.rustup/toolchains/1.97.1-aarch64-apple-darwin/bin:$PATH"
cargo build --release -p gitronimo-desktop
cargo install cargo-packager --version 0.11.8 --locked
cargo packager --release --formats app \
--manifest-path apps/desktop/Cargo.toml \
--out-dir "$(pwd)/target/release-arm" \
--binaries-dir "$(pwd)/target/release"
open target/release-arm/GitRonimo.appUse absolute --out-dir / --binaries-dir values. cargo-packager resolves relative paths from apps/desktop/Cargo.toml.
Confirm the executable architecture:
lipo -archs target/release-arm/GitRonimo.app/Contents/MacOS/GitRonimo
# arm64Cross-compile from Apple Silicon (or build natively on Intel):
rustup target add x86_64-apple-darwin
cargo build --release -p gitronimo-desktop --target x86_64-apple-darwin
cargo packager --release --formats app --target x86_64-apple-darwin \
--manifest-path apps/desktop/Cargo.toml \
--out-dir "$(pwd)/target/release-intel" \
--binaries-dir "$(pwd)/target/x86_64-apple-darwin/release"
lipo -archs target/release-intel/GitRonimo.app/Contents/MacOS/GitRonimo
# x86_64mkdir -p target/dist
ditto -c -k --sequesterRsrc --keepParent \
target/release-arm/GitRonimo.app \
target/dist/GitRonimo-2.0.5-macos-arm64.zip
ditto -c -k --sequesterRsrc --keepParent \
target/release-intel/GitRonimo.app \
target/dist/GitRonimo-2.0.5-macos-x86_64.zip
(cd target/dist && shasum -a 256 GitRonimo-2.0.5-macos-*.zip > SHA256SUMS.txt)A universal binary (lipo of both executables into one GitRonimo.app) is produced by .github/workflows/release.yml on a v* tag, then signed and notarized.
For a distributable release, import an Apple Developer ID Application certificate into a dedicated keychain, then sign with hardened runtime and timestamping:
codesign --force --deep --options runtime --timestamp --sign "Developer ID Application: YOUR NAME (TEAMID)" target/release-arm/GitRonimo.app
codesign --verify --deep --strict --verbose=2 target/release-arm/GitRonimo.app
xcrun notarytool submit YOUR_DMG_OR_ZIP --keychain-profile "notary-profile" --wait
xcrun stapler staple target/release-arm/GitRonimo.appPushing a v* tag starts .github/workflows/release.yml. Configure these repository secrets before creating the tag:
DEVELOPER_ID_APPLICATION: the Developer ID Application signing identity name.MACOS_CERTIFICATE_BASE64: the base64-encoded.p12certificate.MACOS_CERTIFICATE_PASSWORD: the.p12password.KEYCHAIN_PASSWORD: a unique temporary CI keychain password.APPLE_API_KEY_BASE64: the base64-encoded App Store Connect API key.p8file.APPLE_API_KEY_IDandAPPLE_API_ISSUER: the corresponding App Store Connect API key identifiers.
The workflow builds arm64 and x86_64 bundles, creates a universal app, signs it with hardened runtime and a timestamp, notarizes and staples it (retries notarytool --wait on App Store Connect HTTP timeouts), writes SHA256SUMS.txt with the zip basename (hash from target/release-universal/, not a path-prefixed shasum line), and publishes the ZIP with CHANGELOG.md as the release notes via the GitHub REST releases API (gh api), with retries. Do not use gh release view / create here: those go through GraphQL and can 503 while REST still works. If a release for the tag already exists, matching assets are replaced. A 503 after a successful notarize does not mean signing failed: the signed zip is already a workflow artifact (GitRonimo-signed-macos).
If notarytool reports The request timed out after Successfully uploaded file, signing already succeeded; re-run the job. Apple sometimes takes 15–40 minutes, and a poll timeout is not a certificate problem.
Unsigned per-architecture CI artifacts are also uploaded from .github/workflows/ci.yml (GitRonimo-unsigned-macos-arm64 and GitRonimo-unsigned-macos-x86_64). The in-app updater refuses those: it only installs a zip whose extracted GitRonimo.app passes codesign --verify --deep --strict and spctl --assess --type execute.
GitRonimo → Settings… In-app updates is on by default. There is no check on launch. Check now (About Check for updates, GitRonimo menu Check for Updates…, palette Check for updates) GETs https://api.github.com/repos/mpakus/gitronimo/releases/latest without a PAT, downloads SHA256SUMS.txt then GitRonimo-${tag}.zip, verifies SHA-256 with shasum -a 256, unpacks with ditto, Gatekeeper-assesses the .app, then replaces the running GitRonimo.app (backup beside it, restored on copy failure). cargo run / target/ binaries are not a bundle and are refused. Failed hash or Gatekeeper leaves the running app untouched. No telemetry.