From 1ccad3b992d6a70b0f626a9e9254693fe2ef4f55 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Thu, 8 Oct 2026 01:07:55 -0700 Subject: [PATCH 1/5] ci: update the Homebrew tap on each release --- .github/workflows/release.yml | 39 +++++++++++++++++++++++++++++++++++ README.md | 13 +++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 516135b..e7f3b80 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -337,3 +337,42 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./.github/release/node_modules/.bin/semantic-release + + homebrew-tap: + needs: + - version + - release + if: needs.version.outputs.new-release-version != '' + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout tap + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + repository: MegaManSec/homebrew-tap + ssh-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }} + + - name: Update cask + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.version.outputs.new-release-version }} + run: | + CASK=Casks/magic-switch.rb + gh release download "v$VERSION" --repo "$GITHUB_REPOSITORY" --pattern app.zip --dir "$RUNNER_TEMP" + SHA256="$(sha256sum "$RUNNER_TEMP/app.zip" | cut -d' ' -f1)" + sed -i -E \ + -e "s/^ version \".*\"$/ version \"$VERSION\"/" \ + -e "s/^ sha256 \".*\"$/ sha256 \"$SHA256\"/" \ + "$CASK" + grep -qx " version \"$VERSION\"" "$CASK" + grep -qx " sha256 \"$SHA256\"" "$CASK" + if git diff --quiet; then + echo "Cask already at $VERSION" + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -am "magic-switch $VERSION" + git push diff --git a/README.md b/README.md index dff343d..6189d0c 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,13 @@ This is a security-hardened fork of [HoshimuraYuto/blue-switch](https://github.c ## Installation -1. Grab the latest build from the [releases page](https://github.com/MegaManSec/magic-switch/releases). -2. Unzip and move `Magic Switch.app` to `/Applications`. -3. Approve **Bluetooth** and **Local Network** access when macOS prompts. Both are required — Bluetooth to control the peripherals, Local Network to discover and talk to the other Mac. If you dismiss the prompts, grant them later under System Settings → Privacy & Security. -4. Allow **Notifications** when asked. Not strictly required, but it's how Magic Switch reports what happened when no window is open — a switch triggered by hotkey, URL scheme, or dock-on-display that fails does so *silently* without it (see [Troubleshooting](#troubleshooting)). Denied it once? Re-enable under System Settings → Notifications → Magic Switch. +1. Install with [Homebrew](https://brew.sh): + ```sh + brew install --cask megamansec/tap/magic-switch + ``` + Or grab the latest build from the [releases page](https://github.com/MegaManSec/magic-switch/releases), unzip it, and move `Magic Switch.app` to `/Applications`. +2. Approve **Bluetooth** and **Local Network** access when macOS prompts. Both are required — Bluetooth to control the peripherals, Local Network to discover and talk to the other Mac. If you dismiss the prompts, grant them later under System Settings → Privacy & Security. +3. Allow **Notifications** when asked. Not strictly required, but it's how Magic Switch reports what happened when no window is open — a switch triggered by hotkey, URL scheme, or dock-on-display that fails does so *silently* without it (see [Troubleshooting](#troubleshooting)). Denied it once? Re-enable under System Settings → Notifications → Magic Switch. ## Setup @@ -124,7 +127,7 @@ Run the command on the Mac that should act. `direction=take` works even while th ## Updates -Magic Switch tells you when there's a new version — it never updates itself. About once a day it makes a single anonymous request to GitHub's public releases API for [this repo](https://github.com/MegaManSec/magic-switch/releases) and compares your installed version with the latest published release; no account, sign-in, or telemetry is involved. When a newer version exists, an **Update Available** notice (with the new version number) appears at the top of the right-click menu and in **Settings → Other** — clicking it opens the release page so you can download and install it yourself. The first automatic check that spots a given version also posts a single system notification — click it to open the download page. It's posted once per version, so it won't nag (and only if notifications are allowed, see [Troubleshooting](#troubleshooting)). A failed check (offline, rate-limited, etc.) is retried about hourly; otherwise checks happen at most once every 24 hours. Your installed version is always shown in **Settings → Other**. +Magic Switch tells you when there's a new version — it never updates itself. About once a day it makes a single anonymous request to GitHub's public releases API for [this repo](https://github.com/MegaManSec/magic-switch/releases) and compares your installed version with the latest published release; no account, sign-in, or telemetry is involved. When a newer version exists, an **Update Available** notice (with the new version number) appears at the top of the right-click menu and in **Settings → Other** — clicking it opens the release page so you can download and install it yourself (or run `brew upgrade --cask magic-switch` if you installed with Homebrew). The first automatic check that spots a given version also posts a single system notification — click it to open the download page. It's posted once per version, so it won't nag (and only if notifications are allowed, see [Troubleshooting](#troubleshooting)). A failed check (offline, rate-limited, etc.) is retried about hourly; otherwise checks happen at most once every 24 hours. Your installed version is always shown in **Settings → Other**. ## Troubleshooting From f7a31e3846646fc5ef9a62fab3dc5c3146f86d77 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Thu, 8 Oct 2026 01:20:43 -0700 Subject: [PATCH 2/5] ci: verify the release attestation and skip downgrades when updating the tap --- .github/workflows/release.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7f3b80..8507d88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -346,6 +346,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + attestations: read steps: - name: Checkout tap @@ -360,18 +361,25 @@ jobs: VERSION: ${{ needs.version.outputs.new-release-version }} run: | CASK=Casks/magic-switch.rb - gh release download "v$VERSION" --repo "$GITHUB_REPOSITORY" --pattern app.zip --dir "$RUNNER_TEMP" - SHA256="$(sha256sum "$RUNNER_TEMP/app.zip" | cut -d' ' -f1)" + CURRENT="$(sed -nE 's/^ version "(.*)"$/\1/p' "$CASK")" + if [ "$(printf '%s\n' "$CURRENT" "$VERSION" | sort -V | tail -n1)" = "$CURRENT" ]; then + echo "Cask already at $CURRENT" + exit 0 + fi + ZIP="$RUNNER_TEMP/app.zip" + gh release download "v$VERSION" --repo "$GITHUB_REPOSITORY" --pattern app.zip --output "$ZIP" + gh attestation verify "$ZIP" \ + --repo "$GITHUB_REPOSITORY" \ + --signer-workflow "$GITHUB_REPOSITORY/.github/workflows/release.yml" \ + --source-ref refs/heads/main \ + --deny-self-hosted-runners + SHA256="$(sha256sum "$ZIP" | cut -d' ' -f1)" sed -i -E \ -e "s/^ version \".*\"$/ version \"$VERSION\"/" \ -e "s/^ sha256 \".*\"$/ sha256 \"$SHA256\"/" \ "$CASK" grep -qx " version \"$VERSION\"" "$CASK" grep -qx " sha256 \"$SHA256\"" "$CASK" - if git diff --quiet; then - echo "Cask already at $VERSION" - exit 0 - fi git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git commit -am "magic-switch $VERSION" From 66ea06a006ac8f86bd7fb3e6455f78865aca31f2 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Thu, 8 Oct 2026 01:25:40 -0700 Subject: [PATCH 3/5] ci: read the tap deploy key from the main-only homebrew-tap environment --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8507d88..ea41f58 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -344,6 +344,7 @@ jobs: - release if: needs.version.outputs.new-release-version != '' runs-on: ubuntu-latest + environment: homebrew-tap permissions: contents: read attestations: read From 16c93020d7e47baafca95dd47eb7364540dc4cc0 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Thu, 8 Oct 2026 01:25:40 -0700 Subject: [PATCH 4/5] docs: list Homebrew first under Installation and add an author line --- README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6189d0c..699a631 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,21 @@ This is a security-hardened fork of [HoshimuraYuto/blue-switch](https://github.c ## Installation -1. Install with [Homebrew](https://brew.sh): - ```sh - brew install --cask megamansec/tap/magic-switch - ``` - Or grab the latest build from the [releases page](https://github.com/MegaManSec/magic-switch/releases), unzip it, and move `Magic Switch.app` to `/Applications`. -2. Approve **Bluetooth** and **Local Network** access when macOS prompts. Both are required — Bluetooth to control the peripherals, Local Network to discover and talk to the other Mac. If you dismiss the prompts, grant them later under System Settings → Privacy & Security. -3. Allow **Notifications** when asked. Not strictly required, but it's how Magic Switch reports what happened when no window is open — a switch triggered by hotkey, URL scheme, or dock-on-display that fails does so *silently* without it (see [Troubleshooting](#troubleshooting)). Denied it once? Re-enable under System Settings → Notifications → Magic Switch. +With [Homebrew](https://brew.sh): + +```bash +brew install --cask megamansec/tap/magic-switch +``` + +Or manually: + +1. Grab the latest build from the [releases page](https://github.com/MegaManSec/magic-switch/releases). +2. Unzip and move `Magic Switch.app` to `/Applications`. + +Then: + +3. Approve **Bluetooth** and **Local Network** access when macOS prompts. Both are required — Bluetooth to control the peripherals, Local Network to discover and talk to the other Mac. If you dismiss the prompts, grant them later under System Settings → Privacy & Security. +4. Allow **Notifications** when asked. Not strictly required, but it's how Magic Switch reports what happened when no window is open — a switch triggered by hotkey, URL scheme, or dock-on-display that fails does so *silently* without it (see [Troubleshooting](#troubleshooting)). Denied it once? Re-enable under System Settings → Notifications → Magic Switch. ## Setup @@ -127,7 +135,7 @@ Run the command on the Mac that should act. `direction=take` works even while th ## Updates -Magic Switch tells you when there's a new version — it never updates itself. About once a day it makes a single anonymous request to GitHub's public releases API for [this repo](https://github.com/MegaManSec/magic-switch/releases) and compares your installed version with the latest published release; no account, sign-in, or telemetry is involved. When a newer version exists, an **Update Available** notice (with the new version number) appears at the top of the right-click menu and in **Settings → Other** — clicking it opens the release page so you can download and install it yourself (or run `brew upgrade --cask magic-switch` if you installed with Homebrew). The first automatic check that spots a given version also posts a single system notification — click it to open the download page. It's posted once per version, so it won't nag (and only if notifications are allowed, see [Troubleshooting](#troubleshooting)). A failed check (offline, rate-limited, etc.) is retried about hourly; otherwise checks happen at most once every 24 hours. Your installed version is always shown in **Settings → Other**. +Magic Switch tells you when there's a new version — it never updates itself. About once a day it makes a single anonymous request to GitHub's public releases API for [this repo](https://github.com/MegaManSec/magic-switch/releases) and compares your installed version with the latest published release; no account, sign-in, or telemetry is involved. When a newer version exists, an **Update Available** notice (with the new version number) appears at the top of the right-click menu and in **Settings → Other** — clicking it opens the release page so you can download and install it yourself. The first automatic check that spots a given version also posts a single system notification — click it to open the download page. It's posted once per version, so it won't nag (and only if notifications are allowed, see [Troubleshooting](#troubleshooting)). A failed check (offline, rate-limited, etc.) is retried about hourly; otherwise checks happen at most once every 24 hours. Your installed version is always shown in **Settings → Other**. ## Troubleshooting @@ -204,6 +212,8 @@ Known limits: ## Support +Magic Switch is made by me, Joshua Rogers. I write about it and my other projects on my blog, [joshua.hu](https://joshua.hu/). + If Magic Switch is useful to you, you can support its development by [sponsoring me on GitHub](https://github.com/sponsors/MegaManSec). ## License From 080ae68f7a977918091476339bca1ab6bff89209 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Thu, 8 Oct 2026 01:31:38 -0700 Subject: [PATCH 5/5] ci: run the tap update with pipefail --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea41f58..eae6c2b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -357,6 +357,7 @@ jobs: ssh-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }} - name: Update cask + shell: bash env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} VERSION: ${{ needs.version.outputs.new-release-version }}