# ABOUTME: Builds changed packages and publishes them to Gitea Package Registry. # ABOUTME: Triggered by pkgver-bot commits (from per-project CI workflows). name: Build and publish packages on: push: branches: - main paths: - '*/PKGBUILD' jobs: build-and-publish: runs-on: moonarch steps: - name: Build and publish changed packages run: | rm -rf repo git clone http://gitea:3000/nevaforget/moonarch-pkgbuilds.git repo cd repo CHANGED=$(git diff --name-only HEAD~1 HEAD | grep '/PKGBUILD$' | sed 's|/PKGBUILD||' || true) if [ -z "$CHANGED" ]; then echo "No PKGBUILD changes detected" exit 0 fi echo "Changed packages: $CHANGED" # Sync pacman DB so makepkg -s can resolve current deps. # NEVER change this to -Syu. The runner shares I/O with the host # (act_runner runs in network-host mode on the Gitea server). A full # system upgrade here took the host down on 2026-04-20 and required # a hard reboot + Contabo abuse-block recovery. -Sy syncs the DB # only; -s picks targeted makedepends via pacman. sudo pacman -Sy --noconfirm for pkg in $CHANGED; do echo "==> Building $pkg" cd "$pkg" # Install makedepends manually (cargo, go, …) and skip the full # depends check via -d. Rationale: moonarch-git's runtime depends # include AUR-only packages (stasis, auto-cpufreq, ttf-ubuntusans-nerd) # which pacman can't resolve. Those deps aren't needed at build time. # Source the PKGBUILD in a subshell to read the array robustly — # awk/grep parsing breaks on single-line vs multi-line formats. MAKEDEPS=$(bash -c 'source ./PKGBUILD 2>/dev/null; printf "%s " "${makedepends[@]}"') if [ -n "${MAKEDEPS// }" ]; then # shellcheck disable=SC2086 sudo pacman -S --needed --noconfirm $MAKEDEPS fi # Single-threaded build to keep memory peak below the 8 GiB # host budget. Run 108 (2026-04-28) hit a verified global OOM # (constraint=CONSTRAINT_NONE in journalctl, rustc killed at # 1.7 GiB RSS while host was already at 6.2 GiB used / 1.6 GiB # available). Two parallel rustc workers blow that reserve. # Container-side mem_limit doesn't help because the kill is # global, not cgroup. -j1 halves the peak; act_runner blkio # cap (30 MB/s) protects I/O. Do NOT raise without a wider # memory plan (more host RAM, per-container caps, or larger # swap headroom). export CARGO_BUILD_JOBS=1 export MAKEFLAGS="-j1" makepkg -sfd --noconfirm # makepkg can emit multiple artifacts per build (main + -debug # split package). Upload each. Arch filename convention: # ---.pkg.tar.zst; pkgver never # contains '-', so we can strip from the right. shopt -s nullglob PKG_FILES=(*.pkg.tar.zst) shopt -u nullglob if [ "${#PKG_FILES[@]}" -eq 0 ]; then echo "ERROR: No package file found for $pkg" cd .. continue fi # Collect unique package names + the version we just built for # each. We delete only ZOMBIE versions (anything in the registry # for this name that is NOT the version we just built) to clear # the Gitea Arch DB which doesn't evict on pkgver change. # # Run 107 (2026-04-28) showed the previous "delete all versions" # logic killed cross-package state when multiple PKGBUILDs were # built in one run — sweet-cursors's clear loop deleted the # just-uploaded moongreet-git versions. The skip-current-version # logic + .type=="arch" filter prevent that recurrence. declare -A SEEN_NAMES declare -A KEEP_VERS for PKG_FILE in "${PKG_FILES[@]}"; do base="${PKG_FILE%.pkg.tar.zst}" # Strip arch (last -seg), pkgrel (next), pkgver (next) → name. # Keep "$pkgver-$pkgrel" as the full version. arch_stripped="${base%-*}" rel_stripped="${arch_stripped%-*}" ver_stripped="${rel_stripped%-*}" pkg_name="$ver_stripped" full_ver="${arch_stripped#${ver_stripped}-}" SEEN_NAMES["$pkg_name"]=1 KEEP_VERS["$pkg_name"]="$full_ver" done sudo pacman -S --needed --noconfirm jq for PKG_NAME in "${!SEEN_NAMES[@]}"; do KEEP="${KEEP_VERS[$PKG_NAME]}" echo "==> Clearing zombie versions of $PKG_NAME (keep: $KEEP)" VERSIONS=$(curl -s \ -H "Authorization: token ${{ secrets.PKG_REGISTRY_TOKEN }}" \ "https://gitea.moonarch.de/api/v1/packages/nevaforget?type=arch&q=${PKG_NAME}&page=1&limit=100" \ | jq -r --arg n "$PKG_NAME" --arg t "arch" \ '.[] | select(.name==$n and .type==$t) | .version') for V in $VERSIONS; do if [ "$V" = "$KEEP" ]; then echo " skip $PKG_NAME@$V (just built)" continue fi echo " delete $PKG_NAME@$V" DEL_CODE=$(curl -s -o /dev/null -w '%{http_code}' -X DELETE \ -H "Authorization: token ${{ secrets.PKG_REGISTRY_TOKEN }}" \ "https://gitea.moonarch.de/api/v1/packages/nevaforget/arch/${PKG_NAME}/${V}") echo " HTTP $DEL_CODE" done done for PKG_FILE in "${PKG_FILES[@]}"; do base="${PKG_FILE%.pkg.tar.zst}" PKG_ARCH="${base##*-}" base="${base%-*}" PKG_REL="${base##*-}" base="${base%-*}" PKG_VER="${base##*-}" PKG_NAME="${base%-*}" FULL_VER="${PKG_VER}-${PKG_REL}" echo "==> Uploading $PKG_FILE ($PKG_NAME $FULL_VER $PKG_ARCH)" # Upload new version. Capture HTTP status — curl -sf alone # hides the response, and a silent failure lets the run green # while the registry stays stale. HTTP_CODE=$(curl -s -w '%{http_code}' -o /tmp/upload.log \ -H "Authorization: token ${{ secrets.PKG_REGISTRY_TOKEN }}" \ --upload-file "$PKG_FILE" \ "https://gitea.moonarch.de/api/packages/nevaforget/arch/moonarch") echo "--- server response (HTTP $HTTP_CODE) ---" cat /tmp/upload.log echo echo "-----------------------" if [[ ! "$HTTP_CODE" =~ ^2 ]]; then echo "ERROR: Upload failed with HTTP $HTTP_CODE for $PKG_FILE" exit 1 fi echo "==> Published $PKG_NAME $FULL_VER (HTTP $HTTP_CODE)" done cd .. done