From 0a04b994aecc47ba18b6fb491a5ac810997fe132 Mon Sep 17 00:00:00 2001 From: Brodino Date: Thu, 30 Apr 2026 09:59:35 +0200 Subject: [PATCH] feat: asked claude sonnet 4.6 to rework as github workflow instead of on my machine --- .github/workflows/build.yml | 176 ++++++++++++++++++++++ .github/workflows/check.yml | 66 +++++++++ AGENTS.md | 284 ++++++++++++++++++++++++++++++++++++ scripts/build.sh | 10 +- scripts/check-update.sh | 103 ++++++------- scripts/publish.sh | 8 +- 6 files changed, 587 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/check.yml create mode 100644 AGENTS.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..daf43e7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,176 @@ +name: Build and publish webkit2gtk + +on: + workflow_dispatch: + inputs: + aur_version: + description: 'Full AUR version string to build (e.g. 2.46.3-1)' + required: true + type: string + +jobs: + build-and-publish: + name: Build webkit2gtk ${{ inputs.aur_version }} + runs-on: ubuntu-latest-96-cores + container: + image: archlinux:latest + # Needed so makepkg can use FUSE/user namespaces inside the container + options: --privileged + + permissions: + contents: write # create GitHub Releases and upload assets + + env: + GITHUB_REPO: ${{ github.repository }} + AUR_PACKAGE_NAME: webkit2gtk-bin + AUR_MAINTAINER_NAME: ${{ secrets.AUR_MAINTAINER_NAME }} + AUR_MAINTAINER_EMAIL: ${{ secrets.AUR_MAINTAINER_EMAIL }} + + steps: + - name: Checkout repository + uses: actions/checkout@v6.0.2 + + - name: Install build dependencies + run: | + pacman -Syu --noconfirm + pacman -S --noconfirm --needed \ + base-devel \ + git \ + sudo \ + curl \ + jq \ + openssh \ + github-cli \ + clang \ + cmake \ + gi-docgen \ + glib2-devel \ + gobject-introspection \ + gperf \ + gst-plugins-bad \ + lld \ + ninja \ + python \ + ruby \ + ruby-stdlib \ + systemd \ + unifdef \ + wayland-protocols \ + at-spi2-core \ + atk \ + bubblewrap \ + cairo \ + enchant \ + expat \ + fontconfig \ + freetype2 \ + gdk-pixbuf2 \ + glib2 \ + glibc \ + gst-plugins-bad-libs \ + gst-plugins-base-libs \ + gstreamer \ + gtk3 \ + harfbuzz \ + harfbuzz-icu \ + hyphen \ + icu \ + lcms2 \ + libatomic \ + libavif \ + libdrm \ + libegl \ + libepoxy \ + libgcrypt \ + libgl \ + libjpeg-turbo \ + libjxl \ + libmanette \ + libpng \ + libseccomp \ + libsecret \ + libsoup \ + libsystemd \ + libtasn1 \ + libwebp \ + libx11 \ + libxml2 \ + libxslt \ + mesa \ + openjpeg2 \ + pango \ + sqlite \ + ttf-dejavu \ + wayland \ + woff2 \ + xdg-dbus-proxy \ + zlib + + - name: Create builduser (makepkg refuses to run as root) + run: | + useradd -m -G wheel builduser + echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + # Allow builduser to write to the workspace + chown -R builduser:builduser "$GITHUB_WORKSPACE" + git config --system --add safe.directory '*' + + - name: Set up AUR SSH key + env: + AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} + run: | + SSH_DIR=/home/builduser/.ssh + mkdir -p "${SSH_DIR}" + printf '%s\n' "${AUR_SSH_KEY}" > "${SSH_DIR}/aur_id_rsa" + chown -R builduser:builduser "${SSH_DIR}" + chmod 700 "${SSH_DIR}" + chmod 600 "${SSH_DIR}/aur_id_rsa" + printf 'Host aur.archlinux.org\n User aur\n IdentityFile /home/builduser/.ssh/aur_id_rsa\n StrictHostKeyChecking no\n' \ + > "${SSH_DIR}/config" + chmod 600 "${SSH_DIR}/config" + + - name: Configure git identity for builduser + run: | + sudo -u builduser HOME=/home/builduser git config --global user.name "${AUR_MAINTAINER_NAME}" + sudo -u builduser HOME=/home/builduser git config --global user.email "${AUR_MAINTAINER_EMAIL}" + + - name: Import WebKitGTK PGP signing keys + run: | + sudo -u builduser HOME=/home/builduser gpg \ + --keyserver keyserver.ubuntu.com \ + --recv-keys \ + 5AA3BC334FD7E3369E7C77B291C559DBE4C9123B \ + 013A0127AC9C65B34FFA62526C1009B693975393 || \ + sudo -u builduser HOME=/home/builduser gpg \ + --keyserver hkps://keys.openpgp.org \ + --recv-keys \ + 5AA3BC334FD7E3369E7C77B291C559DBE4C9123B \ + 013A0127AC9C65B34FFA62526C1009B693975393 + + - name: Clone webkit2gtk from AUR + run: | + sudo -u builduser HOME=/home/builduser \ + git clone https://aur.archlinux.org/webkit2gtk.git "${GITHUB_WORKSPACE}/webkit2gtk" + + - name: Build webkit2gtk + env: + NPROC: 96 + run: | + sudo -u builduser \ + HOME=/home/builduser \ + GITHUB_WORKSPACE="${GITHUB_WORKSPACE}" \ + NPROC="${NPROC}" \ + bash "${GITHUB_WORKSPACE}/scripts/build.sh" + + - name: Publish to GitHub Releases and AUR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + sudo -u builduser \ + HOME=/home/builduser \ + GITHUB_WORKSPACE="${GITHUB_WORKSPACE}" \ + GITHUB_TOKEN="${GITHUB_TOKEN}" \ + GITHUB_REPO="${GITHUB_REPO}" \ + AUR_PACKAGE_NAME="${AUR_PACKAGE_NAME}" \ + AUR_MAINTAINER_NAME="${AUR_MAINTAINER_NAME}" \ + AUR_MAINTAINER_EMAIL="${AUR_MAINTAINER_EMAIL}" \ + bash "${GITHUB_WORKSPACE}/scripts/publish.sh" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..76e8544 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,66 @@ +name: Check for webkit2gtk update + +on: + # schedule: + # Run every hour + # - cron: '0 * * * *' + workflow_dispatch: + +jobs: + check: + name: Poll AUR for new version + runs-on: ubuntu-latest + permissions: + contents: read + actions: write # required to trigger workflow_dispatch on build.yml + + steps: + - name: Fetch latest AUR version + id: aur + run: | + response=$(curl -fsSL "https://aur.archlinux.org/rpc/v5/info/webkit2gtk") + aur_version=$(echo "${response}" | jq -r '.results[0].Version') + if [[ -z "${aur_version}" || "${aur_version}" == "null" ]]; then + echo "ERROR: Failed to parse version from AUR response: ${response}" >&2 + exit 1 + fi + echo "AUR version: ${aur_version}" + echo "version=${aur_version}" >> "$GITHUB_OUTPUT" + + - name: Fetch last published version from GitHub Releases + id: last + env: + GH_TOKEN: ${{ github.token }} + run: | + # Get the tag of the latest release; strip the leading 'v' + tag=$(gh release list \ + --repo "${{ github.repository }}" \ + --limit 1 \ + --json tagName \ + --jq '.[0].tagName // ""') + last_version="${tag#v}" + echo "Last published version: ${last_version:-}" + echo "version=${last_version}" >> "$GITHUB_OUTPUT" + + - name: Decide whether to build + id: decision + run: | + aur="${{ steps.aur.outputs.version }}" + last="${{ steps.last.outputs.version }}" + if [[ "${aur}" == "${last}" ]]; then + echo "Already up to date (${aur}), nothing to do" + echo "should_build=false" >> "$GITHUB_OUTPUT" + else + echo "New version detected: ${aur} (was: ${last:-})" + echo "should_build=true" >> "$GITHUB_OUTPUT" + fi + + - name: Trigger build workflow + if: steps.decision.outputs.should_build == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh workflow run build.yml \ + --repo "${{ github.repository }}" \ + --field aur_version="${{ steps.aur.outputs.version }}" + echo "Dispatched build.yml for version ${{ steps.aur.outputs.version }}" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c1ddda0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,284 @@ +# AGENTS.md — webkit2gtk-automator + +This file provides guidance for agentic coding agents working in this repository. + +--- + +## Project Overview + +This project is a Bash/Docker automation daemon that: +1. Polls the AUR RPC API for new `webkit2gtk` releases +2. Builds the package using `makepkg` inside an Arch Linux container +3. Publishes the resulting `.pkg.tar.zst` artifact to GitHub Releases +4. Generates and pushes an updated `webkit2gtk-bin` PKGBUILD to the AUR + +The entire codebase is Bash scripts orchestrated via Docker Compose. There is no application framework, no package manager (npm/pip/cargo), and no test suite. + +--- + +## Repository Structure + +``` +webkit2gtk-automator/ +├── .env.example # Template for required environment variables +├── .gitignore +├── Dockerfile # Arch Linux image with build dependencies +├── docker-compose.yml # Defines the "builder" service +├── README.md +└── scripts/ + ├── entrypoint.sh # Container entrypoint; polling loop + ├── check-update.sh # AUR API poll; triggers build + publish if new version + ├── build.sh # Runs makepkg; collects .pkg.tar.zst artifacts + └── publish.sh # Uploads to GitHub Releases; pushes PKGBUILD to AUR +``` + +Runtime-generated (gitignored): +- `state/last_version` — tracks last published version +- `state/artifacts/` — holds built `.pkg.tar.zst` files +- `webkit2gtk/` — AUR source package clone +- `webkit2gtk-bin/` — AUR binary package clone + +--- + +## Environment Setup + +Copy `.env.example` to `.env` and fill in all required values before starting: + +```bash +cp .env.example .env +# Edit .env with your GITHUB_TOKEN, AUR SSH key path, GPG key, etc. +``` + +All scripts validate required variables at startup using the pattern: +```bash +: "${GITHUB_TOKEN:?GITHUB_TOKEN is not set}" +``` +Missing variables cause an immediate exit with a descriptive error. + +--- + +## Build / Run Commands + +| Purpose | Command | +|---|---| +| Start the daemon (detached) | `docker compose up -d` | +| Start and rebuild the image | `docker compose up -d --build` | +| Watch live logs | `docker compose logs -f` | +| Stop the daemon | `docker compose down` | +| Force a rebuild on next poll | `rm state/last_version && docker compose restart` | + +--- + +## Running a Single Script + +There is no test runner. To manually invoke a single script inside the container: + +```bash +# Run check-update.sh (polls AUR and triggers build+publish if needed) +docker compose run --rm builder bash -c "source /workspace/.env && /workspace/scripts/check-update.sh" + +# Run build.sh directly +docker compose run --rm builder bash -c "source /workspace/.env && /workspace/scripts/build.sh" + +# Run publish.sh directly +docker compose run --rm builder bash -c "source /workspace/.env && /workspace/scripts/publish.sh" +``` + +To test publish without recompiling, pre-place an artifact in `state/artifacts/`: +```bash +# build.sh detects existing artifact and skips makepkg +cp some-existing.pkg.tar.zst state/artifacts/ +docker compose run --rm builder bash -c "source /workspace/.env && /workspace/scripts/publish.sh" +``` + +--- + +## No Test Suite + +There are no unit tests, integration tests, or test commands. Manual invocation of +individual scripts (see above) is the primary validation mechanism. + +--- + +## Code Style Guidelines + +### Shebang and Strict Mode + +Every script **must** begin with: +```bash +#!/usr/bin/env bash +set -euo pipefail +``` +- `set -e` — exit immediately on any non-zero return code +- `set -u` — treat unset variables as errors +- `set -o pipefail` — propagate errors through pipelines + +### Variable Naming + +| Scope | Convention | Example | +|---|---|---| +| All variables (env or local) | `UPPER_SNAKE_CASE` | `PKG_VERSION`, `BUILD_DIR` | +| Functions | `snake_case` | `log`, `die`, `check_deps` | +| Script files | `kebab-case.sh` | `check-update.sh`, `build.sh` | + +### Quoting + +Always double-quote variable expansions. Use `${VAR}` brace syntax: +```bash +# Correct +cp "${SRC_FILE}" "${DEST_DIR}/" +echo "Version: ${PKG_VERSION}" + +# Wrong — never do this +cp $SRC_FILE $DEST_DIR/ +``` + +### Sourcing Files + +Use `source` (not the POSIX `.` shorthand): +```bash +# Correct +source /workspace/.env + +# Avoid +. /workspace/.env +``` + +To auto-export all variables from a sourced file: +```bash +set -a +source /workspace/.env +set +a +``` + +### Logging + +Every script defines a `log()` function with a consistent timestamp prefix: +```bash +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [script-name] $*"; } +``` +Use `log` for all informational output. Use `log "ERROR: ..." >&2` (or `die`) for errors. + +### Arrays + +Use Bash arrays when collecting multiple items: +```bash +packages=() +packages+=("${pkg_file}") +``` + +--- + +## Error Handling Conventions + +### Required Variable Checks + +Use Bash parameter expansion to validate required env vars: +```bash +: "${GITHUB_TOKEN:?GITHUB_TOKEN is not set}" +: "${AUR_SSH_KEY:?AUR_SSH_KEY is not set}" +``` + +### `die()` Helper + +Define and use a `die()` function for fatal errors: +```bash +die() { + log "ERROR: $*" >&2 + exit 1 +} + +# Usage +[[ -f "${pkg_file}" ]] || die "Expected artifact not found: ${pkg_file}" +``` + +### Explicit Guard Blocks + +Prefer explicit `if/else` over bare command calls for critical steps: +```bash +if "${SCRIPT_DIR}/build.sh"; then + log "Build succeeded" +else + log "ERROR: Build failed, aborting" + exit 1 +fi +``` + +### Null/Empty API Response Checks + +Always validate outputs from `curl | jq` pipelines: +```bash +aur_version=$(curl -s "${AUR_API_URL}" | jq -r '.results[0].Version') +if [[ -z "${aur_version}" || "${aur_version}" == "null" ]]; then + log "ERROR: Failed to parse version from AUR response" + exit 1 +fi +``` + +### Graceful No-Op + +For idempotent operations (e.g., git commit), allow the no-op case: +```bash +git commit -m "Update to ${FULL_VERSION}" || { + log "Nothing to commit, already at ${FULL_VERSION}" + exit 0 +} +``` + +--- + +## External Tool Dependencies + +These CLI tools are invoked directly by the scripts. They must be present in the container: + +| Tool | Purpose | +|---|---| +| `curl` | HTTP requests to AUR RPC API | +| `jq` | JSON parsing of AUR API responses | +| `makepkg` | Arch Linux package build tool | +| `gh` | GitHub CLI — creating releases and uploading assets | +| `git` | Cloning/pulling AUR repos, committing, pushing | +| `gpg` | Verifying PGP signatures on source tarballs | +| `sha256sum` | Computing checksums for the artifact | +| `bsdtar` | Extracting the `.pkg.tar.zst` in the generated PKGBUILD | +| `sudo` | Dropping privileges from root to `builduser` for `makepkg` | +| `ssh` | AUR authentication via SSH key | + +All of these are installed in the `Dockerfile`. If adding a new dependency, add it there. + +--- + +## Dockerfile and docker-compose.yml + +- The base image is Arch Linux. Keep it updated with `pacman -Syu` in the `Dockerfile`. +- The container runs as root but drops to a `builduser` account for `makepkg` (which + refuses to run as root). +- Secrets (`.env`, SSH keys, GPG keys) are mounted at runtime via volume mounts defined + in `docker-compose.yml`. Never bake secrets into the image. + +--- + +## ShellCheck + +ShellCheck is the recommended linter for Bash scripts. It is not formally enforced (no +`.shellcheckrc` or CI step), but inline directives are already used in the codebase +(e.g., `# shellcheck source=/dev/null`). When editing scripts, run ShellCheck locally: + +```bash +shellcheck scripts/*.sh +``` + +Fix all warnings before committing. Use inline directives sparingly and only with a +comment explaining why the suppression is necessary. + +--- + +## Adding a New Script + +1. Create the file as `scripts/kebab-case-name.sh` +2. Start with `#!/usr/bin/env bash` and `set -euo pipefail` +3. Define a `log()` function matching the pattern above +4. Validate all required env vars with `: "${VAR:?...}"` +5. Define a `die()` helper for fatal errors +6. Make it executable: `chmod +x scripts/kebab-case-name.sh` +7. If it needs to run inside the container, invoke it via `docker compose run --rm builder` diff --git a/scripts/build.sh b/scripts/build.sh index 5f96727..b6abb64 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,12 +1,16 @@ #!/usr/bin/env bash # build.sh -# Runs INSIDE the Docker container as builduser. +# Runs as builduser (inside Docker or a GitHub Actions container job). # Builds webkit2gtk from the AUR PKGBUILD and copies the resulting -# .pkg.tar.zst packages to /workspace/state/artifacts/. +# .pkg.tar.zst packages to /state/artifacts/. +# +# Works in both environments: +# Docker / local: WORKSPACE=/workspace (default) +# GitHub Actions: WORKSPACE=$GITHUB_WORKSPACE (set by runner) set -euo pipefail -WORKSPACE=/workspace +WORKSPACE="${GITHUB_WORKSPACE:-/workspace}" SRC_DIR="${WORKSPACE}/webkit2gtk" ARTIFACTS_DIR="${WORKSPACE}/state/artifacts" diff --git a/scripts/check-update.sh b/scripts/check-update.sh index a16a48d..7f24dab 100755 --- a/scripts/check-update.sh +++ b/scripts/check-update.sh @@ -1,26 +1,38 @@ #!/usr/bin/env bash # check-update.sh -# Polls the AUR RPC API for the latest webkit2gtk version. -# If a newer version is detected, runs build.sh then publish.sh directly. -# Called by entrypoint.sh on a loop — runs entirely inside the container. +# Queries the AUR RPC API for the latest webkit2gtk version and compares it +# against the last published version on GitHub Releases. +# +# Outputs: +# aur_version= — latest AUR version string +# should_build=true|false — whether a new build is needed +# +# When run inside a GitHub Actions step, GITHUB_OUTPUT is set and the outputs +# are written there automatically. When run locally, they are printed to stdout. +# +# Required env vars (only when comparing against GitHub Releases): +# GITHUB_TOKEN — a token with 'contents: read' on GITHUB_REPO +# GITHUB_REPO — owner/repo, e.g. Brodino96/webkit2gtk-automator set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" - -# Paths -STATE_DIR="${ROOT_DIR}/state" -LAST_VERSION_FILE="${STATE_DIR}/last_version" -mkdir -p "${STATE_DIR}" - -# Logging -# Output goes to stdout so docker compose logs picks it up automatically. log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [check-update] $*" } -# Fetch latest AUR version +set_output() { + local key="$1" + local value="$2" + if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + echo "${key}=${value}" >> "${GITHUB_OUTPUT}" + else + log "OUTPUT: ${key}=${value}" + fi +} + +# --------------------------------------------------------------------------- +# 1. Fetch latest AUR version +# --------------------------------------------------------------------------- AUR_API_URL="https://aur.archlinux.org/rpc/v5/info/webkit2gtk" log "Querying AUR for webkit2gtk" @@ -33,50 +45,31 @@ if [[ -z "${aur_version}" || "${aur_version}" == "null" ]]; then fi log "AUR version: ${aur_version}" +set_output "aur_version" "${aur_version}" -# Compare with last built version -last_version="" -if [[ -f "${LAST_VERSION_FILE}" ]]; then - last_version=$(cat "${LAST_VERSION_FILE}") -fi +# --------------------------------------------------------------------------- +# 2. Fetch last published version from GitHub Releases +# --------------------------------------------------------------------------- +: "${GITHUB_TOKEN:?GITHUB_TOKEN is not set}" +: "${GITHUB_REPO:?GITHUB_REPO is not set}" -log "Last built version: ${last_version:-}" +log "Fetching latest GitHub Release tag from ${GITHUB_REPO}" +tag=$(gh release list \ + --repo "${GITHUB_REPO}" \ + --limit 1 \ + --json tagName \ + --jq '.[0].tagName // ""' 2>/dev/null || echo "") +last_version="${tag#v}" +log "Last published version: ${last_version:-}" + +# --------------------------------------------------------------------------- +# 3. Compare +# --------------------------------------------------------------------------- if [[ "${aur_version}" == "${last_version}" ]]; then - log "Already up to date, nothing to do" - exit 0 -fi - -log "New version detected: ${aur_version} (was: ${last_version:-}), starting build" - -# Update the webkit2gtk AUR clone -WEBKIT2GTK_DIR="${ROOT_DIR}/webkit2gtk" -if [[ -d "${WEBKIT2GTK_DIR}/.git" ]]; then - log "Pulling latest PKGBUILD from AUR" - git -C "${WEBKIT2GTK_DIR}" pull --ff-only + log "Already up to date (${aur_version}), nothing to do" + set_output "should_build" "false" else - log "Cloning webkit2gtk from AUR" - git clone https://aur.archlinux.org/webkit2gtk.git "${WEBKIT2GTK_DIR}" + log "New version detected: ${aur_version} (was: ${last_version:-})" + set_output "should_build" "true" fi - -# Build -log "Running build" -if "${SCRIPT_DIR}/build.sh"; then - log "Build succeeded" -else - log "ERROR: Build failed, aborting" - exit 1 -fi - -# Publish -log "Running publish" -if "${SCRIPT_DIR}/publish.sh"; then - log "Publish succeeded" -else - log "ERROR: Publish failed, exit code: $?" - exit 1 -fi - -# Record new version -echo "${aur_version}" > "${LAST_VERSION_FILE}" -log "Updated last_version to ${aur_version}, done" diff --git a/scripts/publish.sh b/scripts/publish.sh index 2e4ea28..711d34e 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # publish.sh -# Runs INSIDE the Docker container as builduser. +# Runs as builduser (inside Docker or a GitHub Actions container job). # # Steps: # 1. Find the built webkit2gtk .pkg.tar.zst in state/artifacts/ @@ -8,10 +8,14 @@ # 3. Update webkit2gtk-bin/PKGBUILD with the new version, URL and sha256sum # 4. Regenerate webkit2gtk-bin/.SRCINFO # 5. Commit and push webkit2gtk-bin/ to the AUR +# +# Works in both environments: +# Docker / local: WORKSPACE=/workspace (default) +# GitHub Actions: WORKSPACE=$GITHUB_WORKSPACE (set by runner) set -euo pipefail -WORKSPACE=/workspace +WORKSPACE="${GITHUB_WORKSPACE:-/workspace}" ARTIFACTS_DIR="${WORKSPACE}/state/artifacts" BIN_PKG_DIR="${WORKSPACE}/webkit2gtk-bin" SRC_PKGBUILD="${WORKSPACE}/webkit2gtk/PKGBUILD"