Skip to content

Commit a6d3073

Browse files
rayketchamclaude
andauthored
chore(release): v0.9.1 — ship .deb and .rpm installers (#90)
* chore(release): v0.9.1 — ship .deb and .rpm installers Ship native Linux installers instead of raw tar.gz tarballs. The musl static binary is now packaged as both .deb (Debian/Ubuntu) and .rpm (RHEL/Fedora/Rocky/Alma), each signed with Sigstore cosign and carrying a SLSA provenance attestation from the GitHub Actions builder. - crates/pki-client/Cargo.toml: add [package.metadata.deb] and [package.metadata.generate-rpm] with license-file, extended description, and doc-file packaging. - .github/workflows/release.yml: build musl binary, install cargo-deb and cargo-generate-rpm, produce .deb + .rpm, smoke-test both with dpkg-deb/rpm, attest + cosign sign all installer artifacts. - install.sh: detect host package format (dpkg vs rpm) and install the matching installer via the native package manager (dpkg/dnf/yum). - README.md: rewrite Install section to document apt/dnf install flow and per-installer cosign verification. - Bump workspace version 0.9.0 -> 0.9.1 (packaging-only, no code changes — binary is byte-identical to v0.9.0). Co-Authored-By: Claude <noreply@anthropic.com> * ci: build interop binary with musl to fix cross-runner GLIBC flake The interop build job built a glibc-dynamic binary on ubuntu3 (GLIBC 2.39) and uploaded it as an artifact. The downstream TLS Probe and Certificate Round-Trip jobs then downloaded that binary and ran it on Rocky Linux runners with older glibc — producing intermittent \`GLIBC_2.38 not found\` failures (issue #68). Root-cause fix: build the interop artifact statically against musl so it runs on any downstream runner regardless of the host libc version. This removes the need to keep admin-merging past the flake. - Target x86_64-unknown-linux-musl for the interop build. - Install musl-tools if missing. - Verify static linking and fail hard if the binary isn't static. - Upload from target/x86_64-unknown-linux-musl/release/pki. Co-Authored-By: Claude <noreply@anthropic.com> * ci: fix static-linking check for newer ldd output format Newer ldd (glibc 2.39+) prints 'statically linked' instead of the older 'not a dynamic executable' string. The old grep pattern only matched the old wording, so the verify step failed on a binary that was actually static. Match either string. Co-Authored-By: Claude <noreply@anthropic.com> * ci: scope cargo cache per-glibc to prevent cross-runner binary poisoning The shared cache key let glibc-2.39 ubuntu3 builds poison glibc-2.34 Rocky runs — target/debug/deps/*-test binaries cached on ubuntu3 fail on Rocky with "GLIBC_2.39 not found". The aws-lc-sys build-script cleanup only caught a subset of the blast radius. Detect the runner's glibc via ldd and include it in both the exact and restore keys so caches never cross glibc boundaries. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 486cd6d commit a6d3073

10 files changed

Lines changed: 300 additions & 156 deletions

File tree

.github/actions/rust-setup/action.yml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ runs:
6363
cargo --version
6464
echo "::endgroup::"
6565
66+
- name: Detect runner glibc
67+
id: glibc
68+
shell: bash
69+
run: |
70+
# Scope the cargo cache per-glibc so a newer-glibc runner's target/
71+
# (e.g. Ubuntu 24.04 w/ GLIBC 2.39) does not restore onto an older
72+
# runner (Rocky 9 w/ GLIBC 2.34) where pre-linked test binaries fail
73+
# with "GLIBC_2.39 not found".
74+
v="$(ldd --version 2>&1 | head -n1 | awk '{print $NF}')"
75+
echo "version=$v" >> "$GITHUB_OUTPUT"
76+
echo "runner glibc: $v"
77+
6678
- name: Cache cargo registry + build artifacts
6779
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
6880
with:
@@ -72,15 +84,15 @@ runs:
7284
~/.cargo/registry/cache
7385
~/.cargo/git/db
7486
target
75-
key: ${{ runner.os }}-cargo-${{ inputs.cache-prefix }}-${{ hashFiles('**/Cargo.lock') }}
87+
key: ${{ runner.os }}-glibc${{ steps.glibc.outputs.version }}-cargo-${{ inputs.cache-prefix }}-${{ hashFiles('**/Cargo.lock') }}
7688
restore-keys: |
77-
${{ runner.os }}-cargo-${{ inputs.cache-prefix }}-
89+
${{ runner.os }}-glibc${{ steps.glibc.outputs.version }}-cargo-${{ inputs.cache-prefix }}-
7890
7991
- name: Clean stale aws-lc-sys build scripts
8092
shell: bash
8193
run: |
82-
# aws-lc-sys build scripts cached from a runner with newer glibc (2.39)
83-
# fail on Rocky Linux runners with GLIBC_2.39 not found. Force recompile.
94+
# Belt-and-suspenders: even with glibc-scoped cache keys, nuke any
95+
# aws-lc-sys build artifacts that might have slipped through.
8496
rm -rf target/debug/build/aws-lc-sys-* target/release/build/aws-lc-sys-* 2>/dev/null || true
8597
8698
- name: Prune stale cargo caches

.github/workflows/interop.yml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,34 @@ jobs:
3333
persist-credentials: false
3434
- uses: ./.github/actions/rust-setup
3535
with:
36-
cache-prefix: interop
37-
- name: Build release binary
38-
run: cargo build --release
36+
targets: x86_64-unknown-linux-musl
37+
cache-prefix: interop-musl
38+
- name: Install musl toolchain
39+
run: |
40+
if ! command -v musl-gcc &> /dev/null; then
41+
sudo apt-get update
42+
sudo apt-get install -y musl-tools
43+
fi
44+
# Build musl-static so the artifact runs on any downstream runner regardless
45+
# of glibc version. Previously shipped a glibc-dynamic binary that failed on
46+
# Rocky runners with older libc (`GLIBC_2.38 not found`) — issue #68.
47+
- name: Build release binary (musl static)
48+
run: cargo build --release --target x86_64-unknown-linux-musl -p pki-client
49+
- name: Verify static linking
50+
run: |
51+
ldd_out=$(ldd target/x86_64-unknown-linux-musl/release/pki 2>&1 || true)
52+
if echo "$ldd_out" | grep -Eq "statically linked|not a dynamic"; then
53+
echo "OK: statically linked"
54+
else
55+
echo "ERROR: binary is not fully static"
56+
echo "$ldd_out"
57+
exit 1
58+
fi
3959
- name: Upload binary
4060
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
4161
with:
4262
name: pki-binary
43-
path: target/release/pki
63+
path: target/x86_64-unknown-linux-musl/release/pki
4464
retention-days: 1
4565

4666
tls-probe:

.github/workflows/release.yml

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ env:
1212

1313
jobs:
1414
build-linux:
15-
name: Build Linux (static musl)
15+
name: Build Linux installers (.deb + .rpm)
1616
# Pinned to ubuntu3-runner: musl-tools not available on Rocky Linux
1717
runs-on: [self-hosted, Linux]
18-
timeout-minutes: 30
18+
timeout-minutes: 45
1919
steps:
2020
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2121
with:
@@ -30,28 +30,74 @@ jobs:
3030
sudo apt-get update
3131
sudo apt-get install -y musl-tools
3232
fi
33+
- name: Install cargo-deb and cargo-generate-rpm
34+
run: |
35+
if ! command -v cargo-deb &> /dev/null; then
36+
cargo install --locked cargo-deb@2.7.0
37+
fi
38+
if ! command -v cargo-generate-rpm &> /dev/null; then
39+
cargo install --locked cargo-generate-rpm@0.14.1
40+
fi
3341
- name: Build static binary (with PQC support)
34-
run: cargo build --release --features pqc --target x86_64-unknown-linux-musl
42+
run: cargo build --release --features pqc --target x86_64-unknown-linux-musl -p pki-client
3543
- name: Strip binary
3644
run: strip target/x86_64-unknown-linux-musl/release/pki
3745
- name: Verify static linking
3846
run: |
39-
if ldd target/x86_64-unknown-linux-musl/release/pki 2>&1 | grep -q "not a dynamic"; then
47+
ldd_out=$(ldd target/x86_64-unknown-linux-musl/release/pki 2>&1 || true)
48+
if echo "$ldd_out" | grep -Eq "statically linked|not a dynamic"; then
4049
echo "OK: statically linked"
4150
else
42-
echo "WARN: may not be fully static"
51+
echo "ERROR: binary is not fully static"
52+
echo "$ldd_out"
53+
exit 1
4354
fi
4455
ls -lh target/x86_64-unknown-linux-musl/release/pki
45-
- name: Package
56+
57+
# ── Build .deb installer ──────────────────────────────────────
58+
- name: Build .deb installer
59+
env:
60+
TAG_NAME: ${{ github.ref_name }}
61+
run: |
62+
VERSION="${TAG_NAME#v}"
63+
cargo deb --no-build --no-strip \
64+
--target x86_64-unknown-linux-musl \
65+
-p pki-client \
66+
--output "pki-client_${VERSION}_amd64.deb"
67+
ls -lh pki-client_*.deb
68+
69+
# ── Build .rpm installer ──────────────────────────────────────
70+
- name: Build .rpm installer
4671
env:
4772
TAG_NAME: ${{ github.ref_name }}
4873
run: |
49-
cd target/x86_64-unknown-linux-musl/release
50-
tar czf "../../../pki-${TAG_NAME}-x86_64-linux.tar.gz" pki
74+
VERSION="${TAG_NAME#v}"
75+
cargo generate-rpm \
76+
--target x86_64-unknown-linux-musl \
77+
-p crates/pki-client \
78+
--output "pki-client-${VERSION}-1.x86_64.rpm"
79+
ls -lh pki-client-*.rpm
80+
81+
# ── Smoke-test installers ─────────────────────────────────────
82+
- name: Smoke-test .deb (dpkg-deb inspection)
83+
run: |
84+
dpkg-deb --info pki-client_*.deb
85+
dpkg-deb --contents pki-client_*.deb
86+
- name: Smoke-test .rpm (rpm inspection)
87+
run: |
88+
if command -v rpm &> /dev/null; then
89+
rpm -qip pki-client-*.rpm
90+
rpm -qlp pki-client-*.rpm
91+
else
92+
echo "rpm not installed on runner — skipping inspection"
93+
fi
94+
5195
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
5296
with:
53-
name: pki-linux
54-
path: pki-*.tar.gz
97+
name: pki-linux-installers
98+
path: |
99+
pki-client_*.deb
100+
pki-client-*.rpm
55101
56102
sign-and-release:
57103
name: Sign and Release
@@ -87,13 +133,17 @@ jobs:
87133
fi
88134
fi
89135
- name: Generate checksums
90-
run: sha256sum pki-*.tar.gz > SHA256SUMS.txt
136+
run: sha256sum pki-client_*.deb pki-client-*.rpm > SHA256SUMS.txt
91137

92138
# --- Supply chain security: SLSA provenance ---
93-
- name: Attest build provenance (tarball)
139+
- name: Attest build provenance (.deb)
140+
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
141+
with:
142+
subject-path: pki-client_*.deb
143+
- name: Attest build provenance (.rpm)
94144
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
95145
with:
96-
subject-path: pki-*.tar.gz
146+
subject-path: pki-client-*.rpm
97147
- name: Attest build provenance (checksums)
98148
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
99149
with:
@@ -102,9 +152,9 @@ jobs:
102152
# --- Supply chain security: Sigstore cosign signing ---
103153
- name: Install cosign
104154
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3
105-
- name: Sign artifacts with cosign (keyless)
155+
- name: Sign installers with cosign (keyless)
106156
run: |
107-
for f in pki-*.tar.gz SHA256SUMS.txt; do
157+
for f in pki-client_*.deb pki-client-*.rpm SHA256SUMS.txt; do
108158
cosign sign-blob --yes --bundle "${f}.bundle" "$f"
109159
done
110160
@@ -116,4 +166,4 @@ jobs:
116166
gh release create "$TAG_NAME" \
117167
--title "$TAG_NAME" \
118168
--generate-notes \
119-
pki-*.tar.gz SHA256SUMS.txt *.bundle
169+
pki-client_*.deb pki-client-*.rpm SHA256SUMS.txt *.bundle

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.9.1] - 2026-04-19
11+
12+
### Changed
13+
14+
- **Release artifacts are now native Linux installers (`.deb` + `.rpm`)** instead of raw `tar.gz` tarballs. Installers ship the statically-linked musl binary, register with the host package manager (`dpkg`/`rpm`), and install `pki` to `/usr/bin/pki`. `apt remove pki-client` / `dnf remove pki-client` now work cleanly.
15+
- `install.sh` detects the host package format (`dpkg` or `rpm`) and installs the matching installer via the native package manager.
16+
- `README.md` install section rewritten to document the new `.deb`/`.rpm` flow.
17+
- `.github/workflows/release.yml` rewritten to build both installers via `cargo-deb` and `cargo-generate-rpm`, smoke-test them with `dpkg-deb`/`rpm -qip`, and sign each package artifact with Sigstore cosign + SLSA provenance.
18+
19+
### Added
20+
21+
- `[package.metadata.deb]` and `[package.metadata.generate-rpm]` sections on `crates/pki-client/Cargo.toml` — drive installer generation with full license-file, extended-description, and doc-file packaging.
22+
23+
### Notes
24+
25+
- v0.9.1 is a **packaging-only** release on top of v0.9.0. No code changes; the binary inside the installer is byte-identical to the `v0.9.0` musl build.
26+
- If you prefer a raw static binary, extract it from the installer: `dpkg-deb -x pki-client_0.9.1_amd64.deb out/` or `rpm2cpio pki-client-0.9.1-1.x86_64.rpm | cpio -idmv`.
27+
1028
## [0.9.0] - 2026-04-19
1129

1230
### BREAKING CHANGES

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Modern PKI CLI tool -- certificate inspection, key management, TLS probing, compliance validation, DANE, chain building.
44

5-
**Version:** 0.9.0 | **Binary:** `pki` | **License:** Apache-2.0
5+
**Version:** 0.9.1 | **Binary:** `pki` | **License:** Apache-2.0
66

77
---
88

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ members = [
1010
]
1111

1212
[workspace.package]
13-
version = "0.9.0"
13+
version = "0.9.1"
1414
edition = "2021"
1515
license = "Apache-2.0"
1616
rust-version = "1.88.0"

0 commit comments

Comments
 (0)