Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 44 additions & 37 deletions .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
name: Release Build

# Fires when release-please's PR is merged and it publishes a GitHub Release.
# Builds, signs and packages the DMG for a tag, attaches it to the matching
# GitHub Release, and asks maptic/homebrew-tap to bump its cask.
#
# Called by release-please.yml right after it cuts a release (a release created
# with the default GITHUB_TOKEN does not emit a `release: published` event), and
# runnable by hand for a tag that needs re-packaging.
on:
release:
types: [published]
workflow_call:
inputs:
tag:
description: Release tag to build, e.g. 1.2.3
required: true
type: string
workflow_dispatch:
inputs:
tag:
description: Release tag to build, e.g. 1.2.3
required: true
type: string

permissions:
contents: write

jobs:
build-dmg:
runs-on: macos-latest
outputs:
version: ${{ steps.ver.outputs.version }}
env:
TAG: ${{ github.event.release.tag_name }}
TAG: ${{ inputs.tag }}
# 'true' only when Developer-ID signing secrets are configured.
HAS_SIGNING: ${{ secrets.MACOS_CERTIFICATE != '' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}

- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

# Tags are bare semver ("1.2.3"); the strip only guards a hand-typed "v".
- name: Derive version
id: ver
run: echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -101,40 +121,27 @@ jobs:
gh release upload "$TAG" \
"Mounty-$VERSION.dmg" "Mounty-$VERSION.dmg.sha256" --clobber

# -------- Homebrew cask --------
- name: Update Homebrew cask
# -------- Homebrew cask --------
# The tap owns the cask: it renders the download URL, re-computes the sha256
# from the published asset and commits the bump itself. Requires
# HOMEBREW_TAP_TOKEN — a fine-grained PAT with `contents: write` on
# maptic/homebrew-tap. Without it the release still succeeds, only unbumped.
update-tap:
needs: build-dmg
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Dispatch cask-release to maptic/homebrew-tap
env:
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
VERSION: ${{ steps.ver.outputs.version }}
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
VERSION: ${{ needs.build-dmg.outputs.version }}
run: |
if [ -z "${TAP_TOKEN:-}" ]; then
echo "HOMEBREW_TAP_TOKEN not set; skipping cask update."
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "::warning::HOMEBREW_TAP_TOKEN not set; skipping the Homebrew cask bump."
exit 0
fi
SHA=$(shasum -a 256 "Mounty-$VERSION.dmg" | awk '{print $1}')
git clone "https://x-access-token:${TAP_TOKEN}@github.com/maptic/homebrew-tap.git" tap
mkdir -p tap/Casks
cat > tap/Casks/mounty.rb <<EOF
cask "mounty" do
version "$VERSION"
sha256 "$SHA"

url "https://github.com/maptic/mounty/releases/download/v#{version}/Mounty-#{version}.dmg"
name "Mounty"
desc "Menu-bar app that keeps SMB network shares mounted automatically"
homepage "https://github.com/maptic/mounty"

depends_on macos: ">= :tahoe"

app "Mounty.app"

zap trash: "~/Library/Preferences/ch.maptic.Mounty.plist"
end
EOF

cd tap
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Casks/mounty.rb
git commit -m "chore: update mounty to $VERSION"
git push
jq -n --arg version "$VERSION" \
'{event_type: "cask-release", client_payload: {cask: "mounty", version: $version}}' \
| gh api repos/maptic/homebrew-tap/dispatches --input -
echo "::notice::Requested cask bump to $VERSION in maptic/homebrew-tap."
20 changes: 19 additions & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,28 @@ permissions:
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
version: ${{ steps.release.outputs.version }}
steps:
# Opens/updates a release PR based on Conventional Commits. Merging that PR
# creates the git tag + GitHub Release, which triggers release-build.yml.
# creates the git tag + GitHub Release.
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# A release created with the default GITHUB_TOKEN does NOT emit a
# `release: published` event, so the build is chained explicitly here instead
# of relying on that trigger.
release-build:
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
permissions:
contents: write
uses: ./.github/workflows/release-build.yml
with:
tag: ${{ needs.release-please.outputs.tag_name }}
secrets: inherit
22 changes: 22 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,28 @@ Versioning and releases are **fully automated** by `release-please` from commit
`feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE:` → major; `docs/chore/refactor/test/ci`
→ no release.

### Release pipeline

```
push to main → release-please PR → merge → tag X.Y.Z + GitHub Release
├─ release-build.yml: DMG (+ notarization) → release assets
└─ repository_dispatch "cask-release" → maptic/homebrew-tap
bumps Casks/mounty.rb
```

`release-build.yml` is **called by** `release-please.yml`, not triggered by the `release: published`
event — a release created with the default `GITHUB_TOKEN` does not emit that event. To re-package a
tag by hand: `gh workflow run release-build.yml -f tag=X.Y.Z`.

Tags are bare semver — `1.2.3`, not `v1.2.3` or `mounty-v1.2.3`. The repository holds one app, so a
component prefix carries no information and only makes release URLs redundant.

The cask bump needs `HOMEBREW_TAP_TOKEN` (a fine-grained PAT with `contents: write` on
`maptic/homebrew-tap`) in this repository's secrets; without it the release still succeeds and only
the tap bump is skipped. The tap re-computes the `sha256` from the published asset itself, so the
dispatch payload carries just the cask token and the version.

### Mandatory model attribution for agent commits

Any commit you create as an AI agent MUST include a `Generated-by:` git trailer naming the exact
Expand Down
28 changes: 28 additions & 0 deletions docs/stories/024-homebrew-tap-distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# STORY-024: Homebrew tap distribution

- Status: IN_PROGRESS
- Type: ci
- Date: 2026-08-10
- Commit: _none_

## Intent

Ship Mounty through `brew install --cask maptic/tap/mounty` and keep the cask current without manual
steps: cutting a release must build the DMG, attach it to the GitHub Release, and bump the cask in
`maptic/homebrew-tap`.

## Acceptance criteria

- [ ] Merging the release-please PR builds and attaches the DMG — previously the release build never
ran, because a release created with the default `GITHUB_TOKEN` emits no `release: published`
event.
- [ ] Release tags are bare semver (`1.2.1`), matching the download URL the cask interpolates, and
the existing `mounty-v1.2.0` tag and release are renamed to that scheme.
- [ ] The release build asks `maptic/homebrew-tap` to bump the cask via a `cask-release`
`repository_dispatch`, and skips that step (without failing) when `HOMEBREW_TAP_TOKEN` is absent.
- [ ] `release-build.yml` can be re-run by hand for an existing tag.

## Validation

Record the release run that produced the DMG assets and the resulting cask bump commit in
`maptic/homebrew-tap`.
1 change: 1 addition & 0 deletions docs/stories/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Newest stories first. Statuses: `OPEN`, `IN_PROGRESS`, `CLOSED`.

| ID | Type | Story | Status | Date |
| --- | --- | --- | --- | --- |
| [024](./024-homebrew-tap-distribution.md) | ci | Homebrew tap distribution | IN_PROGRESS | 2026-08-10 |
| [023](./023-readme-logo-visual-alignment.md) | fix | README logo visual alignment | CLOSED | 2026-08-10 |
| [022](./022-documentation-source-artwork.md) | docs | Documentation source artwork | CLOSED | 2026-08-10 |
| [021](./021-readme-logo-alignment.md) | fix | README logo alignment | CLOSED | 2026-08-10 |
Expand Down
3 changes: 2 additions & 1 deletion release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"bump-minor-pre-major": false,
"draft": false,
"prerelease": false,
"include-v-in-tag": true,
"include-v-in-tag": false,
"include-component-in-tag": false,
"extra-files": [
"version.txt"
]
Expand Down
Loading