Skip to content

Commit 05c4102

Browse files
hackgnarclaude
andcommitted
Add release workflow for prebuilt cross-arch binaries
Build stripped x86_64/aarch64/armv7 Linux binaries on tag push and attach them to a GitHub Release. Cross.toml installs the matching foreign-arch libdbus-1-dev in the cross container and the workflow verifies libdbus actually links before publishing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ba83843 commit 05c4102

2 files changed

Lines changed: 187 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
name: release
2+
3+
# Build prebuilt, stripped gratttool binaries for the common workshop targets
4+
# and attach them to a GitHub Release so attendees can download-and-run without
5+
# installing Rust or any build toolchain.
6+
7+
on:
8+
push:
9+
tags:
10+
- 'v*' # e.g. v1.0.0
11+
workflow_dispatch: # manual trigger for testing (builds + uploads CI
12+
# artifacts, but only publishes a Release on a v* tag)
13+
14+
permissions:
15+
contents: write # required to create/update the GitHub Release
16+
17+
jobs:
18+
build:
19+
name: Build ${{ matrix.artifact }}
20+
# ubuntu-22.04 is the OLDEST generally-available GitHub-hosted runner
21+
# (glibc 2.35); ubuntu-20.04 (glibc 2.31) was retired in 2025. The native
22+
# binary is dynamically linked against the runner's glibc, so building on
23+
# the oldest runner keeps it loadable on older student distros. A binary
24+
# built against a newer glibc dies at startup with
25+
# "version `GLIBC_2.xx' not found" on machines with an older libc.
26+
# (The aarch64/armv7 binaries are built inside cross's own older-glibc
27+
# images, so they get an even lower baseline regardless of this runner.)
28+
runs-on: ubuntu-22.04
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
include:
33+
# Primary: most laptops and VMs. Native build on the runner.
34+
- artifact: gratttool-linux-x86_64
35+
target: x86_64-unknown-linux-gnu
36+
cross: false
37+
strip: strip
38+
39+
# 64-bit Raspberry Pi OS, Apple Silicon Linux VMs.
40+
- artifact: gratttool-linux-aarch64
41+
target: aarch64-unknown-linux-gnu
42+
cross: true
43+
strip: aarch64-linux-gnu-strip
44+
pkg_config_path: /usr/lib/aarch64-linux-gnu/pkgconfig
45+
46+
# 32-bit Raspberry Pi OS.
47+
- artifact: gratttool-linux-armv7
48+
target: armv7-unknown-linux-gnueabihf
49+
cross: true
50+
strip: arm-linux-gnueabihf-strip
51+
pkg_config_path: /usr/lib/arm-linux-gnueabihf/pkgconfig
52+
53+
steps:
54+
- uses: actions/checkout@v4
55+
56+
- name: Install Rust toolchain
57+
uses: dtolnay/rust-toolchain@stable
58+
with:
59+
targets: ${{ matrix.target }}
60+
61+
# --- Native (x86_64): libdbus dev headers must be present so the
62+
# libdbus-sys build script's pkg-config call resolves against the host.
63+
- name: Install native build dependencies
64+
if: ${{ !matrix.cross }}
65+
run: |
66+
sudo apt-get update
67+
sudo apt-get install -y libdbus-1-dev pkg-config
68+
69+
# --- Cross: install the foreign-arch strip binaries on the runner so we
70+
# can strip the aarch64/armv7 outputs after the build (the actual libdbus
71+
# cross-link happens inside the cross container, see Cross.toml).
72+
- name: Install cross strip tools
73+
if: ${{ matrix.cross }}
74+
run: |
75+
sudo apt-get update
76+
sudo apt-get install -y binutils-aarch64-linux-gnu binutils-arm-linux-gnueabihf
77+
78+
- name: Install cross
79+
if: ${{ matrix.cross }}
80+
run: cargo install cross --locked
81+
82+
- name: Build (native)
83+
if: ${{ !matrix.cross }}
84+
run: cargo build --release --locked --target ${{ matrix.target }}
85+
86+
# PKG_CONFIG_* tell the in-container pkg-config to resolve the foreign
87+
# libdbus: allow cross probing, point at the target arch's .pc dir, and
88+
# keep the sysroot at the container root where the :arm64/:armhf package
89+
# was installed. These names are passed through to the container by the
90+
# [build.env] passthrough list in Cross.toml.
91+
- name: Build (cross)
92+
if: ${{ matrix.cross }}
93+
env:
94+
PKG_CONFIG_ALLOW_CROSS: "1"
95+
PKG_CONFIG_SYSROOT_DIR: /
96+
PKG_CONFIG_PATH: ${{ matrix.pkg_config_path }}
97+
run: cross build --release --locked --target ${{ matrix.target }}
98+
99+
# Don't assume the cross link worked — prove libdbus actually got linked.
100+
# A missing/misconfigured pkg-config would otherwise produce a binary
101+
# that fails only at runtime on an attendee's Pi.
102+
- name: Verify binary architecture and libdbus link
103+
run: |
104+
BIN="target/${{ matrix.target }}/release/gratttool"
105+
file "$BIN"
106+
if ! readelf -d "$BIN" | grep -qi 'libdbus-1'; then
107+
echo "ERROR: libdbus-1 is not in the binary's dynamic dependencies."
108+
echo "The cross pkg-config setup did not link libdbus — see Cross.toml."
109+
readelf -d "$BIN" || true
110+
exit 1
111+
fi
112+
echo "OK: libdbus-1 is linked."
113+
114+
- name: Strip binary
115+
run: |
116+
${{ matrix.strip }} "target/${{ matrix.target }}/release/gratttool"
117+
cp "target/${{ matrix.target }}/release/gratttool" "${{ matrix.artifact }}"
118+
ls -lh "${{ matrix.artifact }}"
119+
120+
- name: Upload build artifact
121+
uses: actions/upload-artifact@v4
122+
with:
123+
name: ${{ matrix.artifact }}
124+
path: ${{ matrix.artifact }}
125+
if-no-files-found: error
126+
127+
release:
128+
name: Publish GitHub Release
129+
needs: build
130+
runs-on: ubuntu-22.04
131+
# Only publish a Release for an actual tag push; a manual workflow_dispatch
132+
# run still builds + uploads CI artifacts above for testing, but skips this.
133+
if: startsWith(github.ref, 'refs/tags/v')
134+
permissions:
135+
contents: write
136+
steps:
137+
- name: Download all build artifacts
138+
uses: actions/download-artifact@v4
139+
with:
140+
path: dist
141+
merge-multiple: true # flatten every artifact into dist/
142+
143+
- name: List artifacts
144+
run: ls -lh dist
145+
146+
- name: Publish release
147+
uses: softprops/action-gh-release@v2
148+
with:
149+
files: dist/*
150+
generate_release_notes: true # auto-generate notes from commits/PRs
151+
fail_on_unmatched_files: true

Cross.toml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Cross.toml — configures `cross` for the libdbus-linked cross builds.
2+
#
3+
# gratttool -> bluer -> dbus -> libdbus-sys, whose build script calls
4+
# pkg-config to locate `dbus-1`. For a cross build that means the matching
5+
# foreign-architecture libdbus dev package must exist *inside* the cross
6+
# build container, and pkg-config must be told to look at it.
7+
#
8+
# $CROSS_DEB_ARCH is set automatically by cross to the Debian arch name for
9+
# the target (arm64 for aarch64, armhf for armv7), and the cross images have
10+
# the corresponding ports.ubuntu.com apt sources pre-configured, so the
11+
# `:$CROSS_DEB_ARCH` package actually resolves.
12+
#
13+
# The pkg-config env vars (PKG_CONFIG_ALLOW_CROSS / PKG_CONFIG_PATH /
14+
# PKG_CONFIG_SYSROOT_DIR) are set in the workflow and passed through to the
15+
# container via the [build.env] passthrough list below.
16+
17+
[build.env]
18+
passthrough = [
19+
"PKG_CONFIG_ALLOW_CROSS",
20+
"PKG_CONFIG_PATH",
21+
"PKG_CONFIG_SYSROOT_DIR",
22+
]
23+
24+
[target.aarch64-unknown-linux-gnu]
25+
pre-build = [
26+
"dpkg --add-architecture $CROSS_DEB_ARCH",
27+
"apt-get update",
28+
"apt-get install --assume-yes --no-install-recommends libdbus-1-dev:$CROSS_DEB_ARCH pkg-config",
29+
]
30+
31+
[target.armv7-unknown-linux-gnueabihf]
32+
pre-build = [
33+
"dpkg --add-architecture $CROSS_DEB_ARCH",
34+
"apt-get update",
35+
"apt-get install --assume-yes --no-install-recommends libdbus-1-dev:$CROSS_DEB_ARCH pkg-config",
36+
]

0 commit comments

Comments
 (0)