Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions distribution/distribution-contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -119,7 +119,7 @@
},
{
"id": "homebrew",
"state": "planned",
"state": "available",
"targets": [
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
Expand Down
24 changes: 20 additions & 4 deletions docs/distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

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

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.

Expand Down Expand Up @@ -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:
Expand Down
13 changes: 10 additions & 3 deletions scripts/distribution-contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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/);
});
8 changes: 5 additions & 3 deletions scripts/validate-distribution-contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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) {
Expand Down