From 79bb0e41d556842c9db69397e2abbe3aab358591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Geel=20Weirs=C3=B8e?= Date: Wed, 30 Sep 2026 19:18:21 +0200 Subject: [PATCH] Build everything in one cargo run, and package for the AUR Four cargo builds with four target directories compiled the GTK and GStreamer bindings four times; one build with a shared target directory takes a package build from about an hour to about seven minutes. Adds the ahfail binary and /etc/pam.d/ahfail to the install, the update_check option, an AUR PKGBUILD (offline build after cargo fetch --locked) and a tag-triggered workflow that publishes it and mirrors the PKGBUILD to the tarballz index. Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG --- .gitea/workflows/aur-publish.yml | 96 ++++++++++++++++++++++++++++++++ meson.build | 68 +++++++++++----------- meson.options | 2 + packaging/aur/PKGBUILD | 43 ++++++++++++++ packaging/aur/ahfail.install | 29 ++++++++++ 5 files changed, 205 insertions(+), 33 deletions(-) create mode 100644 .gitea/workflows/aur-publish.yml create mode 100644 meson.options create mode 100644 packaging/aur/PKGBUILD create mode 100644 packaging/aur/ahfail.install diff --git a/.gitea/workflows/aur-publish.yml b/.gitea/workflows/aur-publish.yml new file mode 100644 index 0000000..cbb2b0e --- /dev/null +++ b/.gitea/workflows/aur-publish.yml @@ -0,0 +1,96 @@ +name: Publish to AUR + +on: + push: + tags: + - 'v*' + +jobs: + aur: + runs-on: ubuntu-latest + container: + image: archlinux:base-devel + steps: + - name: Install base tools + run: pacman -Syu --noconfirm git openssh pacman-contrib nodejs sudo + + - uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: | + TAG="${GITHUB_REF#refs/tags/v}" + echo "version=$TAG" >> "$GITHUB_OUTPUT" + echo "Publishing ahfail v$TAG to AUR" + + - name: Configure SSH for AUR + run: | + mkdir -p ~/.ssh + echo "${{ secrets.AUR_SSH_PRIVATE_KEY }}" > ~/.ssh/aur + chmod 600 ~/.ssh/aur + ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null + cat > ~/.ssh/config <<'EOF' + Host aur.archlinux.org + User aur + IdentityFile ~/.ssh/aur + StrictHostKeyChecking accept-new + EOF + + - name: Create unprivileged build user + run: | + useradd -m builder + echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + mkdir -p /home/builder/.ssh + cp -r ~/.ssh/. /home/builder/.ssh/ + chown -R builder:builder /home/builder/.ssh + chmod 600 /home/builder/.ssh/aur + + - name: Clone existing AUR repo (or init new) + run: | + git clone ssh://aur@aur.archlinux.org/ahfail.git aur-pkg 2>&1 || true + if [ ! -d aur-pkg/.git ]; then + mkdir -p aur-pkg + cd aur-pkg + git init -b master + git remote add origin ssh://aur@aur.archlinux.org/ahfail.git + fi + + - name: Render PKGBUILD for this release + run: | + cp packaging/aur/PKGBUILD packaging/aur/ahfail.install aur-pkg/ + sed -i "s/^pkgver=.*/pkgver=${{ steps.version.outputs.version }}/" aur-pkg/PKGBUILD + sed -i "s/^pkgrel=.*/pkgrel=1/" aur-pkg/PKGBUILD + + - name: Update checksums and regenerate .SRCINFO + run: | + chown -R builder:builder aur-pkg + cd aur-pkg + sudo -u builder updpkgsums + sudo -u builder makepkg --printsrcinfo > .SRCINFO + + - name: Commit and push to AUR + run: | + cd aur-pkg + git config user.name "Asger Geel Weirsoe" + git config user.email "asger@weircon.dk" + git add PKGBUILD .SRCINFO ahfail.install + if git diff --staged --quiet; then + echo "No changes — nothing to push." + exit 0 + fi + git commit -m "Update to ${{ steps.version.outputs.version }}" + git push -u origin master + + # Mirror the same PKGBUILD to https://asger.weirsøe.dk/tarballz/, the + # human-readable package index. The key is pinned on the web host to a + # forced command that can only write this one file, and a failure there + # must never turn an otherwise good AUR release red. + - name: Publish PKGBUILD to the tarballz index + continue-on-error: true + env: + TARBALLZ_SSH_KEY: ${{ secrets.TARBALLZ_SSH_KEY }} + run: | + printf '%s\n' "$TARBALLZ_SSH_KEY" > ~/.ssh/tarballz + chmod 600 ~/.ssh/tarballz + ssh -i ~/.ssh/tarballz -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \ + root@192.168.10.148 < aur-pkg/PKGBUILD diff --git a/meson.build b/meson.build index 42f763a..d7091db 100644 --- a/meson.build +++ b/meson.build @@ -1,12 +1,13 @@ project( 'ahfail', ['c', 'rust'], - version: '0.1.0', + version: '0.10.0', meson_version: '>=1.3.0', default_options: ['warning_level=3', 'c_std=c11'] ) ahfail_inc = include_directories('include') + cc = meson.get_compiler('c') m_dep = cc.find_library('m', required : false) gtk_dep = dependency('gtk+-3.0') @@ -20,47 +21,48 @@ resources = gnome.compile_resources( c_name: 'ahfail' ) -# Each cargo target uses its own --target-dir to avoid parallel lock contention. -cargo_target = custom_target( +# All Rust artefacts come from ONE cargo invocation with one target directory. +# Four separate builds with four target directories compiled the whole GTK and +# GStreamer binding stack four times over (about an hour for a package build). +# +# -Dupdate_check=false (distro packages) builds without the ahfail-ui feature +# `update-check`, so the binaries never ask Gitea for new releases and the HTTP +# client is not compiled in at all. +cargo_features = get_option('update_check') ? '' : '--no-default-features' + +cargo_all = custom_target( 'ahfail-cargo-build', - input: ['crates/ahfail-gtklock/src/lib.rs', 'Cargo.toml'], - output: ['libahfail_module.a'], - command: [ - 'sh', '-c', 'cargo build --release -p ahfail-gtklock --target-dir "@OUTDIR@/target-gtklock" && cp "@OUTDIR@/target-gtklock/release/libahfail_module.a" "@OUTPUT@"' - ], - build_by_default: true -) - -# PAM module (.so with C ABI — no GResources needed, Cargo self-contained via build.rs) -pam_cargo = custom_target( - 'ahfail-pam-cargo-build', - input: ['crates/ahfail-pam/src/lib.rs', 'Cargo.toml'], - output: ['libahfail_pam.so'], + input: ['Cargo.toml', 'Cargo.lock'], + output: ['libahfail_module.a', 'libahfail_pam.so', 'ahfail-display', 'ahfail'], command: [ 'sh', '-c', - # Pass the absolute libdir so build.rs emits the correct AHFAIL_INSTALL_DIR. - # get_option('libdir') is relative ("lib"); prefix it to get an absolute path. - 'AHFAIL_LIBDIR=' + get_option('prefix') / get_option('libdir') + ' cargo build --release -p ahfail-pam --target-dir "@OUTDIR@/target-pam" && cp "@OUTDIR@/target-pam/release/libahfail_pam.so" "@OUTPUT@"' + # The PAM module's build.rs needs the absolute libdir to know where + # ahfail-display is installed; get_option('libdir') alone is relative. + 'AHFAIL_LIBDIR=' + get_option('prefix') / get_option('libdir') + + ' cargo build --release --locked' + + ' -p ahfail-gtklock -p ahfail-pam -p ahfail-display -p ahfail ' + cargo_features + + ' --target-dir "@OUTDIR@/target"' + + ' && cp "@OUTDIR@/target/release/libahfail_module.a" "@OUTPUT0@"' + + ' && cp "@OUTDIR@/target/release/libahfail_pam.so" "@OUTPUT1@"' + + ' && cp "@OUTDIR@/target/release/ahfail-display" "@OUTPUT2@"' + + ' && cp "@OUTDIR@/target/release/ahfail" "@OUTPUT3@"' ], build_by_default: true, + build_always_stale: true, # cargo decides what is out of date, not meson install: true, - install_dir: get_option('libdir') / 'ahfail', + # module archive: linked below, not installed; PAM module + display binary: + # libdir/ahfail; the ahfail command: bindir. + install_dir: [false, get_option('libdir') / 'ahfail', get_option('libdir') / 'ahfail', get_option('bindir')], install_mode: 'rwxr-xr-x' ) +cargo_target = cargo_all[0] +pam_cargo = cargo_all[1] -# Display binary (embeds GResources via its own build.rs using glib-compile-resources) -display_cargo = custom_target( - 'ahfail-display-cargo-build', - input: ['crates/ahfail-display/src/main.rs', 'Cargo.toml'], - output: ['ahfail-display'], - command: [ - 'sh', '-c', - 'cargo build --release -p ahfail-display --target-dir "@OUTDIR@/target-display" && cp "@OUTDIR@/target-display/release/ahfail-display" "@OUTPUT@"' - ], - build_by_default: true, - install: true, - install_dir: get_option('libdir') / 'ahfail', - install_mode: 'rwxr-xr-x' +# The locker's own PAM service. Shipped as its own file, like swaylock and +# gtklock do, so no other package's PAM configuration is ever touched. +install_data('data/pam.d/ahfail', + install_dir: get_option('sysconfdir') / 'pam.d', + install_mode: 'rw-r--r--' ) libahfail = shared_library( diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..286dcae --- /dev/null +++ b/meson.options @@ -0,0 +1,2 @@ +option('update_check', type: 'boolean', value: true, + description: 'Let the binaries check Gitea for new releases. Distro packages set this to false.') diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD new file mode 100644 index 0000000..5673411 --- /dev/null +++ b/packaging/aur/PKGBUILD @@ -0,0 +1,43 @@ +# Maintainer: Asger Geel Weirsoe +pkgname=ahfail +pkgver=0.0.0 +pkgrel=1 +pkgdesc="Screen locker that says 'ah ah ah, you didn't say the magic word' on a wrong password" +arch=('x86_64') +url="https://gitea.weircon.dk/agw/gtk-ahfail" +license=('AGPL-3.0-only') +# gst-plugins-bad-libs: libgstplayer. gst-plugins-base/-good are runtime plugins +# (audio conversion, the mpg123 mp3 decoder) that namcap cannot see being used. +depends=('gtk3' 'gtk-session-lock' 'gstreamer' 'gst-plugins-bad-libs' 'gst-plugins-base' + 'gst-plugins-good' 'pam' 'glib2' 'gdk-pixbuf2' 'cairo' 'pango' 'at-spi2-core' 'gst-plugins-base-libs' 'glibc' 'libgcc' 'libx11') +makedepends=('meson' 'ninja' 'cargo') +optdepends=('pipewire-pulse: raise the volume to 100% on a failed attempt (or libpulse)' + 'libpulse: raise the volume to 100% on a failed attempt' + 'swayidle: lock automatically on Wayland' + 'xss-lock: lock automatically on X11' + 'gtklock: legacy gtklock module (/usr/lib/gtklock/ahfail-module.so)') +backup=('etc/pam.d/ahfail') +install=ahfail.install +options=('!lto') +source=("$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz") +sha256sums=('SKIP') + +prepare() { + cd "$srcdir/gtk-ahfail" + export RUSTUP_TOOLCHAIN=stable + cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')" +} + +build() { + cd "$srcdir/gtk-ahfail" + export RUSTUP_TOOLCHAIN=stable + export CARGO_NET_OFFLINE=true + arch-meson . build -Dupdate_check=false + meson compile -C build +} + +package() { + cd "$srcdir/gtk-ahfail" + meson install -C build --destdir "$pkgdir" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} diff --git a/packaging/aur/ahfail.install b/packaging/aur/ahfail.install new file mode 100644 index 0000000..7f4a365 --- /dev/null +++ b/packaging/aur/ahfail.install @@ -0,0 +1,29 @@ +_ahfail_msg() { + cat <<'MSG' +==> ahfail is its own screen locker and uses its own PAM service (/etc/pam.d/ahfail). + Nothing in your other PAM files has been changed. + + Check your setup: ahfail doctor + See the effect safely: ahfail test + Lock the screen: ahfail + + Lock automatically: + Wayland: swayidle -w timeout 300 'ahfail' before-sleep 'ahfail' + X11: xss-lock -- ahfail + + The old PAM hook (auth optional .../libahfail_pam.so in e.g. /etc/pam.d/i3lock) + is no longer needed with `ahfail`. Remove that line yourself if you added it. +MSG +} + +post_install() { + _ahfail_msg +} + +post_upgrade() { + # Only when coming from before 0.10.0, when ahfail became its own locker; + # repeating the message on every update would just teach people to skip it. + if (( $(vercmp "$2" 0.10.0) < 0 )); then + _ahfail_msg + fi +}