Skip to content

build: v3.1.0 release pipeline with signing, notarization, icon and single version source (TASK-009) - #21

Merged
osmcgrgenc merged 2 commits into
mainfrom
task/release
Sep 28, 2026
Merged

osmcgrgenc merged 2 commits into
mainfrom
task/release

Conversation

@osmcgrgenc

Copy link
Copy Markdown
Contributor

Summary

  • One version source: VERSION (3.1.0). build_app.sh writes it to CFBundleShortVersionString and git rev-list --count HEAD to CFBundleVersion. The CLI version command prints MacOptimizer Pro v3.1.0 from the bundle. The release workflow checks that the tag is v<VERSION>.

  • Bundle: universal binary (arm64 + x86_64) via UNIVERSAL=1, minos 14.0. LSUIElement=false (keeps the Dock icon), LSMinimumSystemVersion 14.0, a new NSHumanReadableCopyright, and CFBundleLocalizations en/tr. The build now fails if en.lproj or tr.lproj is missing from the resource bundle.

  • Icon: Scripts/build_icns.sh turns the master Resources/Brand/AppIcon-1024.png into all iconset sizes (sips) and then AppIcon.icns (iconutil) on every build. The current master is an original placeholder drawn by Scripts/make_icon.swift (CoreGraphics). That generator runs only if the master is missing. To use the designed logo, replace the PNG.

  • Entitlements: Scripts/MacOptimizer.entitlements is empty on purpose: hardened runtime, not sandboxed, no exceptions. The reasoning is in the file and in docs/RELEASING.md.

  • Scripts/release.sh:

    1. Builds the app.
    2. Asserts the bundle holds a single Mach-O.
    3. Signs with codesign --options runtime --timestamp, then runs codesign --verify --strict --deep and spctl.
    4. Builds a DMG with an /Applications link and signs it.
    5. Notarizes the DMG once (keychain profile codifya-notary, or an API key in CI), then staples and validates it.
    6. Runs spctl on the DMG and writes a .sha256.

    DRY_RUN=1 skips notarization.

  • CI:

    • release.yml builds an unsigned universal DMG for tags as a workflow artifact.
    • An optional sign-and-notarize job runs only when all five secrets exist. It uses a temporary keychain and API-key notarization, creates a draft release, and cleans up afterwards. It is skipped without failing when the secrets are absent.
    • ci.yml now builds the universal bundle on PRs and checks the version, the icon, both .lproj folders and both architectures.
  • docs/RELEASING.md covers the local flow, the icon and the CI secrets.

Test plan

  • swift build and swift test (scratch path /private/tmp/release-build): 87 XCTest, 0 failures
  • DRY_RUN=1 ./Scripts/release.sh:
    • flags=0x10000(runtime), with authority chain Developer ID Application: OSMAN CAGRI GENC (N83DXM47FV) → Developer ID CA → Apple Root CA, and TeamIdentifier N83DXM47FV
    • The app and the DMG are both valid on disk
    • spctl reports Unnotarized Developer ID, which is expected before notarization
  • The mounted DMG contains MacOptimizer.app and the Applications link. The app has AppIcon.icns plus en.lproj and tr.lproj, passes codesign --verify --strict --deep, and prints version 3.1.0.
  • actionlint passes on release.yml
  • Real notarized release (Claude, local, after review)

🤖 Generated with Claude Code

osmcgrgenc and others added 2 commits September 29, 2026 01:43
@osmcgrgenc
osmcgrgenc merged commit 00fc97e into main Sep 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant