Skip to content

Commit fe37fec

Browse files
authored
Merge pull request #15 from MinaProtocol/add-release-workflow
Add release workflow (GitHub Release on tag push)
2 parents d7c1090 + 8ac3171 commit fe37fec

3 files changed

Lines changed: 223 additions & 10 deletions

File tree

.github/workflows/package.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,7 @@ jobs:
150150
path: out/mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb
151151
if-no-files-found: error
152152

153-
- name: Attach to GitHub Release (tag push only)
154-
if: startsWith(github.ref, 'refs/tags/v')
155-
uses: softprops/action-gh-release@v2
156-
with:
157-
files: out/mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb
153+
# NB: GitHub Release creation + asset attachment for tag pushes
154+
# lives in `.github/workflows/release.yml`. The Package workflow
155+
# is responsible only for the artifact build + workflow-artifact
156+
# upload (always-on) and the GHCR push (docker job, also on tag).

.github/workflows/release.yml

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
name: Release
2+
3+
# Fires on `vX.Y.Z` tag push. Builds the .deb fresh from the tagged
4+
# commit, creates a GitHub Release with auto-generated notes,
5+
# attaches the .deb, and references the matching Docker image on
6+
# GHCR (which the `Package` workflow publishes in parallel for the
7+
# same tag).
8+
#
9+
# Scope: GitHub-side publishing only. Pushing to the actual APT
10+
# repositories (`stable.apt.packages.minaprotocol.com` etc.) and
11+
# Docker image signing are intentionally out of scope for the
12+
# first iteration — they require GPG signing keys and S3 credentials
13+
# as repo secrets and warrant their own PR.
14+
15+
on:
16+
push:
17+
tags:
18+
- 'v*'
19+
20+
env:
21+
CARGO_TERM_COLOR: always
22+
RUST_BACKTRACE: 1
23+
IMAGE_NAME: ${{ github.repository_owner }}/mina-release-toolkit
24+
25+
concurrency:
26+
# One release run per tag. A re-pushed tag (force-push) cancels the
27+
# superseded run rather than racing to attach the same asset twice.
28+
group: release-${{ github.ref }}
29+
cancel-in-progress: true
30+
31+
jobs:
32+
release:
33+
name: Build .deb and publish GitHub Release
34+
runs-on: ubuntu-latest
35+
permissions:
36+
contents: write # required to create the Release and upload assets
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v4
40+
with:
41+
submodules: recursive
42+
43+
- name: Install Rust + musl target
44+
# Static-linking against musl removes the glibc floor entirely:
45+
# one binary that runs on any Linux x86_64. The four crates
46+
# already use rustls (no OpenSSL link), so musl Just Works.
47+
uses: dtolnay/rust-toolchain@stable
48+
with:
49+
targets: x86_64-unknown-linux-musl
50+
51+
- name: Cache cargo
52+
uses: Swatinem/rust-cache@v2
53+
with:
54+
workspaces: |
55+
release-manager
56+
mina-bench-upload
57+
buildkite-cache-manager
58+
deb-toolkit
59+
# Cache key includes the target triple so the musl build's
60+
# cache doesn't collide with the default-host build cache
61+
# used elsewhere.
62+
key: release-musl-${{ github.ref_name }}
63+
64+
- name: Install Debian + musl build tools
65+
run: |
66+
sudo apt-get update
67+
sudo apt-get install -y --no-install-recommends \
68+
fakeroot dpkg-dev build-essential pkg-config libssl-dev \
69+
musl-tools
70+
71+
- name: Strip leading `v` from tag for the Debian version string
72+
# Debian version strings are looser than semver but we still
73+
# want `mina-release-toolkit_1.2.3_amd64.deb`, not
74+
# `…_v1.2.3_…`. `${ref_name#v}` does the trim.
75+
id: ver
76+
run: echo "version=${GITHUB_REF_NAME#v}" >> "${GITHUB_OUTPUT}"
77+
78+
- name: Build the four release binaries (musl static)
79+
# `--target x86_64-unknown-linux-musl` produces fully static
80+
# binaries — no glibc dependency, so a single .deb is
81+
# installable on bullseye / focal / jammy / noble / bookworm
82+
# alike. The lock-file policy is the same as package.yml:
83+
# in-tree crates use --locked, the deb-toolkit submodule
84+
# gitignores its lockfile upstream so we let cargo resolve.
85+
env:
86+
TARGET: x86_64-unknown-linux-musl
87+
run: |
88+
(cd release-manager && cargo build --release --locked --target "$TARGET")
89+
(cd mina-bench-upload && cargo build --release --locked --target "$TARGET")
90+
(cd buildkite-cache-manager && cargo build --release --locked --target "$TARGET")
91+
(cd deb-toolkit && cargo build --release --target "$TARGET")
92+
93+
- name: Assemble .deb via deb-toolkit
94+
# build-deb.sh respects CARGO_TARGET to look in
95+
# `target/<triple>/release/` instead of `target/release/`.
96+
env:
97+
CARGO_TARGET: x86_64-unknown-linux-musl
98+
run: packaging/build-deb.sh "${{ steps.ver.outputs.version }}" ./out
99+
100+
- name: Sanity-check the produced .deb
101+
run: |
102+
set -euo pipefail
103+
DEB="./out/mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb"
104+
test -f "${DEB}"
105+
dpkg-deb --info "${DEB}"
106+
dpkg-deb -c "${DEB}" | head -20
107+
108+
- name: Compose release body
109+
# Small extra context appended to the auto-generated notes:
110+
# the matching GHCR image reference and an apt-install
111+
# one-liner once the .deb attachment is live.
112+
id: body
113+
run: |
114+
cat > release_extra.md <<EOF
115+
## Artifacts
116+
117+
- **Debian package** (attached to this release as \`mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb\`)
118+
- **Docker image**: \`ghcr.io/${IMAGE_NAME}:${{ steps.ver.outputs.version }}\` — also tagged \`${{ steps.ver.outputs.version }}\`, the major/minor floor, and \`latest\`. See the [Package workflow](https://github.com/${{ github.repository }}/actions/workflows/package.yml) for the build that produced it.
119+
120+
## Install
121+
122+
The .deb is a fully static musl build — one binary that runs on
123+
\`bullseye\`, \`focal\`, \`jammy\`, \`noble\`, and \`bookworm\`. Pick
124+
the codename matching your distro for the apt source line:
125+
126+
\`\`\`bash
127+
# Debian, via the apt repo (preferred). Replace <CODENAME> with
128+
# one of: bullseye focal jammy noble bookworm
129+
echo "deb [trusted=yes] http://packages.o1test.net <CODENAME> stable" \\
130+
| sudo tee /etc/apt/sources.list.d/mina-release-toolkit.list
131+
sudo apt-get update && sudo apt-get install mina-release-toolkit
132+
133+
# Debian, by downloading the .deb from this release's Assets:
134+
sudo apt install ./mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb
135+
136+
# Docker:
137+
docker pull ghcr.io/${IMAGE_NAME}:${{ steps.ver.outputs.version }}
138+
\`\`\`
139+
EOF
140+
141+
- name: Create GitHub Release and upload .deb
142+
uses: softprops/action-gh-release@v2
143+
with:
144+
# Auto-generated commit-summary notes; our `body` is appended.
145+
generate_release_notes: true
146+
body_path: release_extra.md
147+
files: out/mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb
148+
fail_on_unmatched_files: true
149+
150+
# ------- Publish to packages.o1test.net APT repo --------------------
151+
# The toolkit's `apt install` channel. CLAUDE.md tags this bucket as
152+
# "for everything (multichannel repo for every purpose)" — unsigned
153+
# legacy repo, no GPG key required, which is appropriate for the
154+
# toolkit (CI / dev-box internal tooling).
155+
#
156+
# Requires two repo secrets: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY.
157+
# Region (us-west-2) and bucket (packages.o1test.net) are stable
158+
# per CLAUDE.md and hardcoded here.
159+
#
160+
# The job uses `deb-s3` (a Ruby gem) directly rather than going
161+
# through release-manager: release-manager is the right tool for
162+
# the mina-daemon channel-promotion flow but this is a one-shot
163+
# upload, and the direct call is one line + one install step.
164+
165+
- name: Set up Ruby for deb-s3
166+
uses: ruby/setup-ruby@v1
167+
with:
168+
ruby-version: '3.2'
169+
170+
- name: Install deb-s3
171+
run: sudo gem install deb-s3
172+
173+
- name: Publish .deb to packages.o1test.net (all codenames)
174+
# The musl static .deb runs on every Debian/Ubuntu release we
175+
# care about (bullseye through noble), so we publish the same
176+
# artifact under each codename. deb-s3 happily re-uploads to
177+
# multiple codenames as separate logical entries even though
178+
# the underlying file in S3 is identical.
179+
env:
180+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
181+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
182+
run: |
183+
set -euo pipefail
184+
DEB="./out/mina-release-toolkit_${{ steps.ver.outputs.version }}_amd64.deb"
185+
for codename in bullseye focal jammy noble bookworm; do
186+
echo "=== uploading to ${codename}/stable ==="
187+
deb-s3 upload \
188+
--bucket=packages.o1test.net \
189+
--s3-region=us-west-2 \
190+
--codename="${codename}" \
191+
--component=stable \
192+
--preserve-versions \
193+
"${DEB}"
194+
done

packaging/build-deb.sh

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,31 @@ OUTPUT_DIR="$2"
2929
ARCH="${ARCH:-amd64}"
3030

3131
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
32-
DEB_TOOLKIT_BIN="${REPO_ROOT}/deb-toolkit/target/release/deb-toolkit"
32+
33+
# When CARGO_TARGET is set (e.g. `x86_64-unknown-linux-musl` for a
34+
# static glibc-free build), cargo places its output under
35+
# `target/<TRIPLE>/release/` instead of the default `target/release/`.
36+
# We follow the same convention for all four crates so a single env
37+
# var swaps "default native build" for "musl static build" without
38+
# changing any other code.
39+
TARGET_SUBDIR="release"
40+
if [[ -n "${CARGO_TARGET:-}" ]]; then
41+
TARGET_SUBDIR="${CARGO_TARGET}/release"
42+
fi
43+
44+
bin_path() {
45+
# $1 = crate dir, $2 = binary name
46+
echo "${REPO_ROOT}/$1/target/${TARGET_SUBDIR}/$2"
47+
}
48+
49+
DEB_TOOLKIT_BIN="$(bin_path deb-toolkit deb-toolkit)"
3350

3451
if [[ ! -x "${DEB_TOOLKIT_BIN}" ]]; then
3552
echo "deb-toolkit release binary not found at ${DEB_TOOLKIT_BIN}." >&2
3653
echo "Run 'cargo build --release' in deb-toolkit/ first." >&2
54+
if [[ -n "${CARGO_TARGET:-}" ]]; then
55+
echo "(CARGO_TARGET=${CARGO_TARGET} is set; pass --target ${CARGO_TARGET} to cargo build.)" >&2
56+
fi
3757
exit 1
3858
fi
3959

@@ -43,10 +63,10 @@ fi
4363
STAGING="$(mktemp -d)"
4464
trap 'rm -rf "${STAGING}"' EXIT
4565

46-
install -D -m 0755 "${REPO_ROOT}/release-manager/target/release/release-manager" "${STAGING}/usr/bin/release-manager"
47-
install -D -m 0755 "${REPO_ROOT}/mina-bench-upload/target/release/mina-bench-upload" "${STAGING}/usr/bin/mina-bench-upload"
48-
install -D -m 0755 "${REPO_ROOT}/buildkite-cache-manager/target/release/buildkite-cache-manager" "${STAGING}/usr/bin/buildkite-cache-manager"
49-
install -D -m 0755 "${DEB_TOOLKIT_BIN}" "${STAGING}/usr/bin/deb-toolkit"
66+
install -D -m 0755 "$(bin_path release-manager release-manager)" "${STAGING}/usr/bin/release-manager"
67+
install -D -m 0755 "$(bin_path mina-bench-upload mina-bench-upload)" "${STAGING}/usr/bin/mina-bench-upload"
68+
install -D -m 0755 "$(bin_path buildkite-cache-manager buildkite-cache-manager)" "${STAGING}/usr/bin/buildkite-cache-manager"
69+
install -D -m 0755 "${DEB_TOOLKIT_BIN}" "${STAGING}/usr/bin/deb-toolkit"
5070

5171
mkdir -p "${OUTPUT_DIR}"
5272

0 commit comments

Comments
 (0)