Homebrew release flow

June 15, 2026 · View on GitHub

Nehir is published through the shared tap:

Required repository setup

The normal release flow is fully automated by .github/workflows/release.yml and is started manually from GitHub Actions.

The workflow needs write access to Guria/homebrew-tap. Nehir uses a GitHub App instead of a personal access token.

Configure these repository secrets in Guria/nehir:

  • TAP_APP_ID: numeric GitHub App ID.
  • TAP_APP_PRIVATE_KEY: full private key PEM for the GitHub App.

The GitHub App should be installed only on Guria/homebrew-tap and needs repository permissions:

  • Contents: Read and write
  • Metadata: Read-only

Also ensure Guria/nehir has Actions workflow permissions set to Read and write permissions so the workflow can commit release prep changes, create tags, and publish GitHub Releases.

For public Homebrew installs, the release asset URL must be publicly downloadable. That means the Nehir source repository should be public, or the release ZIP must be hosted somewhere public with corresponding GPL source access provided to recipients.

Changesets

Every user-visible change should add a pending changeset:

mise run changeset -- patch "Fixed window restoration after display changes."
mise run changeset -- minor "Added a new workspace overview command."
mise run changeset -- major "Changed configuration format incompatibly."

Changesets use the official Changesets frontmatter shape:

---
"nehir": patch
contributors: [github-handle]
---

Short user-facing description of the change.

Use optional contributors: [...] frontmatter for reports or contributions; release notes render those handles as plain @github-handle mentions in the entry thanks line so GitHub can populate its native release Contributors footer.

Release notes preserve structured Markdown in changeset bodies. A summary followed by bullets renders as one top-level change with nested details.

Breaking changes render in a separate Breaking changes section when the bump is major or the body starts with BREAKING: / BREAKING CHANGE:. Keep the first line short, put migration details in follow-up paragraphs or nested bullets, and use a separate changeset for unrelated non-breaking fixes.

Supported bump types are:

  • patch: bug fixes and small improvements.
  • minor: new user-facing functionality.
  • major: incompatible changes.
  • none: internal note; does not create a versioned release by itself.

CI enforces changeset coverage for source/user-visible changes. If a PR truly has no release-note impact, apply the no release note label.

Normal release steps

Do not manually bump Info.plist, generate release notes, create a version tag, or update the Homebrew tap for normal releases.

Instead:

  1. Ensure pending changesets are committed on main.
  2. Open Guria/nehirActionsRelease.
  3. Click Run workflow on main.
  4. Leave prerelease inputs empty/disabled for a stable release.

The workflow will:

  1. Read pending .changeset/*.md files.
  2. Calculate the next app version from the latest stable semantic version tag and the highest pending bump type, falling back to Info.plist only when no stable tag exists.
  3. Stamp CFBundleShortVersionString in the workflow working tree only.
  4. Build, sign, and notarize dist/Nehir-X.Y.Z.zip.
  5. Create tag vX.Y.Z on the source commit after the signed/notarized build succeeds.
  6. Create the GitHub Release with notes rendered from the pending changesets.
  7. Compute the release ZIP SHA-256.
  8. Update Guria/homebrew-tap/Casks/nehir.rb with the new version and checksum.
  9. Commit and push the tap update.
  10. Clear consumed pending changesets from main after publishing succeeds.

If any publishing step fails before changeset cleanup, pending changesets remain in .changeset/ so the release can be retried safely. Release tags are created only after the signed and notarized build succeeds. Info.plist is not committed during release prep; it is stamped in the workflow workspace only, so failed runs do not advance the source version.

The workflow uploads a short-lived notarization resume artifact immediately after submitting to Apple. If the wait step times out but the submission later completes, rerun the workflow with resume_notary_run_id set to the previous workflow run ID. The rerun downloads the exact submitted Nehir-notary.zip, waits on the existing Apple submission ID from the artifact, staples it, and continues publishing without creating a new notarization submission.

Prereleases

Prereleases are also published to the RC cask in the Homebrew tap. Users can install the current preview build with:

brew install --cask guria/tap/nehir@rc

To publish a GitHub prerelease and update nehir@rc:

  1. Open Guria/nehirActionsRelease.
  2. Click Run workflow on main.
  3. Enable prerelease.

The workflow auto-selects the next rc.N suffix for the calculated base version, tags the release as vX.Y.Z-rc.N, creates dist/Nehir-X.Y.Z-rc.N.zip, signs and notarizes the app, marks the GitHub Release as a prerelease with notes showing only changes since the last RC, and updates Guria/homebrew-tap/Casks/nehir@rc.rb. It skips changeset cleanup so the same pending changesets remain available for the next stable release.

Mise file tasks

Release helper commands are exposed as mise file tasks under .config/mise/tasks/ so the task implementation remains ordinary shell script with editor syntax highlighting.

Useful local checks

Preview the calculated next stable release:

mise run release:plan

Preview the calculated next prerelease with an auto-selected rc.N suffix:

PRERELEASE=true mise run release:plan

Preview a prerelease with an explicit suffix:

PRERELEASE=true PRERELEASE_SUFFIX=beta.1 mise run release:plan

Create a changeset:

mise run changeset -- patch "Fixed window restoration after display changes."

Run the changeset coverage check:

mise run changeset:check

Render the release notes to stdout:

mise run release:notes -- 0.2.2

Run tests:

mise run test

Apple Developer ID signing and notarization

Releases are signed with a Developer ID Application certificate and notarized through Apple's notary service. The release workflow handles signing and notarization automatically.

Required repository secrets

Configure these secrets in Guria/nehir → Settings → Secrets and variables → Actions:

SecretDescription
APPLE_DEVELOPER_ID_CERT_P12_BASE64Base64-encoded Developer ID Application .p12 certificate
APPLE_DEVELOPER_ID_CERT_PASSWORDPassword used when exporting the .p12 from Keychain
APPLE_SIGNING_IDENTITYFull codesigning identity string, e.g. Developer ID Application: Name (TEAMID)
APPLE_NOTARY_KEY_IDApp Store Connect API key ID
APPLE_NOTARY_ISSUER_IDApp Store Connect API issuer ID
APPLE_NOTARY_KEY_P8Full contents of the App Store Connect API .p8 private key

The certificate is exported from Keychain Access as a .p12 (Personal Information Exchange) containing the certificate and private key. The API key is created in App Store Connect → Users and Access → Integrations → App Store Connect API.