From d888476513579c4984dc5f9298ae4d1553e36d42 Mon Sep 17 00:00:00 2001 From: Felitendo Date: Sun, 4 Oct 2026 20:40:35 +0200 Subject: [PATCH] ci: run on gitea actions at git.felo.gg [skip ci] --- .github/workflows/notify.yml | 88 ++++++++++++++++++++++++------------ .github/workflows/update.yml | 9 ++-- README.md | 33 ++++++++------ scripts/update-package.sh | 39 ++++++++++++++-- sharpemu-bin/pkg.sh | 2 - 5 files changed, 119 insertions(+), 52 deletions(-) diff --git a/.github/workflows/notify.yml b/.github/workflows/notify.yml index 18ee353..ef21dc1 100644 --- a/.github/workflows/notify.yml +++ b/.github/workflows/notify.yml @@ -1,11 +1,13 @@ name: Notify -# Opens an issue when a package fails to update on main, so it reaches GitHub's -# notifications, the mobile app and email. One issue per package: a broken -# package must not hide behind another one, and a run of only some packages -# must not close the issues of the others. A later failure refreshes the issue -# instead of commenting, as the schedule would otherwise add one every 6 hours. -# The next successful update of that package closes it again. +# Opens an issue when a package fails to update on main, so it shows up in the +# notifications on git.felo.gg. One issue per package: a broken package must +# not hide behind another one, and a run of only some packages must not close +# the issues of the others. A later failure refreshes the issue instead of +# commenting, as the schedule would otherwise add one every 6 hours. The next +# successful update of that package closes it again. +# +# Talks to the Gitea API with curl: gh only knows GitHub. on: workflow_run: @@ -30,10 +32,10 @@ jobs: steps: - name: Open, update or close the failure issues env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} + TOKEN: ${{ github.token }} + API: ${{ github.api_url }}/repos/${{ github.repository }} OWNER: ${{ github.repository_owner }} - WORKFLOW: ${{ github.event.workflow_run.name }} + WORKFLOW: ${{ github.event.workflow_run.name || 'Update AUR packages' }} CONCLUSION: ${{ github.event.workflow_run.conclusion }} EVENT: ${{ github.event.workflow_run.event }} RUN_ID: ${{ github.event.workflow_run.id }} @@ -42,7 +44,25 @@ jobs: run: | set -euo pipefail label=ci-failure - issues="$(gh issue list --state open --label "$label" --limit 200 --json number,title)" + + api() { + local method=$1 path=$2 + shift 2 + curl -fsSL -X "$method" -H "Authorization: token $TOKEN" \ + -H 'Content-Type: application/json' "$API$path" "$@" + } + + issues="$(api GET "/issues?state=open&type=issues&labels=$label&limit=50")" + + label_id() { + local id + id="$(api GET '/labels?limit=50' | jq -r --arg l "$label" '.[] | select(.name == $l) | .id')" + if [ -z "$id" ]; then + id="$(api POST /labels -d "$(jq -cn --arg l "$label" \ + '{name: $l, color: "#d73a4a", description: "A package fails to update"}')" | jq -r .id)" + fi + echo "$id" + } # The issue title for a job: the package for "update ()", # the workflow for anything else. @@ -59,14 +79,16 @@ jobs: } # The lines of the failed step leading up to its first error, enough - # to see what broke without opening the log. + # to see what broke without opening the log. Falls back to the end + # of the log. excerpt_for() { - curl -fsSL -H "Authorization: Bearer $GH_TOKEN" \ - "$GITHUB_API_URL/repos/$GH_REPO/actions/jobs/$1/logs" | - sed -E 's/^[0-9TZ:.-]+ //; s/\x1b\[[0-9;]*m//g' | - awk '/^##\[group\]Run / { out = "" } { out = out $0 "\n" } - /^##\[error\]/ { printf "%s", out; exit }' | - tail -40 || true + local log + log="$(api GET "/actions/jobs/$1/logs" | + sed -E 's/^[0-9TZ:.-]+ //; s/\x1b\[[0-9;]*m//g')" || return 0 + awk '/^##\[group\]Run / { out = "" } { out = out $0 "\n" } + /^(##\[error\]|::error::)/ { printf "%s", out; found = 1; exit } + END { if (!found) exit 1 }' <<<"$log" | tail -40 || + tail -40 <<<"$log" } # report <job json, or empty if the run never started> @@ -102,11 +124,10 @@ jobs: issue="$(issue_for "$title")" if [ -n "$issue" ]; then - gh issue edit "$issue" --body "$body" + api PATCH "/issues/$issue" -d "$(jq -cn --arg b "$body" '{body: $b}')" >/dev/null else - gh label create "$label" --color d73a4a \ - --description "A package fails to update" --force - gh issue create --title "$title" --label "$label" --body "@$OWNER $body" + api POST /issues -d "$(jq -cn --arg t "$title" --arg b "@$OWNER $body" \ + --argjson l "$(label_id)" '{title: $t, body: $b, labels: [$l]}')" >/dev/null fi } @@ -114,7 +135,9 @@ jobs: local issue issue="$(issue_for "$1")" if [ -n "$issue" ]; then - gh issue close "$issue" --comment "Passing again: $RUN_URL" + api POST "/issues/$issue/comments" \ + -d "$(jq -cn --arg b "Passing again: $RUN_URL" '{body: $b}')" >/dev/null + api PATCH "/issues/$issue" -d '{"state": "closed"}' >/dev/null fi } @@ -124,10 +147,17 @@ jobs: fi # Cancelled and skipped jobs say nothing about a package. - while IFS= read -r -u 3 job; do - title="$(title_for "$(jq -r .name <<<"$job")")" - case "$(jq -r .conclusion <<<"$job")" in - success) close "$title" ;; - failure | timed_out) report "$title" "$job" ;; - esac - done 3< <(gh api --paginate "repos/$GH_REPO/actions/runs/$RUN_ID/jobs?per_page=100" --jq '.jobs[]') + page=1 + while :; do + jobs="$(api GET "/actions/runs/$RUN_ID/jobs?limit=50&page=$page" | jq -c '.jobs[]?')" + [ -n "$jobs" ] || break + while IFS= read -r -u 3 job; do + title="$(title_for "$(jq -r .name <<<"$job")")" + case "$(jq -r .conclusion <<<"$job")" in + success) close "$title" ;; + failure | timed_out) report "$title" "$job" ;; + esac + done 3<<<"$jobs" + [ "$(wc -l <<<"$jobs")" -ge 50 ] || break + page=$((page + 1)) + done diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 55893ef..2a6f2c2 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -3,7 +3,7 @@ run-name: ${{ inputs.package && format('Update {0}', inputs.package) || 'Update on: schedule: - # every 6 hours (offset from the full hour to avoid GitHub's load spikes) + # every 6 hours, off the full hour - cron: '37 */6 * * *' workflow_dispatch: inputs: @@ -79,17 +79,18 @@ jobs: max-parallel: 1 matrix: package: ${{ fromJson(needs.discover.outputs.packages) }} - env: - GH_TOKEN: ${{ github.token }} steps: + # nodejs: actions/checkout is a JavaScript action, and the Gitea runner + # runs it inside this container - name: Install base tooling - run: pacman -Syu --noconfirm --needed git openssh github-cli jq zstd + run: pacman -Syu --noconfirm --needed git openssh jq zstd nodejs - uses: actions/checkout@v7 - name: Update ${{ matrix.package }} env: AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + GITHUB_API_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} AUR_GIT_NAME: ${{ vars.AUR_GIT_NAME }} AUR_GIT_EMAIL: ${{ secrets.AUR_GIT_EMAIL }} run: bash scripts/update-package.sh "${{ matrix.package }}" diff --git a/README.md b/README.md index 5084b41..5b69d42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # PKGBUILDS -Automated AUR packages, kept up to date by GitHub Actions. +Automated AUR packages, kept up to date by Gitea Actions on +[git.felo.gg](https://git.felo.gg/Felitendo/PKGBUILDS). Every 6 hours the [update workflow](.github/workflows/update.yml) checks each package's upstream for a new release. When one is found it: @@ -42,25 +43,25 @@ source in the `PKGBUILD` (and drop the `-bin` suffix), or don't package it. | Package | Upstream | Upstream deliverable | AUR | |---|---|---|---| -| `bottles-opener` | [LoonixTools/bottles-opener](https://github.com/LoonixTools/bottles-opener) — double-click a file and it opens in its Windows program inside Bottles, with the file types and icons from the bottle's registry | *built from source* | [bottles-opener](https://aur.archlinux.org/packages/bottles-opener) | -| `bt-volume-step` | [LoonixTools/bt-volume-step](https://github.com/LoonixTools/bt-volume-step) — corrects the coarse internal volume grid of Bluetooth audio devices on PipeWire | *built from source* | [bt-volume-step](https://aur.archlinux.org/packages/bt-volume-step) | -| `cachy-auto-update` | [LoonixTools/cachy-auto-update](https://github.com/LoonixTools/cachy-auto-update) — unattended background updates for CachyOS (pacman, AUR, Flatpak, AppImages) | *built from source* | [cachy-auto-update](https://aur.archlinux.org/packages/cachy-auto-update) | +| `bottles-opener` | [LoonixTools/bottles-opener](https://git.felo.gg/LoonixTools/bottles-opener) — double-click a file and it opens in its Windows program inside Bottles, with the file types and icons from the bottle's registry | *built from source* | [bottles-opener](https://aur.archlinux.org/packages/bottles-opener) | +| `bt-volume-step` | [LoonixTools/bt-volume-step](https://git.felo.gg/LoonixTools/bt-volume-step) — corrects the coarse internal volume grid of Bluetooth audio devices on PipeWire | *built from source* | [bt-volume-step](https://aur.archlinux.org/packages/bt-volume-step) | +| `cachy-auto-update` | [LoonixTools/cachy-auto-update](https://git.felo.gg/LoonixTools/cachy-auto-update) — unattended background updates for CachyOS (pacman, AUR, Flatpak, AppImages) | *built from source* | [cachy-auto-update](https://aur.archlinux.org/packages/cachy-auto-update) | | `capture-studio-bin` | [tenzen.studio](https://tenzen.studio): Capture Studio by Tenzen Studio, screen recorder and editor for product demos (proprietary Electron app). Called `tenzen-studio-bin` before | Flatpak bundle | [capture-studio-bin](https://aur.archlinux.org/packages/capture-studio-bin) | | `chiaki-ng-bin` | [streetpea/chiaki-ng](https://github.com/streetpea/chiaki-ng) — PlayStation Remote Play client (Qt6) | AppImage | [chiaki-ng-bin](https://aur.archlinux.org/packages/chiaki-ng-bin) | | `chromium-widevine-helper` | [GloriousEggroll/chromium-widevine-helper](https://github.com/GloriousEggroll/chromium-widevine-helper) — extension + native helper installing Google's Widevine CDM into Chromium-based browser profiles | *no build step* | [chromium-widevine-helper](https://aur.archlinux.org/packages/chromium-widevine-helper) | | `concat` | [jub0t/Concat](https://github.com/jub0t/Concat) — free and open-source CapCut replacement (a Slint window over a Rust video engine) | *built from source* | [concat](https://aur.archlinux.org/packages/concat) | | `concat-bin` | [jub0t/Concat](https://github.com/jub0t/Concat) — prebuilt release of `concat` | `.deb` | [concat-bin](https://aur.archlinux.org/packages/concat-bin) | | `concat-git` | [jub0t/Concat](https://github.com/jub0t/Concat) — `main` branch of `concat` | *built from source* | [concat-git](https://aur.archlinux.org/packages/concat-git) | -| `face-unlock` | [LoonixTools/face-unlock](https://github.com/LoonixTools/face-unlock): Face ID for Linux, for the lock screen, sudo and admin prompts on Plasma, GNOME, Hyprland and Niri, with a photo check (C++/Qt6, OpenCV). Called `plasma-face-unlock` before 2.0.0 | *built from source* | [face-unlock](https://aur.archlinux.org/packages/face-unlock) | -| `face-unlock-bin` | [LoonixTools/face-unlock](https://github.com/LoonixTools/face-unlock): prebuilt release of `face-unlock`, with OpenCV linked in | tarball (built on Arch) | [face-unlock-bin](https://aur.archlinux.org/packages/face-unlock-bin) | +| `face-unlock` | [LoonixTools/face-unlock](https://git.felo.gg/LoonixTools/face-unlock): Face ID for Linux, for the lock screen, sudo and admin prompts on Plasma, GNOME, Hyprland and Niri, with a photo check (C++/Qt6, OpenCV). Called `plasma-face-unlock` before 2.0.0 | *built from source* | [face-unlock](https://aur.archlinux.org/packages/face-unlock) | +| `face-unlock-bin` | [LoonixTools/face-unlock](https://git.felo.gg/LoonixTools/face-unlock): prebuilt release of `face-unlock`, with OpenCV linked in | tarball (built on Arch) | [face-unlock-bin](https://aur.archlinux.org/packages/face-unlock-bin) | | `faugus-launcher-bin` | [Faugus/faugus-launcher](https://github.com/Faugus/faugus-launcher) — launcher for Windows games via UMU-Launcher | `.deb` (`all`) | [faugus-launcher-bin](https://aur.archlinux.org/packages/faugus-launcher-bin) | | `fluxer-bin` | [fluxer.app](https://fluxer.app) — Fluxer desktop client (Electron) | tarball | [fluxer-bin](https://aur.archlinux.org/packages/fluxer-bin) | | `kitty-tune-bin` | [alan7383/KittyTuneDesktop](https://github.com/alan7383/KittyTuneDesktop) — SoundCloud and YouTube music player (Kotlin/Compose Multiplatform) | `.deb` | [kitty-tune-bin](https://aur.archlinux.org/packages/kitty-tune-bin) | | `lunar-client-bin` | [lunarclient.com](https://lunarclient.com) — Minecraft PvP modpack launcher | AppImage | [lunar-client-bin](https://aur.archlinux.org/packages/lunar-client-bin) | -| `middleclick-autoscroll` | [LoonixTools/middleclick-autoscroll](https://github.com/LoonixTools/middleclick-autoscroll) — enables middle-click autoscroll in every application that supports it | *built from source* | [middleclick-autoscroll](https://aur.archlinux.org/packages/middleclick-autoscroll) | +| `middleclick-autoscroll` | [LoonixTools/middleclick-autoscroll](https://git.felo.gg/LoonixTools/middleclick-autoscroll) — enables middle-click autoscroll in every application that supports it | *built from source* | [middleclick-autoscroll](https://aur.archlinux.org/packages/middleclick-autoscroll) | | `modrinth-app-bin` | [modrinth/code](https://github.com/modrinth/code) — Minecraft mod manager/launcher | `.deb` | [modrinth-app-bin](https://aur.archlinux.org/packages/modrinth-app-bin) | -| `modrinth-enhanced` | [Felitendo/Modrinth-Enhanced](https://github.com/Felitendo/Modrinth-Enhanced) — Modrinth App without ads or telemetry, with offline and Ely.by accounts (a patch series on top of each Modrinth App release) | *built from source* | [modrinth-enhanced](https://aur.archlinux.org/packages/modrinth-enhanced) | -| `modrinth-enhanced-bin` | [Felitendo/Modrinth-Enhanced](https://github.com/Felitendo/Modrinth-Enhanced) — prebuilt release of `modrinth-enhanced` | `.deb` | [modrinth-enhanced-bin](https://aur.archlinux.org/packages/modrinth-enhanced-bin) | +| `modrinth-enhanced` | [Felitendo/Modrinth-Enhanced](https://git.felo.gg/Felitendo/Modrinth-Enhanced) — Modrinth App without ads or telemetry, with offline and Ely.by accounts (a patch series on top of each Modrinth App release) | *built from source* | [modrinth-enhanced](https://aur.archlinux.org/packages/modrinth-enhanced) | +| `modrinth-enhanced-bin` | [Felitendo/Modrinth-Enhanced](https://git.felo.gg/Felitendo/Modrinth-Enhanced) — prebuilt release of `modrinth-enhanced` | `.deb` | [modrinth-enhanced-bin](https://aur.archlinux.org/packages/modrinth-enhanced-bin) | | `moondeckbuddy` | [FrogTheFrog/moondeck-buddy](https://github.com/FrogTheFrog/moondeck-buddy) — host companion of the MoonDeck Steam Deck plugin (C++/Qt6) | *built from source* | [moondeckbuddy](https://aur.archlinux.org/packages/moondeckbuddy) | | `moondeckbuddy-bin` | [FrogTheFrog/moondeck-buddy](https://github.com/FrogTheFrog/moondeck-buddy) — prebuilt release of `moondeckbuddy` | AppImage | *not published yet* | | `moonlight-vrr` | [Nonary/moonlight-qt](https://github.com/Nonary/moonlight-qt): fork of the Moonlight game streaming client with smooth VRR pacing and the PyroWave codec (C++/Qt6) | *built from source* | [moonlight-vrr](https://aur.archlinux.org/packages/moonlight-vrr) | @@ -73,8 +74,8 @@ source in the `PKGBUILD` (and drop the `-bin` suffix), or don't package it. | `schist-bin` | [Infrawrench/schist](https://github.com/Infrawrench/schist): prebuilt release of `schist` | `.pkg.tar.zst` | [schist-bin](https://aur.archlinux.org/packages/schist-bin) | | `sharpemu-bin` | [sharpemu/sharpemu](https://github.com/sharpemu/sharpemu) — experimental PlayStation 5 emulator | tarball | [sharpemu-bin](https://aur.archlinux.org/packages/sharpemu-bin) | | `snapx-bin` | [SnapXL/SnapX](https://github.com/SnapXL/SnapX) — ShareX-fork screenshot/sharing tool | self-contained tarball | [snapx-bin](https://aur.archlinux.org/packages/snapx-bin) | -| `untix` | [ostfriese4/untis](https://codeberg.org/ostfriese4/untis): "Timetable", a GTK4 + LibAdwaita client for WebUntis. Called `untis` and `timetable` on the AUR before | *built from source* | [untix](https://aur.archlinux.org/packages/untix) | -| `untix-git` | [ostfriese4/untis](https://codeberg.org/ostfriese4/untis): `main` branch of `untix` | *built from source* | [untix-git](https://aur.archlinux.org/packages/untix-git) | +| `untix` | [ostfriese4/untix](https://codeberg.org/ostfriese4/untix): "Timetable", a GTK4 + LibAdwaita client for WebUntis. Called `untis` and `timetable` on the AUR before | *built from source* | [untix](https://aur.archlinux.org/packages/untix) | +| `untix-git` | [ostfriese4/untix](https://codeberg.org/ostfriese4/untix): `main` branch of `untix` | *built from source* | [untix-git](https://aur.archlinux.org/packages/untix-git) | | `vacuumtube-bin` | [shy1132/VacuumTube](https://github.com/shy1132/VacuumTube) — YouTube Leanback (TV UI) with built-in adblocker | `.deb` | [vacuumtube-bin](https://aur.archlinux.org/packages/vacuumtube-bin) | | `waydroid-helper-bin` | [waydroid-helper/waydroid-helper](https://github.com/waydroid-helper/waydroid-helper) — GTK4 GUI for Waydroid configuration and extensions | AppImage | [waydroid-helper-bin](https://aur.archlinux.org/packages/waydroid-helper-bin) | | `wiiudownloader-bin` | [Xpl0itU/WiiUDownloader](https://github.com/Xpl0itU/WiiUDownloader) — Wii U title downloader (Go + GTK4) | AppImage | [wiiudownloader-bin](https://aur.archlinux.org/packages/wiiudownloader-bin) | @@ -86,10 +87,16 @@ source in the `PKGBUILD` (and drop the `-bin` suffix), or don't package it. 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). + (Settings → Actions → Secrets → Add secret). 3. Optionally set the repository variable `AUR_GIT_NAME` and the repository **secret** `AUR_GIT_EMAIL` to control the commit identity used on the AUR - (defaults: `Felitendo` / the maintainer's GitHub noreply address). + (defaults: `Felitendo` / the maintainer's old GitHub noreply address). +4. Optionally add a GitHub token (no scopes needed) as the secret + `GITHUB_API_TOKEN`. Packages ask the GitHub API about upstream releases; + without a token those calls are anonymous and share 60 per hour. + +The runner needs the `ubuntu-latest` label and Docker: every package is built +in an `archlinux:base-devel` container. The first push to `ssh://aur@aur.archlinux.org/<pkgname>.git` creates the AUR package automatically. diff --git a/scripts/update-package.sh b/scripts/update-package.sh index 6ee7085..cb30949 100644 --- a/scripts/update-package.sh +++ b/scripts/update-package.sh @@ -12,8 +12,10 @@ # allow a PKGBUILD to pull in a binary tarball built by the maintainer, so # nothing is ever built or hosted here. # -# Requires: GH_TOKEN (repo push), AUR_SSH_PRIVATE_KEY. -# Optional: AUR_GIT_NAME / AUR_GIT_EMAIL for the AUR commit identity. +# Requires: AUR_SSH_PRIVATE_KEY. The push back to this repository uses the +# credentials actions/checkout left behind. +# Optional: GITHUB_API_TOKEN for a higher GitHub API rate limit, +# AUR_GIT_NAME / AUR_GIT_EMAIL for the AUR commit identity. set -euo pipefail pkg="${1:?usage: update-package.sh <package-dir>}" @@ -27,6 +29,35 @@ if [[ "${CI:-}" == "true" ]]; then git config --global --add safe.directory "$repo_root" fi +# pkg.sh asks GitHub about upstream releases with `gh api <path> [--jq <filter>] +# [-H <header>]`. CI runs on Gitea, which has no GitHub token to give gh, so +# this stands in for it: the same calls as plain curl, anonymous unless +# GITHUB_API_TOKEN is set. Anonymous calls get 60 per hour, enough for a run. +gh() { + if [[ "${1:-}" != api ]]; then + echo "gh $1: only 'gh api' is available here" >&2 + return 1 + fi + shift + local api_path="" filter="" headers=() out + while (($#)); do + case "$1" in + --jq) filter="$2"; shift 2 ;; + -H) headers+=(-H "$2"); shift 2 ;; + *) api_path="$1"; shift ;; + esac + done + if [[ -n "${GITHUB_API_TOKEN:-}" ]]; then + headers+=(-H "Authorization: Bearer $GITHUB_API_TOKEN") + fi + out="$(curl -sfL "${headers[@]}" "https://api.github.com/${api_path#/}")" || return 1 + if [[ -n "$filter" ]]; then + jq -r "$filter" <<< "$out" + else + printf '%s\n' "$out" + fi +} + BUILD_DEPS=() # A package can be kept out of the AUR while it is still being prepared here: # pkg.sh sets AUR_PUBLISH=false and everything up to step 4 runs as usual, so @@ -121,8 +152,8 @@ rm -f "$pkg"/*.pkg.tar.* "$pkg"/*.tar.zst "$pkg"/*.tar.gz "$pkg"/*.tar.bz2 \ committed=false if [[ "${CI:-}" == "true" ]]; then - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.name "gitea-actions[bot]" + git config user.email "actions@git.felo.gg" git add "$pkg/PKGBUILD" "$pkg/.SRCINFO" if git diff --cached --quiet; then diff --git a/sharpemu-bin/pkg.sh b/sharpemu-bin/pkg.sh index d42c534..b4ebffd 100644 --- a/sharpemu-bin/pkg.sh +++ b/sharpemu-bin/pkg.sh @@ -12,8 +12,6 @@ UPSTREAM_REPO="sharpemu/sharpemu" latest_version() { - # gh instead of plain curl: authenticated API calls, so shared-IP rate - # limits on the CI runners can't bite. gh api "repos/$UPSTREAM_REPO/releases/latest" --jq '.tag_name' \ | sed -e 's/^v//' -e 's/-/_/g' }