From 096d07055d4e43c43aa14de73b76a7b04ebb4bbb Mon Sep 17 00:00:00 2001 From: konojunya Date: Sat, 5 Sep 2026 13:20:57 +0900 Subject: [PATCH] Document Homebrew distribution availability --- README.md | 10 +++++++-- distribution/distribution-contract.json | 4 ++-- docs/distribution.md | 24 ++++++++++++++++++---- scripts/distribution-contract.test.mjs | 13 +++++++++--- scripts/validate-distribution-contract.mjs | 8 +++++--- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c14e5d7..5c13132 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,17 @@ `stack-sh/cli` is the open-source native Rust `stack` command for Stack architecture diagrams. -The repository contains native validation, formatting, and rendering commands. [Stack CLI 0.3.0](https://github.com/stack-sh/cli/releases/tag/v0.3.0) is the first supported native binary release for macOS 13 or newer and glibc-based Linux 2.31 or newer, on arm64 and x86_64. Homebrew, Cargo, Aqua, and self-update are not available yet. The target matrix, artifact names, verification material, channel ownership, and rollback rules are defined by the [distribution contract](./docs/distribution.md), with signing and verification procedures in the [supply-chain guide](./docs/supply-chain.md). +The repository contains native validation, formatting, and rendering commands. [Stack CLI 0.3.0](https://github.com/stack-sh/cli/releases/tag/v0.3.0) is the first supported native binary release for macOS 13 or newer and glibc-based Linux 2.31 or newer, on arm64 and x86_64. GitHub Releases and Homebrew are available; Cargo, Aqua, and self-update are still planned. The target matrix, artifact names, verification material, channel ownership, and rollback rules are defined by the [distribution contract](./docs/distribution.md), with signing and verification procedures in the [supply-chain guide](./docs/supply-chain.md). ## Install -Download the archive for your target and its verification material from [GitHub Releases](https://github.com/stack-sh/cli/releases/tag/v0.3.0). Verify the checksum signature and both attestations by following the [supply-chain guide](./docs/supply-chain.md), then follow the [direct installation steps](./docs/distribution.md#direct-installation). The macOS artifacts are reproducibly ad-hoc signed, not Apple-notarized; Sigstore and GitHub attestations provide the publisher-identity check. +On supported Homebrew hosts, install the owner-maintained formula: + +```sh +brew install stack-sh/tap/stack +``` + +For a direct installation, download the archive for your target and its verification material from [GitHub Releases](https://github.com/stack-sh/cli/releases/tag/v0.3.0). Verify the checksum signature and both attestations by following the [supply-chain guide](./docs/supply-chain.md), then follow the [direct installation steps](./docs/distribution.md#direct-installation). The macOS artifacts are reproducibly ad-hoc signed, not Apple-notarized; Sigstore and GitHub attestations provide the publisher-identity check. ## Commands diff --git a/distribution/distribution-contract.json b/distribution/distribution-contract.json index 74205da..9d79e7a 100644 --- a/distribution/distribution-contract.json +++ b/distribution/distribution-contract.json @@ -11,7 +11,7 @@ }, "availability": { "state": "available", - "message": "Stack CLI 0.3.0 is available from GitHub Releases. Homebrew, Cargo, Aqua, and self-update remain planned." + "message": "Stack CLI 0.3.0 is available from GitHub Releases and Homebrew. Cargo, Aqua, and self-update remain planned." }, "versioning": { "scheme": "Semantic Versioning", @@ -119,7 +119,7 @@ }, { "id": "homebrew", - "state": "planned", + "state": "available", "targets": [ "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", diff --git a/docs/distribution.md b/docs/distribution.md index d20bdda..6c686f5 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -2,7 +2,7 @@ This document defines the shared release contract for the Stack CLI. It is normative for GitHub Releases, Homebrew, Cargo, Aqua, and `stack` self-update implementations. The machine-readable source is [`distribution/distribution-contract.json`](../distribution/distribution-contract.json). -[Stack CLI 0.3.0](https://github.com/stack-sh/cli/releases/tag/v0.3.0) is available as a supported GitHub Release for every target below. Homebrew, Cargo, Aqua, and self-update remain **planned** and have no supported install command yet. +[Stack CLI 0.3.0](https://github.com/stack-sh/cli/releases/tag/v0.3.0) is available as a supported GitHub Release for every target below and through the owner-maintained Homebrew tap for the hosts marked below. Cargo, Aqua, and self-update remain **planned** and have no supported install command yet. ## Supported platform matrix @@ -10,10 +10,10 @@ The first supported binary matrix is intentionally narrow: | Rust target | OS | Architecture | Runtime floor | Direct | Homebrew | Cargo | Aqua | Self-update | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `aarch64-apple-darwin` | macOS | arm64 | macOS 13 | available | planned | planned | planned | planned | +| `aarch64-apple-darwin` | macOS | arm64 | macOS 13 | available | available | planned | planned | planned | | `x86_64-apple-darwin` | macOS | x86_64 | macOS 13 | available | — | planned | planned | planned | -| `aarch64-unknown-linux-gnu` | Linux | arm64 | glibc 2.31 | available | planned | planned | planned | planned | -| `x86_64-unknown-linux-gnu` | Linux | x86_64 | glibc 2.31 | available | planned | planned | planned | planned | +| `aarch64-unknown-linux-gnu` | Linux | arm64 | glibc 2.31 | available | available | planned | planned | planned | +| `x86_64-unknown-linux-gnu` | Linux | x86_64 | glibc 2.31 | available | available | planned | planned | planned | Windows, musl-based Linux distributions such as Alpine, BSD, and 32-bit architectures are not supported release targets. A source build may happen to work elsewhere, but it is best-effort and does not block a release. Cargo installs on supported targets require Rust 1.85 or newer. Homebrew availability additionally follows [Homebrew's current tier-1 host requirements](https://docs.brew.sh/Support-Tiers); Stack does not label a host as supported when the package manager itself classifies it below tier 1. @@ -58,6 +58,22 @@ The sorted checksum file uses SHA-256 and covers the release manifest, all archi The release manifest records the tag, commit, source version, `minimumSupportedCliVersion`, each target's artifact names and SHA-256 values, the build identity, and each channel whose own install smoke test passed. Its schema is [`distribution/release-manifest.schema.json`](../distribution/release-manifest.schema.json). Supply-chain generation and user verification are documented in the [supply-chain guide](./supply-chain.md). +## Homebrew installation + +The owner-maintained [`stack-sh/homebrew-tap`](https://github.com/stack-sh/homebrew-tap) installs the canonical GitHub Release archive without rebuilding or repacking it. Homebrew is available on Apple Silicon macOS and glibc-based Linux on arm64 and x86_64 when the host meets Homebrew's current tier-1 requirements. + +Install, upgrade, or uninstall with: + +```sh +brew install stack-sh/tap/stack +brew upgrade stack-sh/tap/stack +brew uninstall stack-sh/tap/stack +``` + +The formula does not remove or replace Stack configuration and icon stores during an upgrade or uninstall. Formula updates verify release checksums, provenance, and SBOM attestations before changing the archive mapping. The fail-closed update and recovery procedure is maintained in the tap's [maintainer guide](https://github.com/stack-sh/homebrew-tap/blob/main/docs/maintaining.md). + +Homebrew was activated after the immutable `v0.3.0` release assets were published. The release manifest therefore remains the publication-time record, while this contract and the tap CI record the later channel activation; release assets are not replaced to retrofit that state. + ## Direct installation Download [Stack CLI 0.3.0](https://github.com/stack-sh/cli/releases/tag/v0.3.0), select the archive whose target matches the supported platform table, and obtain all matching verification material. Complete the [supply-chain verification](./supply-chain.md), then extract and install the verified binary. Replace `{target}` with the exact release target: diff --git a/scripts/distribution-contract.test.mjs b/scripts/distribution-contract.test.mjs index 99cf2ea..7f55d2b 100644 --- a/scripts/distribution-contract.test.mjs +++ b/scripts/distribution-contract.test.mjs @@ -66,9 +66,16 @@ test("the GitHub release cannot be activated with a planned target", () => { assert.throws(() => validateDistributionContract(candidate, cargoToml), /must be available after release verification/); }); -test("package-manager channels remain planned after direct release activation", () => { +test("the activated Homebrew channel cannot regress to planned", () => { const candidate = changed((value) => { - value.channels.find(({ id }) => id === "homebrew").state = "available"; + value.channels.find(({ id }) => id === "homebrew").state = "planned"; }); - assert.throws(() => validateDistributionContract(candidate, cargoToml), /homebrew state must be planned/); + assert.throws(() => validateDistributionContract(candidate, cargoToml), /homebrew state must be available/); +}); + +test("an unactivated package-manager channel cannot become available", () => { + const candidate = changed((value) => { + value.channels.find(({ id }) => id === "cargo").state = "available"; + }); + assert.throws(() => validateDistributionContract(candidate, cargoToml), /cargo state must be planned/); }); diff --git a/scripts/validate-distribution-contract.mjs b/scripts/validate-distribution-contract.mjs index 51dbbdb..40f503c 100644 --- a/scripts/validate-distribution-contract.mjs +++ b/scripts/validate-distribution-contract.mjs @@ -12,6 +12,7 @@ const expectedTargets = [ "x86_64-unknown-linux-gnu", ]; const expectedChannels = ["aqua", "cargo", "github-release", "homebrew", "self-update"]; +const availableChannels = new Set(["github-release", "homebrew"]); const requiredArchiveEntries = ["LICENSE", "NOTICE", "THIRD_PARTY_LICENSES.md", "stack"]; const requiredUnsupportedTerms = ["32-bit", "BSD", "Windows", "musl"]; const requiredActivationTerms = ["Cargo package version", "SBOMs", "provenance", "stack --version"]; @@ -55,8 +56,9 @@ export function validateDistributionContract(contract, cargoToml) { invariant(contract.availability?.state === "available", "distribution must be available after the verified stable release"); invariant( contract.availability?.message?.includes("Stack CLI 0.3.0") && - contract.availability.message.includes("GitHub Releases"), - "availability message must identify the verified stable GitHub release", + contract.availability.message.includes("GitHub Releases") && + contract.availability.message.includes("Homebrew"), + "availability message must identify the verified stable GitHub release and Homebrew channel", ); invariant( contract.product.currentSourceVersion === cargoValue(cargoToml, "version"), @@ -94,7 +96,7 @@ export function validateDistributionContract(contract, cargoToml) { ); const targetIds = new Set(expectedTargets); for (const channel of contract.channels) { - const expectedState = channel.id === "github-release" ? "available" : "planned"; + const expectedState = availableChannels.has(channel.id) ? "available" : "planned"; invariant(channel.state === expectedState, `${channel.id} state must be ${expectedState}`); uniqueSorted(channel.targets, `${channel.id} targets`); for (const target of channel.targets) {