feat: package Left as a universal macOS download - #1
Open
ItsFlow wants to merge 5 commits into
Open
Conversation
scripts/package-dmg.sh builds a universal, ad-hoc-signed image under .build so GitHub Releases can serve a stable download URL. install.sh still installs from source; the packager never writes ~/Applications/Left.app.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Package Left as a Mac-installable download the captain can offer from a website. Public repo https://github.com/ItsFlow/left, MIT, required base origin/main at 0f21484 ("Publish Left.").
Ship a .dmg containing Left.app plus a GitHub Release so the website can use a stable URL such as https://github.com/ItsFlow/left/releases/latest/download/Left.dmg (or a versioned name if cleaner; report the exact URL). Follow the Night Walker download pattern: ad-hoc signed, first launch is right-click → Open, not App Store. macOS 13+. Prefer a universal (arm64+x86_64) binary if this toolchain can produce one without a new dependency; otherwise ship native and state the arch in the README.
Also: a scripted, repeatable packager (scripts/package-dmg.sh) that builds, ad-hoc signs, verifies, and writes the DMG under .build/ (gitignored). Do not commit the .dmg or .app unless a tiny committed copy is required for Pages — prefer the GitHub Release asset. README: a Download section at the top of Install with the release URL and the one-line Gatekeeper instruction. Keep source ./scripts/install.sh for people building from checkout. App version already lives at scripts/install.sh (0.2.0); use that (or bump if packaging rules require). Tag/release v0.2.0 unless the existing version is wrong. Probe security find-identity -v -p codesigning for a Developer ID. If none, stay ad-hoc (do not invent notarization). If a Developer ID is present, use it and notarize only if the existing machine already has the notary credentials; otherwise stay ad-hoc and note it. Do not prompt the captain for an Apple password. Do not build a marketing site. Do not enable GitHub Pages unless a one-file download landing is the only way to get a stable URL (Release is preferred). Do not install or replace ~/Applications/Left.app. Do not invoke chrome-devtools-axi. Do not run live quota-axi --full.
Acceptance: repeatable DMG packager; Left.app inside; ad-hoc (or Developer ID) signature verifies. GitHub Release with the DMG attached; full https://github.com/ItsFlow/left/releases/... URL. README download + Gatekeeper line. ./scripts/verify.sh still passes. PR through no-mistakes, checks green. Firstmate owns merge.
Implementation decisions already made and accepted: no Developer ID identities on this machine, so stay ad-hoc and do not notarize. Dual swift build --arch arm64 --arch x86_64 needs Xcode xcbuild which Command Line Tools lack; the packager builds each slice and lipos them so the DMG is still universal. Notarization only if NOTARY_KEYCHAIN_PROFILE is already set (never prompt). The packager must not write the live install path. The GitHub Release v0.2.0 with Left.dmg is required; create it once the branch is on GitHub, targeting the validated commit.
What Changed
Left.app, and writes.build/Left.dmgwith an Applications shortcut.Risk Assessment
✅ Low: The packaging changes are well-bounded, prior universal-build and live-install-path issues are resolved, and no remaining source-verifiable defects or intent contradictions were found.
Testing
Built and mounted the real DMG end to end: it contains a valid ad-hoc-signed universal Left 0.2.0 app for macOS 13+ and an Applications shortcut. No UI screenshot was necessary because the reviewed product is the generated disk image itself. The public acceptance path still fails because v0.2.0 is absent and the README download URL returns 404; the worktree remained clean.
/var/folders/16/p22jkpcn0z10z3lrwkpc4yq00000gn/T/no-mistakes-evidence/01M140VJSVEFQCMZ1EEVF1Q6QC/Left.dmg)Evidence: Mounted DMG inspection
Evidence: Public release URL response headers
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 2 issues found → auto-fixed (3) ✅
scripts/package-app.sh:60- The live-install guard compares only the path string, but the laterrm -rffollows symlinks in parent components. A destination such as/tmp/Applications/Left.app, where/tmp/Applicationslinks to~/Applications, bypasses the guard and deletes/replaces the live app. Canonicalize and validate the destination parent before any removal or write.scripts/package-app.sh:133- The accepted intent says the per-architecture builds must be lipod so the DMG “is still universal,” and the README advertises Intel and Apple Silicon. However, failure of either slice silently produces a native-only DMG. Make the default universal path fail hard; native packaging should occur only through the explicit--nativeoption.🔧 Fix: Harden package destination and universal builds
1 error still open:
scripts/package-app.sh:131- The universal build still does not reliably fail hard.build_sliceruns inside command substitution, where Bash normally clearserrexit; ifswift buildfails, the function can continue through the successful--show-bin-pathandprintf. A stale executable from an earlier build then passes the-xcheck and gets included in the DMG. Explicitly return on either Swift command failure and validate each slice beforelipo.🔧 Fix: Fail universal packaging on stale architecture slices
1 error still open:
scripts/package-dmg.sh:105- The accepted requirement says “Native-only output is allowed only with explicit --native,” but--appcopies any supplied bundle without checking its architectures. Thereforepackage-dmg.sh --app <native-only.app>silently creates a native-only DMG whileUNIVERSALremains true. Validate arm64+x86_64 for--appunless--nativewas explicitly passed.🔧 Fix: Enforce universal architecture for supplied apps
✅ Re-checked - no issues remain.
README.md:34- The required GitHub Releasev0.2.0is not published: the repository reports zero releases and the documented stable download URL returns HTTP 404. Publish the validated DMG asLeft.dmgon releasev0.2.0.security find-identity -v -p codesigning./scripts/package-dmg.sh --output /var/folders/16/p22jkpcn0z10z3lrwkpc4yq00000gn/T/no-mistakes-evidence/01M140VJSVEFQCMZ1EEVF1Q6QC/Left.dmgMounted the generated DMG read-only withhdiutil attach; verifiedLeft.app, the/Applicationssymlink, signature, architectures, version, minimum macOS version, bundle identifier, and SHA-256gh-axi release view v0.2.0 --fullandgh-axi release list --limit 10curl -sSIL https://github.com/ItsFlow/left/releases/latest/download/Left.dmgswift package cleanandgit status --shortto remove generated build products and confirm no worktree changes🔧 Fix: Release blocked until target commit is pushed
1 error still open:
README.md:34- The required GitHub Releasev0.2.0remains unpublished: GitHub reports the release as not found and the README’s stablereleases/latest/download/Left.dmgURL returns HTTP 404. Publish the validated DMG asLeft.dmgon releasev0.2.0.Inspected the base-to-target changed-file set withgit diff --statandgit diff --name-status.security find-identity -v -p codesigning(zero Developer ID identities)../scripts/package-dmg.sh --output /var/folders/16/p22jkpcn0z10z3lrwkpc4yq00000gn/T/no-mistakes-evidence/01M140VJSVEFQCMZ1EEVF1Q6QC/Left.dmg.Mounted the DMG read-only and verified its payload, Applications symlink, bundle version 0.2.0, macOS 13.0 minimum, bundle identifier, strict ad-hoc signature, and x86_64+arm64 architectures usinghdiutil,plutil,codesign, andlipo.Executed the mountedLeft.app/Contents/MacOS/Left --render-preview Fixtures/demo-quota.json …/packaged-app-preview.pngand visually inspected the rendered 340×430 app surface../scripts/package-app.sh --destination /Users/flo/Applications/Left.app(expected refusal before building, exit 2).gh-axi release view v0.2.0 --repo ItsFlow/left.curl -fsSIL https://github.com/ItsFlow/left/releases/latest/download/Left.dmg.Removed transient.build/outputs withgit clean -fdX .buildand confirmed the worktree remained clean.🔧 Fix: Release blocked by unpublished target commit
1 error still open:
README.md:34- The required GitHub Releasev0.2.0is still unpublished: GitHub reportsrelease not found, and the README’s stablereleases/latest/download/Left.dmgURL returns HTTP 404. Publish the validated DMG asLeft.dmgon releasev0.2.0, targeting the validated commit.security find-identity -v -p codesigning./scripts/package-dmg.sh --output /var/folders/16/p22jkpcn0z10z3lrwkpc4yq00000gn/T/no-mistakes-evidence/01M140VJSVEFQCMZ1EEVF1Q6QC/Left.dmgMounted the produced DMG read-only and verifiedLeft.app, the/Applicationssymlink, version 0.2.0, macOS 13.0 minimum, x86_64 and arm64 slices, and the ad-hoc signaturegh-axi release view v0.2.0 --repo ItsFlow/leftcurl -sSIL https://github.com/ItsFlow/left/releases/latest/download/Left.dmggit status --shortand checked for residual DMG/icon staging directories🔧 Fix: Release blocked until target commit is pushed
1 error still open:
README.md:34- The required GitHub Releasev0.2.0remains unpublished: GitHub reportsrelease not found, and the documented stable download URL returns HTTP 404. Publish the validated universal DMG asLeft.dmgon releasev0.2.0, targeting commite509da93112e73de8a622aaae0762652c3caca35.security find-identity -v -p codesigningconfirmed no Developer ID identity is available../scripts/package-dmg.sh --output /var/folders/16/p22jkpcn0z10z3lrwkpc4yq00000gn/T/no-mistakes-evidence/01M140VJSVEFQCMZ1EEVF1Q6QC/Left.dmgbuilt the end-user disk image.Mounted the generated DMG read-only and verifiedLeft.app, the/Applicationsshortcut, version 0.2.0, macOS 13 minimum, arm64+x86_64 architectures, and the valid ad-hoc signature.gh-axi release view v0.2.0 --repo ItsFlow/leftreturnedrelease not found.curl -sSIL https://github.com/ItsFlow/left/releases/latest/download/Left.dmgreturned HTTP 404.git ls-remote origin refs/heads/fm/left-distfound no published remote branch for the target change.✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.