From 6db1876137b493abc5e912ac9718fef18aae0e84 Mon Sep 17 00:00:00 2001 From: Periicles Date: Mon, 31 Aug 2026 10:01:55 +0200 Subject: [PATCH 1/3] build(scripts): add a cask bump script for the Homebrew tap Hashes the .dmg attached to a release, rewrites version and sha256 in the tap's cask, then opens an auto-merging pull request. --dry-run stops after the rewrite so the substitution can be rehearsed against a tap checkout. --- scripts/bump-cask.sh | 106 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 scripts/bump-cask.sh diff --git a/scripts/bump-cask.sh b/scripts/bump-cask.sh new file mode 100755 index 0000000..aaadc11 --- /dev/null +++ b/scripts/bump-cask.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Bumps the notchbar cask in the Homebrew tap to a published release, as an +# auto-merging pull request. +# +# Usage: bump-cask.sh VERSION TAP_DIR [--dry-run] +# VERSION the released version, without the leading v (e.g. 0.4.0) +# TAP_DIR a checkout of Periicles/homebrew-tap +# --dry-run rewrite the cask and print the diff, then stop before any push +# +# The sha256 is computed from the .dmg attached to the GitHub release, not from +# a local build: it has to match the exact bytes Homebrew will download. +# +# Pushing needs a token with contents, pull-requests and issues write access on +# the tap (CI passes TAP_TOKEN); `gh` reads it from GH_TOKEN. +set -euo pipefail + +TAP_REPO="Periicles/homebrew-tap" +CASK_PATH="Casks/notchbar.rb" +DMG_URL_BASE="https://github.com/Periicles/Notchapp/releases/download" + +VERSION="${1:-}" +TAP_DIR="${2:-}" +DRY_RUN="" +[ "${3:-}" = "--dry-run" ] && DRY_RUN=1 + +if [ -z "$VERSION" ] || [ -z "$TAP_DIR" ]; then + sed -n '2,12p' "$0" >&2 + exit 2 +fi +if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "error: '$VERSION' is not a version like 0.4.0" >&2 + exit 2 +fi + +CASK="$TAP_DIR/$CASK_PATH" +test -f "$CASK" || { echo "error: $CASK not found — is $TAP_DIR a tap checkout?" >&2; exit 2; } + +sha256_of() { + if command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$1" | awk '{print $1}' + else + sha256sum "$1" | awk '{print $1}' + fi +} + +DMG_URL="$DMG_URL_BASE/v$VERSION/NotchBar.dmg" +DMG="$(mktemp -t notchbar-dmg)" +trap 'rm -f "$DMG"' EXIT + +echo "==> Downloading $DMG_URL" +curl -fsSL -o "$DMG" "$DMG_URL" +SHA="$(sha256_of "$DMG")" +echo "==> sha256 $SHA" + +CURRENT_VERSION="$(sed -n 's/^ version "\(.*\)"$/\1/p' "$CASK")" +CURRENT_SHA="$(sed -n 's/^ sha256 "\(.*\)"$/\1/p' "$CASK")" +if [ "$CURRENT_VERSION" = "$VERSION" ] && [ "$CURRENT_SHA" = "$SHA" ]; then + echo "==> Cask is already on $VERSION with a matching sha256 — nothing to do" + exit 0 +fi + +echo "==> Rewriting the cask: $CURRENT_VERSION -> $VERSION" +sed -i.bak \ + -e "s|^ version \".*\"$| version \"$VERSION\"|" \ + -e "s|^ sha256 \".*\"$| sha256 \"$SHA\"|" \ + "$CASK" +rm -f "$CASK.bak" + +# A silent no-op sed here would open an empty PR that auto-merges into nothing. +grep -q "^ version \"$VERSION\"$" "$CASK" || { echo "error: version line not rewritten" >&2; exit 1; } +grep -q "^ sha256 \"$SHA\"$" "$CASK" || { echo "error: sha256 line not rewritten" >&2; exit 1; } + +git -C "$TAP_DIR" --no-pager diff -- "$CASK_PATH" + +if [ -n "$DRY_RUN" ]; then + echo "==> Dry run: stopping before commit" + exit 0 +fi + +BRANCH="chore/notchbar-$VERSION" +echo "==> Pushing $BRANCH to $TAP_REPO" +git -C "$TAP_DIR" config user.name "github-actions[bot]" +git -C "$TAP_DIR" config user.email "41898282+github-actions[bot]@users.noreply.github.com" +git -C "$TAP_DIR" checkout -B "$BRANCH" +git -C "$TAP_DIR" add "$CASK_PATH" +git -C "$TAP_DIR" commit -m "chore(cask): notchbar $VERSION" +# Force-push so re-running a failed release job reuses the same branch and PR. +git -C "$TAP_DIR" push --force origin "HEAD:refs/heads/$BRANCH" + +PR_URL="$(gh pr list --repo "$TAP_REPO" --head "$BRANCH" --state open --json url --jq '.[0].url // empty')" +if [ -n "$PR_URL" ]; then + echo "==> Reusing the open PR $PR_URL" +else + echo "==> Opening the pull request" + PR_URL="$(gh pr create --repo "$TAP_REPO" --base main --head "$BRANCH" --assignee @me \ + --title "chore(cask): notchbar $VERSION" \ + --body "NotchBar $VERSION is published, so the cask has to follow — until it does, \`brew upgrade --cask notchbar\` is a no-op and Homebrew users stay on $CURRENT_VERSION. + +Version and sha256 come from the .dmg attached to the [v$VERSION release]($DMG_URL_BASE/v$VERSION/NotchBar.dmg), computed on the exact bytes Homebrew downloads. + +Verification: \`brew test-bot --only-tap-syntax\` on this PR, then \`brew upgrade --cask notchbar\`.")" +fi + +echo "==> Enabling auto-merge on $PR_URL" +gh pr merge --repo "$TAP_REPO" --auto --squash --delete-branch "$PR_URL" +echo "==> Done: $PR_URL" From 056b7e0b72c4ad46ee14af2d913cf0f07e591cd0 Mon Sep 17 00:00:00 2001 From: Periicles Date: Mon, 31 Aug 2026 10:01:55 +0200 Subject: [PATCH 2/3] ci(release): bump the Homebrew cask after publishing Until now the cask was bumped by hand, so `brew upgrade --cask notchbar` served the previous version for as long as that took. The job runs after the release exists, since the sha256 has to come from the published .dmg. The PR is authored with a PAT rather than GITHUB_TOKEN: a GITHUB_TOKEN PR does not trigger the tap's test-bot, which is the check auto-merge waits on. --- .github/workflows/release.yml | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44c8578..4155f17 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,8 @@ jobs: release: name: Build & publish release runs-on: macos-15 + outputs: + version: ${{ steps.version.outputs.version }} env: DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer steps: @@ -68,3 +70,37 @@ jobs: dist/NotchBar.dmg \ --title "NotchBar $GITHUB_REF_NAME" \ --generate-notes + + bump-cask: + name: Bump the Homebrew cask + needs: release + runs-on: ubuntu-latest + # Only needs to read this repo; every write goes to the tap through TAP_TOKEN. + permissions: + contents: read + steps: + - uses: actions/checkout@v5 + + - name: Require the tap token + env: + TAP_TOKEN: ${{ secrets.TAP_TOKEN }} + run: | + [ -n "$TAP_TOKEN" ] || { + echo "::error::TAP_TOKEN is unset. The cask bump needs a fine-grained PAT with contents, pull-requests and issues write access on Periicles/homebrew-tap." + exit 1 + } + + # The tap lives in its own repository, so GITHUB_TOKEN cannot write to it. + - name: Check out the tap + uses: actions/checkout@v5 + with: + repository: Periicles/homebrew-tap + token: ${{ secrets.TAP_TOKEN }} + path: tap + + # A PAT-authored PR triggers the tap's brew test-bot; a GITHUB_TOKEN one + # would not, and auto-merge would then have no check to wait for. + - name: Open the cask bump pull request + env: + GH_TOKEN: ${{ secrets.TAP_TOKEN }} + run: scripts/bump-cask.sh "${{ needs.release.outputs.version }}" tap From 14cec0e168093ab65cbd17a3947d7f45ee73ef12 Mon Sep 17 00:00:00 2001 From: Periicles Date: Mon, 31 Aug 2026 10:01:55 +0200 Subject: [PATCH 3/3] docs: document updating an install and the automatic cask bump --- CHANGELOG.md | 10 ++++++++++ README.md | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6fcf85..cad6bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to NotchBar are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and NotchBar adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- **Publishing a release now bumps the Homebrew cask on its own.** A `bump-cask` + job hashes the released `.dmg`, opens a pull request on `Periicles/homebrew-tap` + and turns on auto-merge, so it lands as soon as the tap's `brew test-bot` is + green. `brew upgrade --cask notchbar` no longer waits on a manual bump. +- README documents how to update an existing install, per install route. + ## [0.3.0] - 2026-08-15 ### Added diff --git a/README.md b/README.md index 7db229a..9ed18e3 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,18 @@ Nothing is piped into a shell — every step is visible above. `curl` does not s On first launch, grant Calendar access when prompted, then hover over the notch and click the settings icon to choose which calendars to track. +## Updating + +NotchBar has no network access and does not check for new versions on its own — take the route matching how you installed it: + +| Installed with | Update with | +|---|---| +| Homebrew | `brew upgrade --cask notchbar` | +| The one-command install | Quit NotchBar, then re-run the exact same command — it overwrites the copy in `/Applications` | +| The `.dmg` | Quit NotchBar, download the latest `.dmg` and drag the app over the old one | + +The cask is bumped automatically whenever a release is published, so Homebrew is the route that asks the least of you. Your settings and calendar selection survive an update — they live in the app's container, not in the bundle — but macOS may ask for Calendar access again after the app bundle is replaced. + ## Uninstall 1. If you turned on **Launch at login**, hover the notch → open settings → toggle it **off** first (this removes the login item cleanly). You can also remove it later under **System Settings → General → Login Items**. @@ -190,6 +202,14 @@ git push origin v0.3.0 The version comes from the tag (`vX.Y.Z` → `X.Y.Z`) and is injected into the app at build time — no need to edit `Info.plist`. The `CFBundleShortVersionString` checked into `Supporting/Info.plist` is only a placeholder for local `swift run` builds; every packaged build overwrites it. Releases are published as stable, which is what keeps `/releases/latest/download/NotchBar.dmg` resolving — that URL skips pre-releases. +Publishing a release also bumps the [Homebrew cask](https://github.com/Periicles/homebrew-tap). The `bump-cask` job computes the sha256 of the released `.dmg`, opens a pull request on the tap and turns on auto-merge, so the bump lands as soon as the tap's `brew test-bot` is green and `brew upgrade --cask notchbar` serves the new version with no manual step. It needs one repository secret: + +| Secret | For | +|---|---| +| `TAP_TOKEN` | fine-grained PAT with **contents**, **pull requests** and **issues** write access on `Periicles/homebrew-tap` | + +To rehearse the rewrite without pushing anything: `scripts/bump-cask.sh 0.3.0 "$(brew --repository periicles/tap)" --dry-run`. + **Notarization is not planned** — it needs a paid Apple Developer ID, and the command-line install routes already avoid the Gatekeeper prompt. Kept here in case that ever changes: add these repository secrets and follow the commented hooks in `release.yml` / `scripts/package.sh`. | Secret | For |