Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 96 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
version-consistency:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
# One script owns the list, so adding a packaging file cannot leave CI
# checking a stale subset of it.
- name: Check every version declaration agrees
Expand All @@ -56,10 +56,10 @@ jobs:
frontend:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 20
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
# npm ci, not npm install: install can resolve a tree the lockfile does not
Expand All @@ -76,7 +76,7 @@ jobs:
# Not a workspace: the manifest lives under src-tauri/.
working-directory: src-tauri
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -110,7 +110,7 @@ jobs:
audit:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- run: cargo install cargo-audit --locked

Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:
distro-detection:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Re-fetch os-release from live distro images
run: |
Expand Down Expand Up @@ -219,7 +219,7 @@ jobs:
hardware-simulation:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
Expand Down Expand Up @@ -291,9 +291,31 @@ jobs:
#
# So: build in the container, hand the artifacts to a normal runner, and let
# that runner drive Docker.
#
# Two architectures, built NATIVELY on their own runner. `runs-on` picks the
# CPU; `container` pins the glibc floor. Those are separate concerns and the
# split is what makes this work: ubuntu-24.04-arm gives real aarch64 hardware
# (free for public repos), while ubuntu:22.04 -- a multi-arch image -- keeps
# the floor at 2.35 on both.
#
# NOT cross-compiled, deliberately. Tauri's bundlers infer the architecture
# from the build host and declare none in the manifest, so `--target
# aarch64-...` would stage host artifacts under an aarch64 name and ship an
# x86_64 binary in an arm64 package. No QEMU either: emulating a WebKit GUI is
# slow and fails on graphics paths no real user touches, so a red result would
# be uninformative.
bundle:
needs: [rust, frontend]
runs-on: ubuntu-24.04
runs-on: ${{ matrix.runner }}
strategy:
# One arch failing should name that arch rather than cancel its sibling and
# hide how far the damage goes.
fail-fast: false
matrix:
include:
- { arch: x86_64, deb_arch: amd64, runner: ubuntu-24.04 }
- { arch: aarch64, deb_arch: arm64, runner: ubuntu-24.04-arm }
name: bundle (${{ matrix.arch }})
container:
image: ubuntu:22.04
defaults:
Expand All @@ -316,14 +338,34 @@ jobs:
librsvg2-dev patchelf xdg-utils
git config --global --add safe.directory "${GITHUB_WORKSPACE}"

- uses: actions/checkout@v4
- uses: actions/setup-node@v4
# The arch axis is only worth having if it is real. If GitHub renames or
# withdraws the arm label, `runs-on` silently falls back and this job would
# build x86_64 twice, publish it under an arm64 name, and stay green --
# the arm64 half of a channel the download page advertises, untested and
# indistinguishable from working.
- name: This runner really is ${{ matrix.arch }}
run: |
set -euo pipefail
actual="$(uname -m)"
if [ "${actual}" != "${{ matrix.arch }}" ]; then
echo "::error::matrix says ${{ matrix.arch }} but this runner is ${actual} - the arch axis is not testing what it claims"
exit 1
fi
echo "confirmed ${actual}"

- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 20
node-version: 22
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
# Explicit even though the action folds the matrix into its own key: an
# x86_64 cache restored into an aarch64 build poisons target/ with
# foreign object files, and the link error that follows points nowhere
# near the cause.
key: ${{ matrix.arch }}

- run: npm ci

Expand All @@ -349,9 +391,12 @@ jobs:
exit 1 ;;
esac

- uses: actions/upload-artifact@v4
# Per-arch name: a single `packages` artifact would have the two matrix
# legs racing to overwrite each other, and the loser's packages would
# simply not exist with nothing reporting it.
- uses: actions/upload-artifact@v7
with:
name: packages
name: packages-${{ matrix.arch }}
path: |
src-tauri/target/release/bundle/deb/*.deb
src-tauri/target/release/bundle/rpm/*.rpm
Expand All @@ -361,16 +406,44 @@ jobs:
# Everything above proves the code COMPILES. Not one job starts the app. A
# Tauri binary can pass every check here and still die on launch with a dlopen
# panic, or come up showing an empty window because the frontend never mounted.
#
# Run on both arches, and NOT with a thinner arm64 leg. The whole point of the
# arm64 rows is to catch a distro/arch interaction, which is exactly what a
# reduced matrix would be most likely to miss.
#
# This is also where arm64 is genuinely proven rather than assumed.
# JavaScriptCore has its own arm64 JIT, so "the native shell paints but the JS
# never runs" is an arch-specific failure mode -- and a screenshot of it looks
# like a working window. The OCR step is what distinguishes them: a blank-but-
# styled window still has hundreds of distinct colours, and only the absence of
# rendered TEXT gives it away.
gui-launch:
needs: bundle
runs-on: ubuntu-24.04
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- { arch: x86_64, runner: ubuntu-24.04 }
- { arch: aarch64, runner: ubuntu-24.04-arm }
name: gui-launch (${{ matrix.arch }})
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: This runner really is ${{ matrix.arch }}
run: |
set -euo pipefail
actual="$(uname -m)"
if [ "${actual}" != "${{ matrix.arch }}" ]; then
echo "::error::matrix says ${{ matrix.arch }} but this runner is ${actual} - the arch axis is not testing what it claims"
exit 1
fi
echo "confirmed ${actual}"

- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: packages
name: packages-${{ matrix.arch }}
path: artifacts

# The script expects Tauri's bundle layout, not a flat artifact directory.
Expand All @@ -392,9 +465,9 @@ jobs:
# just whatever reached stdout before the container was discarded.
- name: Upload screenshots, OCR text and app logs
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: gui-launch-evidence
name: gui-launch-evidence-${{ matrix.arch }}
path: build/gui-test-out/
if-no-files-found: warn
retention-days: 14
Expand All @@ -405,10 +478,10 @@ jobs:
docs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 20
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: npm ci
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-aur.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
set -euo pipefail
pacman -Sy --noconfirm --needed base-devel git openssh pacman-contrib jq

- uses: actions/checkout@v4
- uses: actions/checkout@v7

# The tag must exist and be pushed BEFORE publishing: PKGBUILD's source
# points at the tag tarball, so a queued build would 404 partway through
Expand Down
64 changes: 52 additions & 12 deletions .github/workflows/publish-copr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
# Needed for the pushed-tag check below.
git config --global --add safe.directory '*'

- uses: actions/checkout@v4
- uses: actions/checkout@v7

# Source0 points at the tag tarball, so a queued build 404s partway through
# if the tag is not pushed. Cheaper to catch here than in the build log.
Expand Down Expand Up @@ -81,21 +81,61 @@ jobs:
printf '%s\n' "$COPR_API_TOKEN" > ~/.config/copr
chmod 600 ~/.config/copr

# The spec fetches crates during %build, which mock forbids by default.
# This is a property of the PROJECT, not the spec, so it does not
# travel with the repository -- hence setting it on every publish
# rather than assuming someone did it once.
# Chroots resolved rather than hardcoded -- a fixed list goes stale
# every six months when Fedora branches.
#
# Selected PER ARCHITECTURE, and that structure is load-bearing rather
# than stylistic. The obvious one-liner -- widening the regex to
# (x86_64|aarch64) and keeping `tail -3` -- takes the last three of the
# COMBINED list, so "newest three releases" silently becomes "newest
# three chroots across both arches". Checked against a realistic chroot
# list, that yields fedora-42-x86_64, fedora-43-aarch64,
# fedora-43-x86_64: two releases of one arch, one of the other, and
# fedora-41 dropped entirely. Which arch loses coverage shifts every
# time Fedora branches, and it exits 0 either way.
#
# This list must stay in step with ExclusiveArch in the spec: a chroot
# for an arch the spec excludes is a build that fails, and an arch in
# the spec with no chroot here is simply never built. Widening
# ExclusiveArch alone ships nothing.
COPR_ARCHES=(x86_64 aarch64)
CHROOTS=()
for a in "${COPR_ARCHES[@]}"; do
mapfile -t arch_chroots < <(copr-cli list-chroots 2>/dev/null \
| grep -E "^fedora-[0-9]+-${a}$" | sort -V | tail -3)
if [ "${#arch_chroots[@]}" -eq 0 ]; then
echo "::error::no active fedora ${a} chroots found - ${a} would silently never build"
exit 1
fi
CHROOTS+=("${arch_chroots[@]}")
done
echo "chroots: ${CHROOTS[*]}"

# `copr-cli list` prints "Name: <project>" plus an indented block, not a
# bare name, so grepping for the name alone never matches and the script
# tries to create a project that already exists.
if ! copr-cli list 2>/dev/null | grep -qE "^Name:[[:space:]]+thinkutils$"; then
echo ">> creating COPR project thinkutils"
# Separate argv entries. "${CHROOTS[@]/#/--chroot }" looks equivalent
# but puts the space INSIDE one argument, so copr-cli sees a single
# unrecognised "--chroot fedora-43-x86_64" token and bails.
CHROOT_ARGS=()
for c in "${CHROOTS[@]}"; do CHROOT_ARGS+=(--chroot "${c}"); done
copr-cli create thinkutils "${CHROOT_ARGS[@]}" \
--description "ThinkPad fan control, battery care and system monitoring" \
--enable-net on
fi

# --enable-net is REQUIRED, not cosmetic: mock disables builder
# networking by default and the spec fetches crates during %build.
# It is a property of the PROJECT, so it does not travel with the
# repository -- hence setting it on every publish rather than trusting
# that someone did it once.
copr-cli modify thinkutils --enable-net on || \
echo "::warning::could not set --enable-net; the build will fail if it is off"

# Newest three x86_64 chroots, resolved rather than hardcoded: a fixed
# list goes stale every six months when Fedora branches.
chroots=$(copr-cli list-chroots 2>/dev/null \
| grep -E '^fedora-[0-9]+-x86_64$' | sort -V | tail -3)
echo "chroots: ${chroots}"

args=()
for c in ${chroots}; do args+=(-r "$c"); done
for c in "${CHROOTS[@]}"; do args+=(-r "$c"); done

copr-cli build "${args[@]}" thinkutils "${SRPM}"

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-ppa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
# build-ppa-source.sh uses `git archive HEAD`, which needs real history
# rather than a shallow clone.
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
dput ppa:vietanhng/thinkutils "${changes}"
done

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
if: always()
with:
name: ppa-source-packages
Expand Down
Loading