diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index ec8602c..a9321da 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -189,3 +189,111 @@ jobs: gh release upload "$TAG" \ "$ARCHIVE" "${ARCHIVE}.sha256" \ "$ALIAS" "${ALIAS}.sha256" --clobber + + # Regenerate the Homebrew formula from the archives just uploaded. + # + # Generated rather than hand-edited, for the reason the tap's README gives: + # a hand-edit is overwritten by the next release, silently. The checksums + # come from the `.sha256` files the matrix produced, so the formula cannot + # describe an archive that was never built. + # + # Best-effort, like the release notification. It needs a token with write + # access to the tap, and without one it says so and exits clean -- a formula + # one version behind is a smaller problem than a release that fails after + # the crate is already on crates.io. + formula: + name: homebrew formula + needs: binaries + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Generate and push + env: + GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + TAG: ${{ inputs.tag || github.event.release.tag_name }} + run: | + set -euo pipefail + if [ -z "${GH_TOKEN:-}" ]; then + echo "::notice::HOMEBREW_TAP_TOKEN is not set; the tap keeps its current formula" + exit 0 + fi + version="${TAG#v}" + base="https://github.com/vyncint/mossaic/releases/download/${TAG}" + + # One checksum per platform the formula serves. Windows has no + # Homebrew, so it is not one of them. + declare -A sums + for target in aarch64-apple-darwin x86_64-apple-darwin \ + aarch64-unknown-linux-musl x86_64-unknown-linux-musl; do + url="${base}/mossaic-${version}-${target}.tar.gz.sha256" + sum="$(curl -fsSL "$url" | awk '{print $1}')" + if [ -z "$sum" ]; then + echo "::error::no checksum for ${target}; the archive did not upload" + exit 1 + fi + sums[$target]="$sum" + done + + block() { + printf ' url "%s/mossaic-%s-%s.tar.gz"\n sha256 "%s"\n' \ + "$base" "$version" "$1" "${sums[$1]}" + } + + mkdir -p tap/Formula + { + echo "# This file is generated by mossaic's release workflow. DO NOT EDIT." + echo "# https://github.com/vyncint/mossaic/blob/main/.github/workflows/binaries.yml" + echo "class Mossaic < Formula" + echo ' desc "GitHub'"'"'s contribution chart in your terminal, and a planner for contribution art"' + echo ' homepage "https://github.com/vyncint/mossaic"' + echo ' license any_of: ["MIT", "Apache-2.0"]' + echo + echo " livecheck do" + echo ' skip "Auto-generated on release."' + echo " end" + echo + echo " on_macos do" + echo " on_arm do"; block aarch64-apple-darwin; echo " end" + echo " on_intel do"; block x86_64-apple-darwin; echo " end" + echo " end" + echo + echo " on_linux do" + echo " on_arm do"; block aarch64-unknown-linux-musl; echo " end" + echo " on_intel do"; block x86_64-unknown-linux-musl; echo " end" + echo " end" + echo + echo " def install" + echo ' bin.install "mossaic", "mossaic-art", "mossaic-glyphs"' + echo ' doc.install "README.md", "CHANGELOG.md"' + echo " # The templates are compiled into the binary; this copy is what" + echo " # somebody starts from when drawing their own." + echo ' pkgshare.install "templates"' + echo " end" + echo + echo " test do" + echo ' assert_match version.to_s, shell_output("#{bin}/mossaic-art --version")' + echo " # The built-in catalogue survived packaging, which is the one thing" + echo " # about this binary that a plain --version cannot show." + echo ' assert_match "dragon", shell_output("#{bin}/mossaic-art --list-templates")' + echo " # And it draws: no account, no network, no terminal." + echo ' assert_match "2027", shell_output("#{bin}/mossaic-art --template dragon --year 2027 --no-color --plan /dev/null")' + echo " end" + echo "end" + } > tap/Formula/mossaic.rb + + cd tap + git init -q + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/vyncint/homebrew-tap.git" + git fetch -q origin main + git reset -q --soft FETCH_HEAD + git add Formula/mossaic.rb + if git diff --cached --quiet; then + echo "the formula already describes ${TAG}" + exit 0 + fi + git commit -q -s -m "chore: mossaic ${version}" + git push -q origin HEAD:main + echo "tap updated to ${version}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f69f3..ccbca43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,17 @@ listed under a **Changed** or **Removed** heading. ## [Unreleased] +### Added + +- **`brew install vyncint/tap/mossaic`.** The formula is generated by the + release itself from the checksums the archives produced, so it cannot + describe an archive that was never built, and a hand-edit in the tap is + overwritten by the next release rather than quietly kept. + + Verified by installing it: `brew audit --strict` is clean, `brew test` + passes, and the three binaries plus a copy of the art templates land where + the formula says. + ### Fixed - **The Intel-Mac binary is cross-compiled from Apple silicon** rather than @@ -26,11 +37,10 @@ listed under a **Changed** or **Removed** heading. the host's architecture would pass every other check here and fail on the first machine that downloaded it. -- **The README no longer promises Homebrew.** The line went in ahead of the - tap existing, on the assumption that the tap would follow within the hour. - It has not yet, and an install command that does not work is worse than one - that is not offered. The three routes documented are the three that are - verified against the real release. +- **The README briefly promised Homebrew before the tap existed**, and said + so for an hour or two until it did. An install command that does not work + is worse than one that is not offered; every route the README lists is now + one that has been run. - **The Windows binary is built.** The 0.6.2 release shipped Linux and macOS archives and no `.zip`: the build step had no `shell: bash`, and Windows diff --git a/README.md b/README.md index 6b12ae6..8d5419e 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,13 @@ labels around them stay text. ## Quickstart **No Rust needed.** Every release ships static binaries for Linux, macOS and -Windows. One command, no toolchain: +Windows, on Intel and arm64 alike. + +```sh +brew install vyncint/tap/mossaic +``` + +Or download one — still no toolchain: ```sh target=x86_64-unknown-linux-musl # or aarch64-unknown-linux-musl, @@ -461,7 +467,18 @@ palette is degraded on purpose, and what the capability probe actually sends. ## Install -Three ways, and only the last needs a Rust toolchain. +Four ways, and only the last needs a Rust toolchain. + +### Homebrew — macOS and Linux + +```sh +brew install vyncint/tap/mossaic +``` + +Installs the three binaries and a copy of the built-in art templates under +`$(brew --prefix)/share/mossaic/templates`, to start your own from. The +formula is generated by the release itself, so it never describes an archive +that was not built. ### A downloaded binary