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.
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.
- Open Keychain Access on the Mac that has your Developer ID certificate.
- Find Developer ID Application: Your Name (TEAMID).
- Expand the certificate row and make sure the private key is included.
- Select the certificate and private key together, then choose File -> Export Items....
- Save as
DeveloperIDApplication.p12and set a strong export password. - Copy the base64 form into the GitHub secret:
base64 -i DeveloperIDApplication.p12 | tr -d '\n' | pbcopyPaste the clipboard into BUILD_CERTIFICATE_BASE64. Put the export password in BUILD_CERTIFICATE_PASSWORD.
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' | pbcopyStore the key ID in APP_STORE_CONNECT_KEY_ID and the issuer ID in APP_STORE_CONNECT_ISSUER_ID.
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_BASE64must be the base64 text of the downloaded.p8private key file, not the JSON key metadata.APP_STORE_CONNECT_KEY_IDmust match the key ID shown for that same.p8file.APP_STORE_CONNECT_ISSUER_IDmust 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"- Move changelog entries from
Unreleasedto a version heading:
## vX.Y.Z (YYYY-MM-DD)- Update
MARKETING_VERSIONandCURRENT_PROJECT_VERSIONinproject.yml. - Regenerate the project if you want the checked-in
.xcodeprojto reflect the new version:
xcodegen generate- Run tests:
xcodebuild test -project TaskMenu.xcodeproj -scheme TaskMenu \
-configuration Debug \
-destination "platform=macOS"- Commit and push to
main. - Create and push the release tag:
git tag -a vX.Y.Z -m "TaskMenu vX.Y.Z"
git push origin main vX.Y.ZThe release workflow validates that the tag version matches MARKETING_VERSION and that CHANGELOG.md has a matching section before it publishes anything.
- 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 pushYou 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.
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