diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 04b4938..6958b6d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,31 @@ jobs: - run: brew test-bot --only-formulae if: github.event_name == 'pull_request' + # test-bot has no cask equivalent — `--only-formulae` reports + # `testing_formulae (none)` for a cask-only change, and the tap-syntax + # step above covers style and audit but never installs anything. So a + # wrong sha256, an unreachable url, or an `app` stanza naming a bundle + # that is not in the archive all pass CI and fail for every user. + # Install what changed, then uninstall it. + - name: Install and uninstall changed casks + if: github.event_name == 'pull_request' && runner.os == 'macOS' + run: | + tap="$(brew --repo drycodeworks/tap)" + git -C "$tap" fetch -q origin "${{ github.base_ref }}" + changed="$(git -C "$tap" diff --name-only \ + "origin/${{ github.base_ref }}...HEAD" -- Casks || true)" + if [[ -z "$changed" ]]; then + echo "No cask changes in this PR." + exit 0 + fi + while read -r path; do + [[ -n "$path" ]] || continue + cask="$(basename "$path" .rb)" + echo "==> Installing drycodeworks/tap/$cask" + brew install --cask "drycodeworks/tap/$cask" + brew uninstall --cask "drycodeworks/tap/$cask" + done <<< "$changed" + - name: Upload bottles as artifact if: always() && github.event_name == 'pull_request' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/Casks/tacet.rb b/Casks/tacet.rb new file mode 100644 index 0000000..08f75bc --- /dev/null +++ b/Casks/tacet.rb @@ -0,0 +1,60 @@ +cask "tacet" do + version "0.1.1" + sha256 "72fbbde28665aae8e2d18c73b711f2b3b7e2c80fb75a86b32e7804743a37f14e" + + url "https://github.com/DRYCodeWorks/tacet/releases/download/v#{version}/Tacet.zip", + verified: "github.com/DRYCodeWorks/tacet/" + name "Tacet" + desc "Push-to-talk dictation that pastes the transcript at the cursor" + homepage "https://github.com/DRYCodeWorks/tacet" + + livecheck do + url :url + strategy :github_latest + end + + depends_on macos: :ventura + + app "Tacet.app" + + # Deliberately NOT `depends_on formula: "whisper-cpp"`. One bundle plays two + # roles: the menu-bar agent, and `tacet serve`, which is the only part that + # needs whisper. Making it a hard dependency would build a large formula on + # every laptop that is only ever a client, talking to a server on another + # machine — the two-machine setup this is designed around. + uninstall launchctl: [ + "com.drycodeworks.tacet", + "com.drycodeworks.tacet-whisper", + ] + + zap trash: [ + "~/.config/tacet", + "~/Library/LaunchAgents/com.drycodeworks.tacet-whisper.plist", + "~/Library/LaunchAgents/com.drycodeworks.tacet.plist", + ] + + caveats do + <<~EOS + This cask installs the app bundle. Tacet still needs one-time setup to + write its config and load its launchd agents: + + Server (the machine that transcribes — needs `brew install whisper-cpp`): + ./install-server.sh from a clone of the repo + + Client (the machine you dictate from): + ./install-client.sh from a clone of the repo + + Both detect this cask and use the bundle Homebrew installed rather than + rebuilding one, so you do not end up with two copies and they will not + re-sign a notarized app. Run either with --doctor to diagnose a boundary + that is not working. + + Tacet needs Accessibility (to paste at the cursor) and Microphone + permission. macOS only offers these when the app asks, so grant them at + the first hotkey press — they cannot be pre-approved. + + Set `whisper.prompt` in ~/.config/tacet/config.toml to your own jargon. + It is empty by default and is the cheapest accuracy win available. + EOS + end +end diff --git a/README.md b/README.md index e978fed..39a31db 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,16 @@ Or `brew tap drycodeworks/tap` and then `brew install tailctl`. +Casks install the same way, with `--cask`: + +`brew install --cask drycodeworks/tap/tacet` + Or, in a `brew bundle` `Brewfile`: ```ruby tap "drycodeworks/tap" brew "tailctl" +cask "tacet" ``` ## Formulae @@ -18,9 +23,16 @@ brew "tailctl" - [`tailctl`](https://github.com/DRYCodeWorks/tailctl) — per-identity Tailscale networking for parallel sessions on a single Mac. -## Changing a formula +## Casks + +- [`tacet`](https://github.com/DRYCodeWorks/tacet) — push-to-talk dictation that + pastes the transcript at the cursor. Ships as a signed and notarized app, so + it is a cask rather than a formula: building from source would discard the + stapled ticket. -**Land formula changes via pull request, not a direct push to main.** +## Changing a formula or cask + +**Land changes via pull request, not a direct push to main.** `brew test-bot` picks which formulae to build by diffing `HEAD` against `origin/main`. On a push to main those are the same commit, so the diff is empty @@ -28,7 +40,12 @@ and CI reports green having built nothing (`testing_formulae (none)`). Only a PR gives it a base to diff against, and only then does CI actually install, test, and audit the formula. -Bumping a version: +Casks have the same requirement for a different reason: `test-bot` has no cask +step at all, so `--only-formulae` reports `testing_formulae (none)` for a +cask-only change and passes. CI installs changed casks in a separate step that +also diffs against the PR base. + +Bumping a formula version: ```bash brew livecheck drycodeworks/tap/ # is there a new release? @@ -38,6 +55,19 @@ brew test drycodeworks/tap/ brew audit --strict --online drycodeworks/tap/ ``` +Bumping a cask version: + +```bash +brew livecheck --cask drycodeworks/tap/ +# update version + sha256 (shasum -a 256 on the release asset) +brew install --cask drycodeworks/tap/ +brew audit --cask --online drycodeworks/tap/ +``` + +`brew audit --new` additionally requires the upstream repository be "notable" +(75+ stars). That gate is for submissions to homebrew-cask, not for this tap — +expect it to fail here and ignore it. + ## Documentation `brew help`, `man brew` or check [Homebrew's documentation](https://docs.brew.sh).