Skip to content

Commit 51eb6e4

Browse files
authored
Merge pull request #3228 from wheels-dev/release/4.0.5-to-main
Release 4.0.5
2 parents 7cd32a7 + 04a4775 commit 51eb6e4

8 files changed

Lines changed: 375 additions & 50 deletions

File tree

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
name: Distribution install smoke (brew / scoop / apt / yum)
2+
3+
# End-to-end guardian for the four public package-manager install vectors.
4+
#
5+
# Every Wheels release fans out to Homebrew, Scoop, apt.wheels.dev and
6+
# yum.wheels.dev through independent downstream repos + workflows. Those break
7+
# in ways the release build itself can't see — and only surface when a user
8+
# can't install:
9+
# - the Homebrew formula auto-update dies when LuCLI ships a binary-less tag
10+
# (recurring; homebrew-wheels#383/#384)
11+
# - the apt stable Packages index gets clobbered to 0 bytes by a bleeding-edge
12+
# publish (#3218 — and the arm64 stable index is empty right now)
13+
# - a tap PR never merges / a dispatch token loses scope, leaving a channel
14+
# stuck on an old version
15+
#
16+
# This workflow installs the CLI the exact documented way on each channel and
17+
# asserts `wheels --version` reports the current GA. It runs daily (propagation
18+
# has settled by then) and on demand. It does NOT run on `release: published`
19+
# on purpose — right after a tag the channels lag, which would be a false red;
20+
# the daily run is the signal.
21+
#
22+
# Java is NOT set up by hand anywhere: every package declares/bundles it
23+
# (brew `depends_on "openjdk@21"`, the .deb `Depends: openjdk-21-jre-headless`,
24+
# the scoop manifest inlines OpenJDK, the .rpm Requires java-21) — so a missing
25+
# Java here is itself a real packaging regression worth catching.
26+
27+
on:
28+
schedule:
29+
# Daily 14:00 UTC. Runs on the default branch (develop). Far enough after
30+
# any release that all four channels have propagated.
31+
- cron: '0 14 * * *'
32+
workflow_dispatch:
33+
inputs:
34+
expected_version:
35+
description: "Version every channel must serve (blank = latest GA tag)"
36+
required: false
37+
default: ""
38+
pull_request:
39+
branches: [develop]
40+
paths:
41+
# Self-test when the workflow itself changes.
42+
- '.github/workflows/distribution-install-smoke.yml'
43+
44+
permissions:
45+
contents: read
46+
47+
jobs:
48+
resolve:
49+
name: Resolve expected version
50+
runs-on: ubuntu-latest
51+
outputs:
52+
version: ${{ steps.v.outputs.version }}
53+
steps:
54+
- name: Determine the GA version each channel must serve
55+
id: v
56+
env:
57+
GH_TOKEN: ${{ github.token }}
58+
REPO: ${{ github.repository }}
59+
# Untrusted (workflow_dispatch input) — kept in env and validated to
60+
# semver below; never interpolated straight into a shell command.
61+
INPUT: ${{ github.event.inputs.expected_version }}
62+
run: |
63+
set -euo pipefail
64+
if [ -n "${INPUT:-}" ]; then
65+
VER="${INPUT#v}"
66+
echo "Using dispatch input: $VER"
67+
else
68+
VER="$(gh release view --repo "$REPO" --json tagName -q .tagName | sed 's/^v//')"
69+
echo "Latest GA tag: $VER"
70+
fi
71+
if ! printf '%s' "$VER" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
72+
echo "::error::could not resolve a clean semver expected version (got '$VER')"
73+
exit 1
74+
fi
75+
echo "version=$VER" >> "$GITHUB_OUTPUT"
76+
echo "All channels must serve wheels $VER"
77+
78+
homebrew:
79+
needs: resolve
80+
name: "Homebrew (${{ matrix.os }})"
81+
runs-on: ${{ matrix.os }}
82+
timeout-minutes: 20
83+
strategy:
84+
fail-fast: false
85+
matrix:
86+
# macos-latest is Apple Silicon (arm64); ubuntu-latest exercises the
87+
# Linuxbrew path (amd64). Both are documented install targets.
88+
os: [macos-latest, ubuntu-latest]
89+
env:
90+
EXPECTED: ${{ needs.resolve.outputs.version }}
91+
HOMEBREW_NO_AUTO_UPDATE: "1"
92+
HOMEBREW_NO_INSTALL_FROM_API: "1"
93+
NONINTERACTIVE: "1"
94+
steps:
95+
# GitHub's ubuntu runners ship Homebrew (Linuxbrew) but do NOT put it on
96+
# PATH; macos runners do. Add it on Linux so `brew` resolves in the next
97+
# steps (GITHUB_PATH persists across steps).
98+
- name: Ensure Homebrew is on PATH (Linuxbrew)
99+
if: runner.os == 'Linux'
100+
run: echo "/home/linuxbrew/.linuxbrew/bin" >> "$GITHUB_PATH"
101+
- name: brew tap + install (the documented path)
102+
run: |
103+
set -euo pipefail
104+
brew tap wheels-dev/wheels
105+
# Newer Homebrew refuses to load a formula from a third-party tap until
106+
# it's trusted — a hard error on Linuxbrew, a warning on macOS. (Worth
107+
# surfacing in the tap's install docs for Linuxbrew users.)
108+
brew trust wheels-dev/wheels || true
109+
brew install wheels
110+
- name: Assert wheels --version == GA
111+
env:
112+
CHANNEL: "Homebrew (${{ matrix.os }})"
113+
run: |
114+
set -uo pipefail
115+
RAW="$(wheels --version 2>&1 || true)"
116+
# Extract the first semver from the output. Channels differ in format:
117+
# the LuCLI/brew build prints "Wheels Version: 4.0.4" (+ an ASCII
118+
# banner), the .deb/.rpm print "wheels 4.0.4 (stable)" — neither a
119+
# keyword filter nor a fixed prefix is portable, so take the first
120+
# x.y.z (the version always leads; the banner carries no semver).
121+
GOT="$(printf '%s\n' "$RAW" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
122+
echo "${CHANNEL}: wheels --version => '${GOT}' (expected '${EXPECTED}')"
123+
if [ "$GOT" != "$EXPECTED" ]; then
124+
echo "::error::${CHANNEL} serves '${GOT}', expected '${EXPECTED}'"
125+
echo "--- raw wheels --version ---"; printf '%s\n' "$RAW" | head -20
126+
exit 1
127+
fi
128+
129+
scoop:
130+
needs: resolve
131+
name: "Scoop (windows)"
132+
runs-on: windows-latest
133+
timeout-minutes: 20
134+
env:
135+
EXPECTED: ${{ needs.resolve.outputs.version }}
136+
steps:
137+
- name: scoop bucket add + install (the documented path)
138+
shell: pwsh
139+
run: |
140+
$ErrorActionPreference = 'Stop'
141+
if (-not (Get-Command scoop -ErrorAction SilentlyContinue)) {
142+
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
143+
}
144+
scoop bucket add wheels https://github.com/wheels-dev/scoop-wheels
145+
scoop install wheels
146+
# scoop puts shims in ~\scoop\shims and edits the *persistent* user
147+
# PATH (registry) — which the next step's shell does not inherit.
148+
# Expose the shims dir to later steps explicitly so `wheels` resolves.
149+
"$env:USERPROFILE\scoop\shims" | Out-File -FilePath $env:GITHUB_PATH -Append -Encoding utf8
150+
- name: Assert wheels --version == GA
151+
shell: pwsh
152+
run: |
153+
$ErrorActionPreference = 'Stop'
154+
$raw = (wheels --version 2>&1 | Out-String)
155+
$got = [regex]::Match($raw, '\d+\.\d+\.\d+').Value
156+
Write-Host "Scoop: wheels --version => '$got' (expected '$env:EXPECTED')"
157+
if ($got -ne $env:EXPECTED) {
158+
Write-Host "::error::Scoop serves '$got', expected '$env:EXPECTED'"
159+
Write-Host "--- raw wheels --version ---"
160+
Write-Host $raw
161+
exit 1
162+
}
163+
164+
apt:
165+
needs: resolve
166+
name: "apt (${{ matrix.arch }})"
167+
runs-on: ${{ matrix.runner }}
168+
timeout-minutes: 15
169+
# arm64 stable is allowed to fail for now: the stable arm64 Packages index
170+
# is currently empty (the per-channel clobber bug, #3218), so `apt install`
171+
# 404s on arm64. continue-on-error surfaces it (annotation + neutral result)
172+
# without blocking the suite. Flip to a hard failure once the arm64 index is
173+
# repaired.
174+
continue-on-error: ${{ matrix.arch == 'arm64' }}
175+
strategy:
176+
fail-fast: false
177+
matrix:
178+
include:
179+
- arch: amd64
180+
runner: ubuntu-latest
181+
- arch: arm64
182+
runner: ubuntu-24.04-arm
183+
env:
184+
EXPECTED: ${{ needs.resolve.outputs.version }}
185+
DEBIAN_FRONTEND: noninteractive
186+
steps:
187+
- name: Add apt.wheels.dev + install
188+
run: |
189+
set -euo pipefail
190+
# The published wheels.gpg is ASCII-armored, so dearmor it into the
191+
# keyring. (The apt-wheels README still shows a bare `tee` of the
192+
# armored key — the user-facing docs should be updated to dearmor to
193+
# match this; tracked as a follow-up.)
194+
curl -fsSL https://apt.wheels.dev/wheels.gpg | sudo gpg --dearmor -o /usr/share/keyrings/wheels.gpg
195+
echo "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev stable main" \
196+
| sudo tee /etc/apt/sources.list.d/wheels.list >/dev/null
197+
sudo apt-get update
198+
sudo apt-get install -y wheels
199+
- name: Assert wheels --version == GA
200+
env:
201+
CHANNEL: "apt (${{ matrix.arch }})"
202+
run: |
203+
set -uo pipefail
204+
RAW="$(wheels --version 2>&1 || true)"
205+
# Extract the first semver from the output. Channels differ in format:
206+
# the LuCLI/brew build prints "Wheels Version: 4.0.4" (+ an ASCII
207+
# banner), the .deb/.rpm print "wheels 4.0.4 (stable)" — neither a
208+
# keyword filter nor a fixed prefix is portable, so take the first
209+
# x.y.z (the version always leads; the banner carries no semver).
210+
GOT="$(printf '%s\n' "$RAW" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
211+
echo "${CHANNEL}: wheels --version => '${GOT}' (expected '${EXPECTED}')"
212+
if [ "$GOT" != "$EXPECTED" ]; then
213+
echo "::error::${CHANNEL} serves '${GOT}', expected '${EXPECTED}'"
214+
echo "--- raw wheels --version ---"; printf '%s\n' "$RAW" | head -20
215+
exit 1
216+
fi
217+
218+
yum:
219+
needs: resolve
220+
name: "yum (rocky 9)"
221+
runs-on: ubuntu-latest
222+
timeout-minutes: 15
223+
# RHEL-family, dnf4 — matches the documented `dnf config-manager --add-repo`
224+
# path. The release ships an x86_64 rpm, which this amd64 container matches.
225+
#
226+
# continue-on-error for now: the rpm installs cleanly and pulls in
227+
# java-21-openjdk-headless, but `wheels --version` reports "cannot find a
228+
# Java 21 runtime" — the wrapper's Java detection doesn't locate Rocky 9's
229+
# headless JRE (the headless package registers no /usr/bin/java alternative).
230+
# That's a real yum-vector packaging bug to fix in the rpm/wrapper; surfaced
231+
# here, non-blocking until then. Flip to a hard failure once fixed.
232+
continue-on-error: true
233+
container:
234+
image: rockylinux:9
235+
env:
236+
EXPECTED: ${{ needs.resolve.outputs.version }}
237+
steps:
238+
- name: Add yum.wheels.dev + install (the documented path)
239+
run: |
240+
set -euo pipefail
241+
dnf -y install dnf-plugins-core
242+
dnf -y config-manager --add-repo https://yum.wheels.dev/wheels.repo
243+
dnf -y install wheels
244+
- name: Assert wheels --version == GA
245+
env:
246+
CHANNEL: "yum (rocky 9)"
247+
run: |
248+
set -uo pipefail
249+
RAW="$(wheels --version 2>&1 || true)"
250+
# Extract the first semver from the output. Channels differ in format:
251+
# the LuCLI/brew build prints "Wheels Version: 4.0.4" (+ an ASCII
252+
# banner), the .deb/.rpm print "wheels 4.0.4 (stable)" — neither a
253+
# keyword filter nor a fixed prefix is portable, so take the first
254+
# x.y.z (the version always leads; the banner carries no semver).
255+
GOT="$(printf '%s\n' "$RAW" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
256+
echo "${CHANNEL}: wheels --version => '${GOT}' (expected '${EXPECTED}')"
257+
if [ "$GOT" != "$EXPECTED" ]; then
258+
echo "::error::${CHANNEL} serves '${GOT}', expected '${EXPECTED}'"
259+
echo "--- raw wheels --version ---"; printf '%s\n' "$RAW" | head -20
260+
exit 1
261+
fi

.github/workflows/release.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ jobs:
331331
# Build .deb / .rpm Linux packages alongside the zip artifacts. nfpm
332332
# needs no Linux-specific build host (it's a single Go binary), so this
333333
# runs on the same ubuntu-latest runner as the rest of the pipeline.
334-
# Generates wheels_<v>_amd64.deb and wheels-<v>.x86_64.rpm in dist/.
334+
# Generates arch-independent wheels_<v>_all.deb and wheels-<v>.noarch.rpm.
335335
# See tools/distribution-drafts/linux-packages/ for the nfpm configs.
336336
- name: Install nfpm
337337
run: |
@@ -484,8 +484,8 @@ jobs:
484484
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip
485485
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.md5
486486
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.sha512
487-
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*_amd64.deb
488-
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*.x86_64.rpm
487+
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*_all.deb
488+
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*.noarch.rpm
489489
490490
#############################################
491491
# Create Snapshot Pre-Release (develop only)
@@ -540,8 +540,8 @@ jobs:
540540
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip
541541
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.md5
542542
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.sha512
543-
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*_amd64.deb
544-
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*.x86_64.rpm
543+
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*_all.deb
544+
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*.noarch.rpm
545545
546546
#############################################
547547
# Dispatch downstream package managers

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ All historical references to "CFWheels" in this changelog have been preserved fo
2626

2727
---
2828

29+
# [4.0.5](https://github.com/wheels-dev/wheels/releases/tag/v4.0.5) => 2026-06-19
30+
31+
### Added
32+
33+
- Linux `.deb` / `.rpm` packages are now architecture-independent (`all` / `noarch`) and install on **arm64 (aarch64)** as well as amd64 — the package launches the CLI through a portable `java -jar` launcher instead of an amd64-only native binary, so `apt install wheels` / `dnf install wheels` now work on arm64 hosts (#3223)
34+
35+
### Fixed
36+
37+
- The Wheels CLI `.rpm` now starts on RHEL-family distributions (Rocky Linux, Fedora, AlmaLinux): the `/usr/bin/wheels` wrapper's Java-21 probe only checked Debian/Ubuntu paths, so `wheels` failed with "cannot find a Java 21 runtime" even with `java-21-openjdk-headless` installed. It now also resolves the RHEL/Fedora JRE layout (and falls back to `java` on `PATH`) (#3223)
38+
39+
---
40+
2941
# [4.0.4](https://github.com/wheels-dev/wheels/releases/tag/v4.0.4) => 2026-06-18
3042

3143
### Added

0 commit comments

Comments
 (0)