Skip to content

Latest commit

 

History

History
161 lines (112 loc) · 6.05 KB

File metadata and controls

161 lines (112 loc) · 6.05 KB

Releasing TaskMenu

TaskMenu releases are published from GitHub Actions when a vX.Y.Z tag is pushed. The release workflow builds a signed archive, packages it into a DMG, notarizes the DMG with Apple, and uploads the DMG plus a SHA-256 checksum to the GitHub release.

Homebrew cask distribution is maintained in the public crazytan/homebrew-tap repository.

One-time GitHub setup

Add these repository secrets in GitHub under Settings -> Secrets and variables -> Actions -> Secrets:

Secret Value
BUILD_CERTIFICATE_BASE64 Base64-encoded .p12 export of your Developer ID Application certificate
BUILD_CERTIFICATE_PASSWORD Password used when exporting the .p12 file
APP_STORE_CONNECT_API_KEY_BASE64 Base64-encoded App Store Connect API private key (.p8)
GOOGLE_CLIENT_ID Google OAuth client ID used by release builds
APP_STORE_CONNECT_KEY_ID App Store Connect API key ID
APP_STORE_CONNECT_ISSUER_ID App Store Connect issuer ID

Add these repository variables under Settings -> Secrets and variables -> Actions -> Variables:

Variable Value
GOOGLE_REDIRECT_SCHEME Google OAuth redirect scheme, for example com.googleusercontent.apps.<client-id-prefix>
APPLE_TEAM_ID Apple Developer Team ID, for example V82M9YX8BR

The release workflow also accepts GOOGLE_REDIRECT_SCHEME and APPLE_TEAM_ID as secrets for compatibility, but variables are preferred when the values are not sensitive.

TaskMenu uses a native OAuth client with PKCE, so release builds do not embed a Google client secret.

Export the signing certificate

  1. Open Keychain Access on the Mac that has your Developer ID certificate.
  2. Find Developer ID Application: Your Name (TEAMID).
  3. Expand the certificate row and make sure the private key is included.
  4. Select the certificate and private key together, then choose File -> Export Items....
  5. Save as DeveloperIDApplication.p12 and set a strong export password.
  6. Copy the base64 form into the GitHub secret:
base64 -i DeveloperIDApplication.p12 | tr -d '\n' | pbcopy

Paste the clipboard into BUILD_CERTIFICATE_BASE64. Put the export password in BUILD_CERTIFICATE_PASSWORD.

Create the App Store Connect API key

Create an App Store Connect API key in App Store Connect -> Users and Access -> Integrations -> App Store Connect API. A key with Developer access is enough for notarization.

Download the .p8 key once, then copy the base64 form into APP_STORE_CONNECT_API_KEY_BASE64:

base64 -i AuthKey_XXXXXXXXXX.p8 | tr -d '\n' | pbcopy

Store the key ID in APP_STORE_CONNECT_KEY_ID and the issuer ID in APP_STORE_CONNECT_ISSUER_ID.

Notarization troubleshooting

If the release job fails with:

source=Unnotarized Developer ID

before the notarytool submit command, that is expected. The app has not been notarized yet, so the workflow packages the signed app into a DMG and submits that DMG to Apple.

If the release job fails with:

HTTP status code: 401. Unauthenticated.

then Apple rejected the App Store Connect API credentials. Check these values first:

  • APP_STORE_CONNECT_API_KEY_BASE64 must be the base64 text of the downloaded .p8 private key file, not the JSON key metadata.
  • APP_STORE_CONNECT_KEY_ID must match the key ID shown for that same .p8 file.
  • APP_STORE_CONNECT_ISSUER_ID must be the issuer ID from App Store Connect API access.
  • The API key must be active, team-scoped, and have at least Developer access.

You can validate the same credentials locally before pushing a tag:

xcrun notarytool history \
  --key "$HOME/private_keys/AuthKey_XXXXXXXXXX.p8" \
  --key-id "XXXXXXXXXX" \
  --issuer "00000000-0000-0000-0000-000000000000"

Release checklist

  1. Move changelog entries from Unreleased to a version heading:
## vX.Y.Z (YYYY-MM-DD)
  1. Update MARKETING_VERSION and CURRENT_PROJECT_VERSION in project.yml.
  2. Regenerate the project if you want the checked-in .xcodeproj to reflect the new version:
xcodegen generate
  1. Run tests:
xcodebuild test -project TaskMenu.xcodeproj -scheme TaskMenu \
  -configuration Debug \
  -destination "platform=macOS"
  1. Commit and push to main.
  2. Create and push the release tag:
git tag -a vX.Y.Z -m "TaskMenu vX.Y.Z"
git push origin main vX.Y.Z

The release workflow validates that the tag version matches MARKETING_VERSION and that CHANGELOG.md has a matching section before it publishes anything.

  1. After the GitHub release publishes, update the Homebrew cask in crazytan/homebrew-tap:
brew tap crazytan/tap
cd "$(brew --repository crazytan/tap)"

version="${VERSION:-$(gh release view --repo crazytan/TaskMenu --json tagName --jq '.tagName | sub("^v"; "")')}"
sha256="$(curl -LfsS "https://github.com/crazytan/TaskMenu/releases/download/v${version}/TaskMenu-${version}.dmg.sha256" | awk '{print $1}')"

perl -0pi -e "s/version \"[^\"]+\"/version \"${version}\"/" Casks/taskmenu.rb
perl -0pi -e "s/sha256 \"[0-9a-f]+\"/sha256 \"${sha256}\"/" Casks/taskmenu.rb

brew audit --cask --strict crazytan/tap/taskmenu
brew livecheck --cask crazytan/tap/taskmenu

git add Casks/taskmenu.rb
git commit -m "Update TaskMenu to ${version}"
git push

Manual workflow dispatch

You can also run Release manually from GitHub Actions. Enter the version without the leading v and choose whether the GitHub release should be marked as a prerelease. The workflow still expects project.yml and CHANGELOG.md to match that version.

Local DMG packaging

After producing a signed app bundle, you can package it locally:

VERSION="X.Y.Z"
SIGNING_IDENTITY="Developer ID Application" \
APP_STORE_CONNECT_KEY_ID="XXXXXXXXXX" \
APP_STORE_CONNECT_ISSUER_ID="00000000-0000-0000-0000-000000000000" \
APP_STORE_CONNECT_KEY_PATH="$HOME/private_keys/AuthKey_XXXXXXXXXX.p8" \
./scripts/make_dmg.sh \
  --app build/TaskMenu.xcarchive/Products/Applications/TaskMenu.app \
  --version "$VERSION" \
  --output-dir build/artifacts