|
| 1 | +# Turicum Homebrew Packaging |
| 2 | + |
| 3 | +*Status: implemented and verified locally, 2026-07-17, for release 1.4.2. One |
| 4 | +publishing step remains open — pushing the tap repository to GitHub, see §5.* |
| 5 | + |
| 6 | +This document records how the Homebrew packaging of the Turicum CLI works, what |
| 7 | +was set up, what state exists on the development machine, and what to do on |
| 8 | +every release. The operational short version lives in `homebrew/README.md`; |
| 9 | +this is the full description. |
| 10 | + |
| 11 | +## 1. Design: what gets installed, from where |
| 12 | + |
| 13 | +The packaging deliberately builds **nothing**. Every Maven release already |
| 14 | +publishes a self-contained CLI distribution to Maven Central: |
| 15 | + |
| 16 | + https://repo1.maven.org/maven2/ch/turic/turicum-cli/<version>/turicum-cli-<version>-distribution.zip |
| 17 | + |
| 18 | +The zip is produced by `cli/src/main/assembly/bin.xml` (maven-assembly-plugin, |
| 19 | +`dist` execution) and contains exactly three flat jars: |
| 20 | + |
| 21 | +| Jar | Role | |
| 22 | +|---|---| |
| 23 | +| `turicum-cli-<version>.jar` | the CLI, entry point `ch.turic.cli.Main` | |
| 24 | +| `turicum-<version>.jar` | the core interpreter | |
| 25 | +| `jline-<version>.jar` | terminal handling for the REPL | |
| 26 | + |
| 27 | +Maven Central serves an official `.sha256` next to the artifact, which is what |
| 28 | +the formula pins. The Homebrew formula: |
| 29 | + |
| 30 | +1. downloads the zip (the formula uses the `search.maven.org/remotecontent` |
| 31 | + form of the URL because `brew audit` prefers it; it serves the identical |
| 32 | + file as `repo1.maven.org`); |
| 33 | +2. installs the three jars into the formula's `libexec` (so they are not on |
| 34 | + anyone's classpath by accident); |
| 35 | +3. writes a `turi` wrapper script into `bin`: |
| 36 | + |
| 37 | + ```bash |
| 38 | + #!/bin/bash |
| 39 | + exec "<openjdk>/bin/java" -cp "<libexec>/*" ch.turic.cli.Main "$@" |
| 40 | + ``` |
| 41 | + |
| 42 | + The explicit `-cp` glob is needed because the cli jar has no `Main-Class` |
| 43 | + manifest entry. The wrapper runs on Homebrew's `openjdk` formula |
| 44 | + (dependency), so users do not need their own Java; Turicum needs Java 21+ |
| 45 | + and the Homebrew `openjdk` is always newer than that. |
| 46 | + |
| 47 | +The command is called **`turi`**, matching the `.turi` source-file extension: |
| 48 | + |
| 49 | + turi program.turi # run a program |
| 50 | + turi -REPL # interactive REPL |
| 51 | + turi -compile x.turi # compile to .turc |
| 52 | + turi -version |
| 53 | + turi -help |
| 54 | + |
| 55 | +## 2. Files in this repository |
| 56 | + |
| 57 | +Everything lives under `homebrew/`: |
| 58 | + |
| 59 | +| File | Purpose | |
| 60 | +|---|---| |
| 61 | +| `homebrew/turicum.rb` | The formula. Clean under `brew style` and `brew audit --strict`. Carries a `livecheck` block (Maven Central `maven-metadata.xml`) and a `test do` block that runs a one-line program and checks `turi -version`. | |
| 62 | +| `homebrew/update-formula.sh` | Release helper: determines the latest release from Maven Central (or takes a version argument), fetches Central's official sha256, verifies it by downloading and hashing the artifact, and rewrites the formula's `url`/`sha256` lines in place. Before bumping, it snapshots the outgoing version as a versioned formula `turicum@<oldversion>.rb` (see §7). | |
| 63 | +| `homebrew/README.md` | Short operational readme: install, publish as tap, release steps. | |
| 64 | + |
| 65 | +Formula details worth remembering: |
| 66 | + |
| 67 | +- There is **no explicit `version` line** — brew scans the version from the |
| 68 | + URL, and an explicit line is flagged as redundant by `brew audit --strict`. |
| 69 | + `update-formula.sh` therefore only rewrites `url` and `sha256`. |
| 70 | +- The file starts with `# typed: strict` / `# frozen_string_literal: true` and |
| 71 | + a class documentation comment — all required by `brew style`. |
| 72 | +- `desc` must not start with the formula name (another style rule), hence |
| 73 | + "Programming language: interpreter, compiler, and REPL". |
| 74 | + |
| 75 | +## 3. The tap |
| 76 | + |
| 77 | +Homebrew 6 refuses to install a formula from a bare file path |
| 78 | +(`Error: Homebrew requires formulae to be in a tap`), so distribution happens |
| 79 | +through a **tap** — a git repository named `homebrew-<name>` on GitHub. |
| 80 | + |
| 81 | +The tap was created locally with `brew tap-new verhas/tap`. It lives at: |
| 82 | + |
| 83 | + $(brew --repository)/Library/Taps/verhas/homebrew-tap |
| 84 | + |
| 85 | +It is a normal git repository, scaffolded by brew with standard CI workflows |
| 86 | +(`.github/workflows/tests.yml`, `publish.yml` for test-bot/bottling — usable |
| 87 | +later, ignorable for now), and contains the formula as |
| 88 | +`Formula/turicum.rb`, committed as "turicum 1.4.2 (new formula)". |
| 89 | + |
| 90 | +The formula exists in two places by design: `homebrew/turicum.rb` in this repo |
| 91 | +is the **source of truth** that is versioned and updated together with |
| 92 | +Turicum; the tap's `Formula/turicum.rb` is the **published copy**. Every |
| 93 | +release copies the former over the latter (§6). |
| 94 | + |
| 95 | +## 4. What was verified (2026-07-17) |
| 96 | + |
| 97 | +- `brew style homebrew/turicum.rb` — no offenses. |
| 98 | +- `brew audit --strict verhas/tap/turicum` — no problems. |
| 99 | +- `brew install verhas/tap/turicum` — installs; `which turi` → |
| 100 | + `/opt/homebrew/bin/turi`. |
| 101 | +- `turi -version` → "Turicum Version 1.4.2 (Built: 2026-06-02…)". |
| 102 | +- A hello-world `.turi` program runs. |
| 103 | +- `brew test turicum` — passes (runs the formula's `test do` block). |
| 104 | +- The sha256 in the formula equals Maven Central's official |
| 105 | + `…distribution.zip.sha256` **and** the locally computed hash of the |
| 106 | + downloaded artifact: |
| 107 | + `c766df5cc426fd8511f6e36c5b5ec3d59fe0690587864b3dac50e665401a9c3c`. |
| 108 | + |
| 109 | +`turi` 1.4.2 is installed on this machine through the local tap |
| 110 | +(`brew uninstall turicum` removes it). |
| 111 | + |
| 112 | +## 5. Remaining step: publish the tap (one-time) |
| 113 | + |
| 114 | +1. Create the GitHub repository **`verhas/homebrew-tap`** (public). |
| 115 | +2. Push the local tap repo to it: |
| 116 | + |
| 117 | + ```sh |
| 118 | + cd "$(brew --repository)/Library/Taps/verhas/homebrew-tap" |
| 119 | + git remote add origin git@github.com:verhas/homebrew-tap.git |
| 120 | + git push -u origin main |
| 121 | + ``` |
| 122 | + |
| 123 | +From then on, anyone installs Turicum with: |
| 124 | + |
| 125 | +```sh |
| 126 | +brew install verhas/tap/turicum |
| 127 | +``` |
| 128 | + |
| 129 | +(`brew` expands `verhas/tap` to `github.com/verhas/homebrew-tap` and taps it |
| 130 | +automatically.) Worth adding to `README.adoc` as the macOS/Linux install |
| 131 | +instruction once the tap is public. |
| 132 | + |
| 133 | +## 6. Per-release workflow |
| 134 | + |
| 135 | +This is step "Homebrew" of the overall release process described in |
| 136 | +`RELEASE.md`. After a normal Maven release has reached Maven Central: |
| 137 | + |
| 138 | +```sh |
| 139 | +homebrew/update-formula.sh # or: homebrew/update-formula.sh 1.4.3 |
| 140 | +brew style homebrew/*.rb # lint the bumped and the snapshot formula |
| 141 | +cp homebrew/turicum.rb homebrew/turicum@*.rb \ |
| 142 | + "$(brew --repository)/Library/Taps/verhas/homebrew-tap/Formula/" |
| 143 | +brew reinstall turicum # smoke-test the new version locally |
| 144 | +brew test turicum |
| 145 | +cd "$(brew --repository)/Library/Taps/verhas/homebrew-tap" |
| 146 | +git add Formula && git commit -m "turicum <version>" |
| 147 | +git push |
| 148 | +``` |
| 149 | + |
| 150 | +Also commit the updated/added formulae under `homebrew/` in this repository. |
| 151 | + |
| 152 | +`brew livecheck turicum` compares the formula against Maven Central's |
| 153 | +`<release>` metadata and reports when the formula lags a release. |
| 154 | + |
| 155 | +## 7. Multiple installable versions |
| 156 | + |
| 157 | +Homebrew's model is one formula file per installable version: `turicum.rb` |
| 158 | +always tracks the latest release, and *versioned formulae* named |
| 159 | +`turicum@<version>.rb` keep specific older releases installable: |
| 160 | + |
| 161 | +```sh |
| 162 | +brew install verhas/tap/turicum # latest |
| 163 | +brew install verhas/tap/turicum@1.4.2 # pinned older release |
| 164 | +``` |
| 165 | + |
| 166 | +`update-formula.sh` creates these automatically: on every bump it snapshots |
| 167 | +the outgoing version first (skipping the snapshot if the file already |
| 168 | +exists). A versioned formula differs from the main one in three ways: |
| 169 | + |
| 170 | +- file and class name: `turicum@1.4.2.rb` must contain class `TuricumAT142` |
| 171 | + (brew's mapping: `@` → `AT`, dots dropped); |
| 172 | +- no `livecheck` block — a pinned version has nothing to check; |
| 173 | +- `keg_only :versioned_formula` — pinned versions install but do not link |
| 174 | + `turi` into the PATH, so any number of them can coexist with the main |
| 175 | + formula. A pinned version is run via |
| 176 | + `$(brew --prefix turicum@1.4.2)/bin/turi`, or force-linked with |
| 177 | + `brew link --overwrite turicum@1.4.2` after unlinking the others. |
| 178 | + |
| 179 | +Versions that predate the snapshot mechanism remain recoverable from the |
| 180 | +tap's git history with `brew extract turicum verhas/tap --version=<v>`, which |
| 181 | +generates the corresponding `turicum@<v>.rb` from a past commit. Users who |
| 182 | +only want to stop upgrading need none of this: `brew pin turicum` blocks |
| 183 | +upgrades until unpinned. |
| 184 | + |
| 185 | +## 8. Possible future improvements |
| 186 | + |
| 187 | +- **Automate the release bump**: a GitHub Actions workflow in the tap repo |
| 188 | + (scheduled, or triggered by the Turicum release) that runs the equivalent of |
| 189 | + `update-formula.sh`, commits, and pushes. Not set up yet. |
| 190 | +- **Bottles**: the tap scaffolding contains brew's test-bot workflows; since |
| 191 | + the formula only unpacks jars and writes a script, bottling brings little — |
| 192 | + installation is already fast — but it removes the Java-less install-time |
| 193 | + dependency on curl reaching Maven Central. |
| 194 | +- **`brew install turicum` without the tap prefix** would require acceptance |
| 195 | + into homebrew-core; their bar (notability, no pre-built binaries policy |
| 196 | + exceptions for Java are common, though — many Java tools ship jars) can be |
| 197 | + attempted once the language has more users. |
0 commit comments