commit 6f7398075ade49601c7b54a0310f3777db5059bb Author: Felitendo <95575686+Felitendo@users.noreply.github.com> Date: Fri Jul 17 09:44:50 2026 +0200 Add timetable-bin with automated AUR update workflow diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml new file mode 100644 index 0000000..19f8839 --- /dev/null +++ b/.github/workflows/update.yml @@ -0,0 +1,55 @@ +name: Update AUR packages + +on: + schedule: + # every 6 hours (offset from the full hour to avoid GitHub's load spikes) + - cron: '37 */6 * * *' + workflow_dispatch: + push: + branches: [main] + paths: + - '*/PKGBUILD' + - '*/pkg.sh' + - 'scripts/**' + - '.github/workflows/update.yml' + +permissions: + contents: write + +jobs: + discover: + runs-on: ubuntu-latest + outputs: + packages: ${{ steps.list.outputs.packages }} + steps: + - uses: actions/checkout@v4 + - id: list + run: | + echo "packages=$(ls -d */PKGBUILD 2>/dev/null | xargs -r -n1 dirname | jq -R . | jq -cs .)" >> "$GITHUB_OUTPUT" + + update: + needs: discover + if: needs.discover.outputs.packages != '[]' + runs-on: ubuntu-latest + container: + image: archlinux:base-devel + strategy: + fail-fast: false + # serialize so parallel jobs don't race on pushing to this repository + max-parallel: 1 + matrix: + package: ${{ fromJson(needs.discover.outputs.packages) }} + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Install base tooling + run: pacman -Syu --noconfirm --needed git openssh github-cli jq zstd + + - uses: actions/checkout@v4 + + - name: Update ${{ matrix.package }} + env: + AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + AUR_GIT_NAME: ${{ vars.AUR_GIT_NAME }} + AUR_GIT_EMAIL: ${{ vars.AUR_GIT_EMAIL }} + run: bash scripts/update-package.sh "${{ matrix.package }}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fae042 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# makepkg build leftovers and downloaded/built artifacts +*.tar.zst +*.pkg.tar.* +*/src/ +*/pkg/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..76257d3 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# PKGBUILDS + +Automated `-bin` packages for the [AUR](https://aur.archlinux.org), kept up to +date by GitHub Actions. + +Every 6 hours the [update workflow](.github/workflows/update.yml) checks each +package's upstream for a new release. When one is found it: + +1. builds the software in an Arch Linux container, +2. publishes the resulting install tree as a `.tar.zst` asset on a GitHub + release of **this** repository (tag `-`), +3. updates the `PKGBUILD` (pkgver/pkgrel/sha256sums), test-builds it with + `makepkg` and regenerates `.SRCINFO`, +4. commits the changes back to this repository, and +5. pushes `PKGBUILD` + `.SRCINFO` to the AUR. + +The AUR packages themselves only download the prebuilt asset from step 2, so +users don't need any build dependencies. + +## Packages + +| Package | Upstream | AUR | +|---|---|---| +| `timetable-bin` | [ostfriese4/untis](https://codeberg.org/ostfriese4/untis) — "Timetable", a GTK4 + LibAdwaita client for WebUntis | [timetable-bin](https://aur.archlinux.org/packages/timetable-bin) | + +## Setup (one-time) + +1. Create an [AUR account](https://aur.archlinux.org/register) and add an SSH + public key to it (AUR account settings). +2. Add the matching **private** key as a repository secret named + `AUR_SSH_PRIVATE_KEY` + (Settings → Secrets and variables → Actions → New repository secret). +3. Optionally set the repository **variables** `AUR_GIT_NAME` and + `AUR_GIT_EMAIL` to control the commit identity used on the AUR + (defaults: `Felitendo` / `95575686+Felitendo@users.noreply.github.com`). + +The first push to `ssh://aur@aur.archlinux.org/.git` creates the AUR +package automatically. + +## Adding a package + +Create a new directory named after the AUR package containing: + +- **`PKGBUILD`** — sources the prebuilt asset from + `https://github.com/Felitendo/PKGBUILDS/releases/download/-/-.tar.zst`. + `pkgver`, `pkgrel` and `sha256sums` are maintained by CI. +- **`pkg.sh`** — bash sourced by [scripts/update-package.sh](scripts/update-package.sh), + providing: + - `BUILD_DEPS` — array of Arch packages installed before building, + - `latest_version` — prints the latest upstream version (no `v` prefix), + - `build_artifact ` — downloads/builds upstream and + writes the install tree (a tarball containing `usr/`) to ``. + +The workflow discovers package directories automatically. Trigger a run +manually via *Actions → Update AUR packages → Run workflow* to publish it +immediately. diff --git a/scripts/update-package.sh b/scripts/update-package.sh new file mode 100644 index 0000000..e6c7da2 --- /dev/null +++ b/scripts/update-package.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# Update one package directory: +# 1. determine the latest upstream version (pkg.sh: latest_version) +# 2. build the binary artifact if the matching GitHub release asset is +# missing (pkg.sh: build_artifact), otherwise reuse the published one +# 3. refresh PKGBUILD (pkgver/pkgrel/sha256sums), test-build it with +# makepkg and regenerate .SRCINFO +# 4. commit changes back to this repository +# 5. push PKGBUILD + .SRCINFO to the AUR +# +# Requires: GH_TOKEN (GitHub release + repo push), AUR_SSH_PRIVATE_KEY. +# Optional: AUR_GIT_NAME / AUR_GIT_EMAIL for the AUR commit identity. +set -euo pipefail + +pkg="${1:?usage: update-package.sh }" +repo_root="$(cd "$(dirname "$0")/.." && pwd)" +cd "$repo_root" +pkg="${pkg%/}" + +source "$pkg/pkg.sh" + +ver="$(latest_version || true)" +if [[ -z "$ver" || "$ver" == "null" ]]; then + echo "::error::$pkg: could not determine the latest upstream version" + exit 1 +fi +echo "$pkg: latest upstream version is $ver" + +oldver="$(grep -Po '^pkgver=\K.*' "$pkg/PKGBUILD")" +oldrel="$(grep -Po '^pkgrel=\K.*' "$pkg/PKGBUILD")" +oldsha="$(grep -Po "^sha256sums=\('\K[0-9a-f]{64}" "$pkg/PKGBUILD" || true)" + +tag="$pkg-$ver" +asset="$pkg-$ver.tar.zst" + +### 1+2: make sure the release asset exists, get a local copy of it ######### + +if gh release view "$tag" --json assets -q '.assets[].name' 2>/dev/null | grep -qxF "$asset"; then + echo "$pkg: release $tag already contains $asset - reusing it" + gh release download "$tag" --pattern "$asset" --dir "$pkg" --clobber +else + echo "$pkg: building $asset" + if [[ "${CI:-}" == "true" && "${#BUILD_DEPS[@]}" -gt 0 ]]; then + pacman -S --noconfirm --needed "${BUILD_DEPS[@]}" + fi + build_artifact "$ver" "$repo_root/$pkg/$asset" + gh release view "$tag" >/dev/null 2>&1 || \ + gh release create "$tag" --title "$tag" \ + --notes "Automated build of $pkg $ver (upstream: ${UPSTREAM_REPO:-unknown})." + gh release upload "$tag" "$pkg/$asset" --clobber +fi + +sha="$(sha256sum "$pkg/$asset" | cut -d' ' -f1)" + +### 3: refresh PKGBUILD, test-build, regenerate .SRCINFO #################### + +if [[ "$ver" != "$oldver" ]]; then + rel=1 +elif [[ "$sha" != "$oldsha" ]]; then + rel=$((oldrel + 1)) +else + rel="$oldrel" +fi + +sed -i \ + -e "s|^pkgver=.*|pkgver=$ver|" \ + -e "s|^pkgrel=.*|pkgrel=$rel|" \ + -e "s|^sha256sums=.*|sha256sums=('$sha')|" \ + "$pkg/PKGBUILD" + +# makepkg refuses to run as root (the CI container), so hand the build to an +# unprivileged user there. -d: the runner only needs to package, not run. +if [[ "$EUID" -eq 0 ]]; then + useradd -m builder 2>/dev/null || true + chown -R builder "$pkg" + (cd "$pkg" && runuser -u builder -- makepkg -fdc) + (cd "$pkg" && runuser -u builder -- makepkg --printsrcinfo > .SRCINFO) + chown -R 0:0 "$pkg" +else + (cd "$pkg" && makepkg -fdc) + (cd "$pkg" && makepkg --printsrcinfo > .SRCINFO) +fi +echo "$pkg: makepkg test build succeeded" +rm -f "$pkg/$asset" "$pkg"/*.pkg.tar.* + +### 4: commit back to this repository ######################################## + +if [[ "${CI:-}" == "true" ]]; then + git config --global --add safe.directory "$repo_root" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git add "$pkg/PKGBUILD" "$pkg/.SRCINFO" + if git diff --cached --quiet; then + echo "$pkg: no changes to commit" + else + git commit -m "$pkg: update to $ver-$rel [skip ci]" + git pull --rebase origin "${GITHUB_REF_NAME:-main}" + git push origin "HEAD:${GITHUB_REF_NAME:-main}" + fi +fi + +### 5: push to the AUR ####################################################### + +if [[ -z "${AUR_SSH_PRIVATE_KEY:-}" ]]; then + echo "::error::$pkg: AUR_SSH_PRIVATE_KEY is not set - cannot push to the AUR." \ + "Add your AUR SSH private key as a repository secret named AUR_SSH_PRIVATE_KEY." + exit 1 +fi + +mkdir -p ~/.ssh && chmod 700 ~/.ssh +printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur_key +chmod 600 ~/.ssh/aur_key +cat >> ~/.ssh/config <<'EOF' +Host aur.archlinux.org + User aur + IdentityFile ~/.ssh/aur_key + IdentitiesOnly yes +EOF +# Pinned host key, see https://aur.archlinux.org +echo 'aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN' \ + >> ~/.ssh/known_hosts + +aurdir="$(mktemp -d)" +git clone "ssh://aur@aur.archlinux.org/$pkg.git" "$aurdir" +cp "$pkg/PKGBUILD" "$pkg/.SRCINFO" "$aurdir/" + +cd "$aurdir" +git config user.name "${AUR_GIT_NAME:-Felitendo}" +git config user.email "${AUR_GIT_EMAIL:-95575686+Felitendo@users.noreply.github.com}" +git add PKGBUILD .SRCINFO +if git diff --cached --quiet && [[ -n "$(git ls-remote origin)" ]]; then + echo "$pkg: AUR package is already up to date" +else + git commit -m "Update to $ver-$rel" + git push origin HEAD:master + echo "$pkg: pushed $ver-$rel to the AUR" +fi diff --git a/timetable-bin/.SRCINFO b/timetable-bin/.SRCINFO new file mode 100644 index 0000000..e480383 --- /dev/null +++ b/timetable-bin/.SRCINFO @@ -0,0 +1,22 @@ +pkgbase = timetable-bin + pkgdesc = GTK4 + LibAdwaita client for WebUntis (prebuilt, bundles python-webuntis) + pkgver = 3.1 + pkgrel = 1 + url = https://codeberg.org/ostfriese4/untis + arch = any + license = GPL-3.0-or-later + depends = gtk4 + depends = libadwaita + depends = python + depends = python-gobject + depends = python-requests + depends = glib2 + depends = hicolor-icon-theme + provides = untis + provides = timetable + conflicts = untis + conflicts = timetable + source = https://github.com/Felitendo/PKGBUILDS/releases/download/timetable-bin-3.1/timetable-bin-3.1.tar.zst + sha256sums = ffd735d1a08204fe18e563815813b3a62f7625bce5bd5d4c086f1456422e0590 + +pkgname = timetable-bin diff --git a/timetable-bin/PKGBUILD b/timetable-bin/PKGBUILD new file mode 100644 index 0000000..c33f329 --- /dev/null +++ b/timetable-bin/PKGBUILD @@ -0,0 +1,20 @@ +# Maintainer: Felitendo +# This PKGBUILD is updated automatically: +# https://github.com/Felitendo/PKGBUILDS + +pkgname=timetable-bin +pkgver=3.1 +pkgrel=1 +pkgdesc="GTK4 + LibAdwaita client for WebUntis (prebuilt, bundles python-webuntis)" +arch=('any') +url="https://codeberg.org/ostfriese4/untis" +license=('GPL-3.0-or-later') +depends=('gtk4' 'libadwaita' 'python' 'python-gobject' 'python-requests' 'glib2' 'hicolor-icon-theme') +provides=('untis' 'timetable') +conflicts=('untis' 'timetable') +source=("https://github.com/Felitendo/PKGBUILDS/releases/download/${pkgname}-${pkgver}/${pkgname}-${pkgver}.tar.zst") +sha256sums=('ffd735d1a08204fe18e563815813b3a62f7625bce5bd5d4c086f1456422e0590') + +package() { + cp -a "$srcdir/usr" "$pkgdir/" +} diff --git a/timetable-bin/pkg.sh b/timetable-bin/pkg.sh new file mode 100644 index 0000000..80f588a --- /dev/null +++ b/timetable-bin/pkg.sh @@ -0,0 +1,49 @@ +# timetable-bin - prebuilt package of https://codeberg.org/ostfriese4/untis +# ("Timetable", a GTK4 + LibAdwaita WebUntis client written in Python). +# +# Upstream publishes no binary release assets, so build_artifact() builds the +# app from the release tarball and the resulting install tree is published as +# a GitHub release asset of this repository, which the PKGBUILD then uses. + +UPSTREAM_REPO="ostfriese4/untis" + +# Installed in CI (pacman) before build_artifact runs. +BUILD_DEPS=(meson ninja glib2 glib2-devel gtk4 libadwaita python python-gobject python-pip gettext) + +latest_version() { + curl -sf "https://codeberg.org/api/v1/repos/$UPSTREAM_REPO/releases/latest" \ + | jq -r '.tag_name' | sed 's/^v//' +} + +# build_artifact +build_artifact() { + local ver="$1" outfile="$2" + local workdir destdir + workdir="$(mktemp -d)" + destdir="$(mktemp -d)" + + curl -sfL "https://codeberg.org/$UPSTREAM_REPO/archive/v$ver.tar.gz" | tar -xz -C "$workdir" + + # Upstream does not maintain the version in meson.build; use the release tag + # so the About dialog shows the right version. + sed -i "0,/version:/s/version: *'[^']*'/version: '$ver'/" "$workdir/untis/meson.build" + + meson setup "$workdir/build" "$workdir/untis" --prefix=/usr --buildtype=release + meson install -C "$workdir/build" --destdir "$destdir" + + # Bundle the pure-python webuntis library: it is packaged neither in the + # Arch repos nor on the AUR. /usr/share/untis is on the launcher's sys.path. + pip download --no-deps --only-binary :all: --dest "$workdir/wheels" webuntis + python -m zipfile -e "$workdir"/wheels/webuntis-*.whl "$destdir/usr/share/untis/" + + # These caches are generated by pacman hooks; shipping them would cause + # file conflicts on install. + rm -f "$destdir/usr/share/glib-2.0/schemas/gschemas.compiled" \ + "$destdir/usr/share/applications/mimeinfo.cache" \ + "$destdir/usr/share/icons/hicolor/icon-theme.cache" + + tar --zstd --sort=name --owner=0 --group=0 --numeric-owner --mtime='@0' \ + -cf "$outfile" -C "$destdir" usr + + rm -rf "$workdir" "$destdir" +}