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
36 changes: 36 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**.
Expand Down Expand Up @@ -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 |
Expand Down
106 changes: 106 additions & 0 deletions scripts/bump-cask.sh
Original file line number Diff line number Diff line change
@@ -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"