Skip to content

Commit 9230312

Browse files
ci(release): add binary matrix release workflow
release.yml builds the mcp-loadtest binary for 4 targets (linux-x64, macos-arm64, macos-x64, windows-x64), then creates the GitHub Release and attaches each archive + .sha256. Hand-rolled matrix chosen over cargo-dist to avoid an interactive setup step. RELEASE.md Gate B/C and ADR 0015 realigned (no cargo-dist) to prevent doc drift. No code change.
1 parent d2ef3a4 commit 9230312

3 files changed

Lines changed: 124 additions & 28 deletions

File tree

.github/workflows/release.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: release
2+
3+
# Builds prebuilt binaries for v0.1.0+ and attaches them to the GitHub
4+
# Release (creating it if absent). No crates.io — ADR 0015.
5+
#
6+
# Triggers:
7+
# - pushing a `v*` tag (future releases)
8+
# - manual run for an existing tag (workflow_dispatch with `tag`),
9+
# which is how v0.1.0 — tagged before this workflow existed — is built.
10+
11+
on:
12+
push:
13+
tags: ["v*"]
14+
workflow_dispatch:
15+
inputs:
16+
tag:
17+
description: "Existing tag to build and release (e.g. v0.1.0)"
18+
required: true
19+
20+
permissions:
21+
contents: write
22+
23+
jobs:
24+
build:
25+
name: ${{ matrix.target }}
26+
runs-on: ${{ matrix.os }}
27+
strategy:
28+
fail-fast: false
29+
matrix:
30+
include:
31+
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
32+
- { os: macos-latest, target: aarch64-apple-darwin }
33+
- { os: macos-latest, target: x86_64-apple-darwin }
34+
- { os: windows-latest, target: x86_64-pc-windows-msvc }
35+
steps:
36+
- uses: actions/checkout@v4
37+
with:
38+
ref: ${{ github.event.inputs.tag || github.ref }}
39+
40+
- name: Install Rust (stable + target)
41+
run: |
42+
rustup toolchain install stable --profile minimal --no-self-update
43+
rustup target add ${{ matrix.target }}
44+
45+
- name: Build CLI
46+
run: cargo build --release --locked -p mcp-loadtest-cli --target ${{ matrix.target }}
47+
48+
- name: Package (unix)
49+
if: matrix.os != 'windows-latest'
50+
shell: bash
51+
run: |
52+
ver="${{ github.event.inputs.tag || github.ref_name }}"
53+
dir="mcp-loadtest-${ver}-${{ matrix.target }}"
54+
mkdir "$dir"
55+
cp "target/${{ matrix.target }}/release/mcp-loadtest" "$dir/"
56+
cp README.md LICENSE-MIT LICENSE-APACHE "$dir/"
57+
tar -czf "${dir}.tar.gz" "$dir"
58+
shasum -a 256 "${dir}.tar.gz" > "${dir}.tar.gz.sha256"
59+
60+
- name: Package (windows)
61+
if: matrix.os == 'windows-latest'
62+
shell: pwsh
63+
run: |
64+
$ver = "${{ github.event.inputs.tag || github.ref_name }}"
65+
$dir = "mcp-loadtest-$ver-${{ matrix.target }}"
66+
New-Item -ItemType Directory -Path $dir | Out-Null
67+
Copy-Item "target/${{ matrix.target }}/release/mcp-loadtest.exe" $dir
68+
Copy-Item README.md, LICENSE-MIT, LICENSE-APACHE $dir
69+
Compress-Archive -Path $dir -DestinationPath "$dir.zip"
70+
"$((Get-FileHash "$dir.zip" -Algorithm SHA256).Hash.ToLower()) $dir.zip" |
71+
Out-File "$dir.zip.sha256" -Encoding ascii
72+
73+
- name: Attach to GitHub Release
74+
uses: softprops/action-gh-release@v2
75+
with:
76+
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
77+
fail_on_unmatched_files: true
78+
files: |
79+
mcp-loadtest-*.tar.gz
80+
mcp-loadtest-*.tar.gz.sha256
81+
mcp-loadtest-*.zip
82+
mcp-loadtest-*.zip.sha256
83+
body: |
84+
Prebuilt binaries for ${{ github.event.inputs.tag || github.ref_name }}. Full notes: [CHANGELOG.md](https://github.com/Teerapat-Vatpitak/mcp-loadtest/blob/main/CHANGELOG.md).
85+
86+
**Install from source** (not on crates.io — [ADR 0015](https://github.com/Teerapat-Vatpitak/mcp-loadtest/blob/main/docs/adr/0015-defer-crates-io-distribution.md)):
87+
88+
```
89+
cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli
90+
```
91+
92+
Or download a prebuilt archive below (verify with the matching `.sha256`).

docs/RELEASE.md

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,42 +49,45 @@ Both distribution channels need a public repo. `Cargo.toml`
4949

5050
Reversibility: the repo can be set private again; nothing is permanent here.
5151

52-
## Gate B — prebuilt binaries via cargo-dist _(hard-confirm; reversible)_
52+
## Gate B — prebuilt binaries + Release (`release.yml`) _(reversible)_
53+
54+
`.github/workflows/release.yml` (hand-rolled matrix — **not** cargo-dist) builds
55+
the `mcp-loadtest` binary for `x86_64-unknown-linux-gnu`,
56+
`aarch64-apple-darwin`, `x86_64-apple-darwin`, `x86_64-pc-windows-msvc`, then
57+
**creates the GitHub Release** (if absent) and attaches each archive + its
58+
`.sha256`. Future `v*` tag pushes run it automatically; `v0.1.0` was tagged
59+
before the workflow existed, so trigger it once manually:
5360

5461
```bash
55-
cargo install cargo-dist # or: cargo binstall cargo-dist
56-
dist init # interactive: pick Linux/macOS/Windows targets,
57-
# writes [workspace.metadata.dist] + .github/workflows/release.yml
58-
git add Cargo.toml dist-workspace.toml .github/workflows/release.yml
59-
git commit -m "build(dist): add cargo-dist release workflow"
60-
git push origin main
62+
gh workflow run release.yml -f tag=v0.1.0
6163
```
6264

63-
Then produce the binaries for the existing `v0.1.0` tag. The generated workflow
64-
triggers on a tag push; since `v0.1.0` is already pushed, pick one:
65+
(Or: GitHub → Actions → **release** → Run workflow → `tag = v0.1.0`.)
66+
67+
Watch the run:
68+
69+
```bash
70+
gh run watch "$(gh run list --workflow=release.yml --limit 1 --json databaseId -q '.[0].databaseId')"
71+
```
6572

66-
- **Build + attach locally** (no tag churn): `dist build` then attach the
67-
artifacts when you create the Release in Gate C (`gh release create … <files>`).
68-
- **Or** re-run the release workflow via `workflow_dispatch` for the `v0.1.0`
69-
tag from the Actions tab.
73+
Do **not** delete-and-repush the `v0.1.0` tag to trigger CI — moving a pushed
74+
tag is destructive and needs explicit sign-off; `workflow_dispatch` exists so
75+
you don't have to.
7076

71-
Do **not** delete-and-repush the `v0.1.0` tag to re-trigger CI — moving a pushed
72-
tag is destructive and needs explicit sign-off.
77+
- [ ] Workflow green; binaries built for all 4 targets.
7378

74-
- [ ] Binaries built for Linux + macOS + Windows.
79+
## Gate C — verify the Release _(reversible)_
7580

76-
## Gate C — GitHub Release _(hard-confirm; tag already pushed; reversible)_
81+
The workflow already created the Release with notes + assets — no manual
82+
`gh release create`. Verify:
7783

7884
```bash
79-
sed -n '/## \[0\.1\.0\]/,/^\[Unreleased\]:/p' CHANGELOG.md | sed '$d' > /tmp/relnotes-0.1.0.md
80-
gh release create v0.1.0 --verify-tag --title "v0.1.0" \
81-
--notes-file /tmp/relnotes-0.1.0.md \
82-
./target/distrib/* # the cargo-dist artifacts (path per `dist build` output)
85+
gh release view v0.1.0 --json assets -q ".assets[].name"
86+
cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli
87+
mcp-loadtest --version
8388
```
8489

85-
(Short body alternative: `--notes "First public release. Install: cargo install --git … . See CHANGELOG.md §[0.1.0]."`)
86-
87-
- [ ] Release shows the binaries + source archives.
90+
- [ ] 4 archives + 4 `.sha256` attached; `cargo install --git` works.
8891
- [ ] A Release can be edited or deleted later — nothing append-only here.
8992

9093
## Gate D — announce _(manual)_

docs/adr/0015-defer-crates-io-distribution.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ Defer crates.io. Ship v0.1.0 through two reversible channels:
3232
1. **`cargo install --git`**
3333
`cargo install --git https://github.com/Teerapat-Vatpitak/mcp-loadtest mcp-loadtest-cli`.
3434
Needs only a public repo; nothing to maintain server-side.
35-
2. **Prebuilt binaries on the GitHub Release**`cargo-dist`-generated artifacts
36-
for Linux/macOS/Windows attached to the `v0.1.0` release, for users without a
37-
Rust toolchain.
35+
2. **Prebuilt binaries on the GitHub Release**built by a hand-rolled
36+
`.github/workflows/release.yml` matrix (Linux/macOS x64+arm64/Windows) that
37+
also creates the Release; for users without a Rust toolchain.
3838

3939
The repo goes **public** (reversible — it can be re-privatised; a Release/tag can
4040
be edited or removed). crates.io stays a recorded future option.
@@ -67,7 +67,8 @@ staying fully reversible.
6767
second-class (no semver resolution from a registry, no docs.rs).
6868
- Not discoverable through crates.io search — weaker than reaatech's npm presence.
6969
- `--git` installs build from source (slower than a registry binary).
70-
- The `cargo-dist` release workflow must be set up and maintained.
70+
- The hand-rolled `release.yml` workflow must be maintained (no cargo-dist
71+
niceties: no generated installer scripts or `cargo binstall` metadata).
7172

7273
**Mitigations**
7374

0 commit comments

Comments
 (0)