diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1693de45..e7a224d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -607,6 +607,11 @@ jobs: ext="${pkg##*.}" cp "$pkg" "dist/openlogi-${ref_name}-linux-${PKG_ARCH}.${ext}" done + # .pkg.tar.zst has a compound extension; handle it separately. + for pkg in target/release/*.pkg.tar.zst; do + [ -f "$pkg" ] || continue + cp "$pkg" "dist/openlogi-${ref_name}-linux-${PKG_ARCH}.pkg.tar.zst" + done - uses: actions/upload-artifact@v7 with: @@ -680,7 +685,7 @@ jobs: # nothing instead of erroring on a literal pattern, so the DMGs are # hashed alone. shopt -s nullglob - sha256sum *.dmg *.exe *.msi *.deb *.rpm > SHA256SUMS + sha256sum *.dmg *.exe *.msi *.deb *.rpm *.pkg.tar.zst > SHA256SUMS # softprops' `files` can't list a glob that matches nothing without # tripping fail_on_unmatched_files. The Windows exe/msi (per arch leg) @@ -698,6 +703,11 @@ jobs: echo "$ext=false" >> "$GITHUB_OUTPUT" fi done + if compgen -G "dist/*.pkg.tar.zst" > /dev/null; then + echo "pacman=true" >> "$GITHUB_OUTPUT" + else + echo "pacman=false" >> "$GITHUB_OUTPUT" + fi - name: Load static updater publishing config from 1Password id: r2_config @@ -760,7 +770,7 @@ jobs: # the globs may match nothing; the DMGs are guaranteed by the publish # gate. shopt -s nullglob - for artifact in dist/*.dmg dist/*.exe dist/*.msi dist/*.deb dist/*.rpm; do + for artifact in dist/*.dmg dist/*.exe dist/*.msi dist/*.deb dist/*.rpm dist/*.pkg.tar.zst; do minisign -S -m "$artifact" -s "$key_file" -x "$artifact.minisig" -W minisign -V -m "$artifact" -P "$OPENLOGI_UPDATE_MINISIGN_PUBLIC_KEY" -x "$artifact.minisig" done @@ -829,6 +839,7 @@ jobs: ${{ steps.artifacts.outputs.msi == 'true' && 'dist/*.msi' || '' }} ${{ steps.artifacts.outputs.deb == 'true' && 'dist/*.deb' || '' }} ${{ steps.artifacts.outputs.rpm == 'true' && 'dist/*.rpm' || '' }} + ${{ steps.artifacts.outputs.pacman == 'true' && 'dist/*.pkg.tar.zst' || '' }} fail_on_unmatched_files: true homebrew-tap: @@ -859,3 +870,82 @@ jobs: event-type: update-openlogi client-payload: | {"version": "${{ github.ref_name }}"} + + aur-check: + name: Check AUR publishing prerequisites + runs-on: ubuntu-latest + needs: publish + if: ${{ github.ref_type == 'tag' }} + outputs: + enabled: ${{ steps.check.outputs.enabled }} + steps: + - id: check + env: + AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} + run: | + if [ -n "$AUR_SSH_KEY" ]; then + echo "enabled=true" >> "$GITHUB_OUTPUT" + else + echo "enabled=false" >> "$GITHUB_OUTPUT" + fi + + aur-publish: + name: Publish openlogi-bin to AUR + runs-on: ubuntu-latest + needs: [publish, aur-check] + if: ${{ needs.aur-check.outputs.enabled == 'true' }} + container: + image: archlinux:base-devel + steps: + - uses: actions/checkout@v6 + + - name: Install build dependencies + run: pacman -Syu --noconfirm git openssh + + - name: Set up AUR SSH key + run: | + mkdir -p ~/.ssh + printf '%s\n' "$AUR_SSH_KEY" > ~/.ssh/aur + chmod 600 ~/.ssh/aur + printf 'Host aur.archlinux.org\n IdentityFile ~/.ssh/aur\n User aur\n StrictHostKeyChecking accept-new\n' >> ~/.ssh/config + env: + AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} + + - name: Compute release checksums + id: checksums + run: | + set -o pipefail + version="${GITHUB_REF_NAME#v}" + base="https://github.com/AprilNEA/OpenLogi/releases/download/${GITHUB_REF_NAME}" + sha_amd64=$(curl -fsSL "${base}/openlogi-${GITHUB_REF_NAME}-linux-amd64.pkg.tar.zst" | sha256sum | awk '{print $1}') + sha_aarch64=$(curl -fsSL "${base}/openlogi-${GITHUB_REF_NAME}-linux-arm64.pkg.tar.zst" | sha256sum | awk '{print $1}') + echo "version=${version}" >> "$GITHUB_OUTPUT" + echo "sha_amd64=${sha_amd64}" >> "$GITHUB_OUTPUT" + echo "sha_aarch64=${sha_aarch64}" >> "$GITHUB_OUTPUT" + + - name: Generate PKGBUILD and .SRCINFO + run: | + version="${{ steps.checksums.outputs.version }}" + sed \ + -e "s/@PKGVER@/${version}/g" \ + -e "s/@SHA256_AMD64@/${{ steps.checksums.outputs.sha_amd64 }}/g" \ + -e "s/@SHA256_AARCH64@/${{ steps.checksums.outputs.sha_aarch64 }}/g" \ + packaging/aur/PKGBUILD.tmpl > /tmp/PKGBUILD + cp packaging/aur/openlogi.install /tmp/openlogi.install + # Run as a non-root user — makepkg refuses to run as root. + useradd -m builder + cp /tmp/PKGBUILD /tmp/openlogi.install /home/builder/ + chown builder /home/builder/PKGBUILD /home/builder/openlogi.install + su builder -c "cd /home/builder && makepkg --printsrcinfo > .SRCINFO" + cp /home/builder/.SRCINFO /tmp/.SRCINFO + + - name: Push to AUR + run: | + git config --global user.name "OpenLogi Release Bot" + git config --global user.email "dev@aprilnea.me" + git clone ssh://aur@aur.archlinux.org/openlogi-bin.git /tmp/aur-repo + cp /tmp/PKGBUILD /tmp/.SRCINFO /tmp/openlogi.install /tmp/aur-repo/ + cd /tmp/aur-repo + git add PKGBUILD .SRCINFO openlogi.install + git commit -m "Update to ${{ github.ref_name }}" + git push diff --git a/packaging/aur/PKGBUILD.tmpl b/packaging/aur/PKGBUILD.tmpl new file mode 100644 index 00000000..8a60df15 --- /dev/null +++ b/packaging/aur/PKGBUILD.tmpl @@ -0,0 +1,34 @@ +# Maintainer: OpenLogi Contributors +pkgname=openlogi-bin +pkgver=@PKGVER@ +pkgrel=1 +pkgdesc="Logitech HID++ device control — remap buttons, DPI, and SmartShift. An open-source, local-first alternative to Logi Options+." +arch=('x86_64' 'aarch64') +url="https://github.com/AprilNEA/OpenLogi" +license=('Apache-2.0 OR MIT') +depends=('dbus' 'systemd-libs') +optdepends=('libusb: USB HID device access') +provides=('openlogi') +conflicts=('openlogi') +install=openlogi.install + +_base="https://github.com/AprilNEA/OpenLogi/releases/download/v${pkgver}" +source_x86_64=("openlogi-v${pkgver}-linux-amd64.pkg.tar.zst::${_base}/openlogi-v${pkgver}-linux-amd64.pkg.tar.zst") +sha256sums_x86_64=('@SHA256_AMD64@') +source_aarch64=("openlogi-v${pkgver}-linux-arm64.pkg.tar.zst::${_base}/openlogi-v${pkgver}-linux-arm64.pkg.tar.zst") +sha256sums_aarch64=('@SHA256_AARCH64@') + +package() { + case "$CARCH" in + x86_64) _src="openlogi-v${pkgver}-linux-amd64.pkg.tar.zst" ;; + aarch64) _src="openlogi-v${pkgver}-linux-arm64.pkg.tar.zst" ;; + esac + # Extract the nfpm-built pacman package, skipping its metadata files. + bsdtar -xf "${srcdir}/${_src}" \ + --exclude='.PKGINFO' \ + --exclude='.INSTALL' \ + --exclude='.CHANGELOG' \ + --exclude='.BUILDINFO' \ + --exclude='.MTREE' \ + -C "${pkgdir}" +} diff --git a/packaging/aur/openlogi.install b/packaging/aur/openlogi.install new file mode 100644 index 00000000..40cb6aa7 --- /dev/null +++ b/packaging/aur/openlogi.install @@ -0,0 +1,36 @@ +post_install() { + post_upgrade +} + +post_upgrade() { + SERVICE=/usr/lib/systemd/user/openlogi-agent.service + if [ -f "$SERVICE" ]; then + sed -i "s|@BINDIR@|/usr/bin|g" "$SERVICE" + fi + + if command -v udevadm > /dev/null 2>&1; then + udevadm control --reload-rules + udevadm trigger --subsystem-match=hidraw + udevadm trigger --subsystem-match=misc --attr-match=name=uinput 2>/dev/null || true + udevadm settle 2>/dev/null || true + fi + + if command -v gtk-update-icon-cache > /dev/null 2>&1; then + gtk-update-icon-cache -qtf /usr/share/icons/hicolor || true + fi + if command -v update-desktop-database > /dev/null 2>&1; then + update-desktop-database -q /usr/share/applications || true + fi + + echo "OpenLogi installed. Enable the background agent for your user with:" + echo " systemctl --user enable --now openlogi-agent.service" +} + +post_remove() { + if command -v udevadm > /dev/null 2>&1; then + udevadm control --reload-rules + udevadm trigger --subsystem-match=hidraw + udevadm trigger --subsystem-match=misc --attr-match=name=uinput 2>/dev/null || true + udevadm settle 2>/dev/null || true + fi +} diff --git a/packaging/linux/nfpm.yaml b/packaging/linux/nfpm.yaml index b4deef47..c9bbd28b 100644 --- a/packaging/linux/nfpm.yaml +++ b/packaging/linux/nfpm.yaml @@ -1,4 +1,4 @@ -# nfpm package descriptor for OpenLogi Linux packages (.deb / .rpm). +# nfpm package descriptor for OpenLogi Linux packages (.deb / .rpm / .pkg.tar.zst). # # Build with the xtask: # cargo run -p xtask -- package-linux @@ -6,6 +6,7 @@ # Or directly (requires nfpm in PATH; set VERSION and PKG_ARCH): # VERSION=0.6.0 PKG_ARCH=amd64 nfpm package --packager deb --target target/release # VERSION=0.6.0 PKG_ARCH=arm64 nfpm package --packager rpm --target target/release +# VERSION=0.6.0 PKG_ARCH=amd64 nfpm package --packager archlinux --target target/release name: openlogi version: "${VERSION}" @@ -59,6 +60,9 @@ contents: file_info: mode: 0644 +archlinux: + packager: "OpenLogi Contributors " + scripts: postinstall: packaging/linux/nfpm-scripts/postinstall.sh postremove: packaging/linux/nfpm-scripts/postremove.sh diff --git a/xtask/src/linux.rs b/xtask/src/linux.rs index 5f4bd56a..0f19655b 100644 --- a/xtask/src/linux.rs +++ b/xtask/src/linux.rs @@ -53,7 +53,7 @@ pub(crate) fn package_linux(args: &PackageLinux) -> Result<()> { other => anyhow::bail!("unsupported Linux package architecture: {other}"), }; - for packager in ["deb", "rpm"] { + for packager in ["deb", "rpm", "archlinux"] { println!("==> nfpm {packager} ({pkg_arch})"); run(ProcessCommand::new("nfpm") .args(["package", "--packager", packager, "--config"])