Files
openwork/.github/workflows/aur-validate.yml
2026-04-20 10:56:51 -07:00

458 lines
15 KiB
YAML

name: AUR Validate
on:
workflow_dispatch:
inputs:
ref:
description: "Git ref to validate (branch, SHA, or tag)"
required: false
type: string
default: dev
version:
description: "Package version override (e.g., 0.11.160)"
required: false
type: string
arch:
description: "Target architecture"
required: false
type: choice
options:
- x86_64
default: x86_64
mode:
description: "Validation mode"
required: false
type: choice
options:
- smoke
- publish-ready
default: smoke
artifact_source:
description: "Where to source desktop .deb"
required: false
type: choice
options:
- local-build-artifact
- release
default: local-build-artifact
release_tag:
description: "Release tag when artifact_source=release (e.g., v0.11.160)"
required: false
type: string
artifact_run_id:
description: "Workflow run ID to download artifact from when artifact_source=local-build-artifact"
required: false
type: string
artifact_name:
description: "Artifact name in artifact_run_id"
required: false
type: string
default: openwork-desktop-linux-amd64-deb
asset_url_x86_64:
description: "Optional explicit public URL for x86_64 .deb"
required: false
type: string
push_to_aur:
description: "Push validated PKGBUILD/.SRCINFO to AUR"
required: false
type: boolean
default: false
aur_repo:
description: "AUR repo name"
required: false
type: string
default: openwork
workflow_call:
inputs:
ref:
required: false
type: string
default: dev
version:
required: false
type: string
arch:
required: false
type: string
default: x86_64
mode:
required: false
type: string
default: smoke
artifact_source:
required: false
type: string
default: local-build-artifact
release_tag:
required: false
type: string
artifact_run_id:
required: false
type: string
artifact_name:
required: false
type: string
default: openwork-desktop-linux-amd64-deb
asset_url_x86_64:
required: false
type: string
push_to_aur:
required: false
type: boolean
default: false
aur_repo:
required: false
type: string
default: openwork
secrets:
AUR_SSH_PRIVATE_KEY:
required: false
permissions:
contents: read
actions: read
concurrency:
group: aur-validate-${{ github.workflow }}-${{ inputs.ref || github.ref_name }}
cancel-in-progress: true
jobs:
aur-validate:
name: Validate AUR package (${{ inputs.arch }}, ${{ inputs.mode }})
runs-on: ubuntu-22.04
container:
image: archlinux:latest
env:
TARGET_ARCH: ${{ inputs.arch || 'x86_64' }}
MODE: ${{ inputs.mode || 'smoke' }}
ARTIFACT_SOURCE: ${{ inputs.artifact_source || 'local-build-artifact' }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_RELEASE_TAG: ${{ inputs.release_tag }}
INPUT_ASSET_URL_X86_64: ${{ inputs.asset_url_x86_64 }}
INPUT_ARTIFACT_RUN_ID: ${{ inputs.artifact_run_id }}
INPUT_ARTIFACT_NAME: ${{ inputs.artifact_name || 'openwork-desktop-linux-amd64-deb' }}
PUSH_TO_AUR: ${{ inputs.push_to_aur && 'true' || 'false' }}
AUR_REPO: ${{ inputs.aur_repo || 'openwork' }}
steps:
- name: Validate workflow inputs
shell: bash
run: |
set -euo pipefail
if [ "${TARGET_ARCH}" != "x86_64" ]; then
echo "Only x86_64 is currently supported." >&2
exit 1
fi
if [ "${MODE}" != "smoke" ] && [ "${MODE}" != "publish-ready" ]; then
echo "mode must be smoke or publish-ready" >&2
exit 1
fi
if [ "${ARTIFACT_SOURCE}" != "local-build-artifact" ] && [ "${ARTIFACT_SOURCE}" != "release" ]; then
echo "artifact_source must be local-build-artifact or release" >&2
exit 1
fi
if [ "${ARTIFACT_SOURCE}" = "local-build-artifact" ] && [ -z "${INPUT_ASSET_URL_X86_64:-}" ] && [ -z "${INPUT_ARTIFACT_RUN_ID:-}" ]; then
echo "artifact_run_id is required when artifact_source=local-build-artifact and asset_url_x86_64 is empty" >&2
exit 1
fi
if [ "${PUSH_TO_AUR}" = "true" ] && [ "${ARTIFACT_SOURCE}" = "local-build-artifact" ] && [ -z "${INPUT_ASSET_URL_X86_64:-}" ]; then
echo "For push_to_aur with local-build-artifact, set asset_url_x86_64 to a public URL (or use artifact_source=release)." >&2
exit 1
fi
- name: Install Arch packaging dependencies
shell: bash
run: |
set -euo pipefail
pacman -Syu --noconfirm --needed \
base-devel \
curl \
git \
jq \
namcap \
openssh \
python \
sudo \
xorg-server-xvfb
- name: Checkout target ref
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref_name }}
fetch-depth: 0
- name: Create non-root makepkg user
shell: bash
run: |
set -euo pipefail
useradd -m -s /bin/bash builder
echo "builder ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/builder
chmod 0440 /etc/sudoers.d/builder
chown -R builder:builder "$GITHUB_WORKSPACE"
- name: Download release asset (.deb)
if: env.ARTIFACT_SOURCE == 'release'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
release_tag="${INPUT_RELEASE_TAG:-}"
if [ -z "$release_tag" ] && [ -n "${INPUT_VERSION:-}" ]; then
release_tag="v${INPUT_VERSION}"
fi
if [ -z "$release_tag" ]; then
echo "release_tag or version is required when artifact_source=release" >&2
exit 1
fi
case "$release_tag" in
v*) ;;
*) release_tag="v${release_tag}" ;;
esac
asset_url="${INPUT_ASSET_URL_X86_64:-}"
if [ -z "$asset_url" ]; then
asset_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${release_tag}/openwork-desktop-linux-amd64.deb"
fi
mkdir -p /tmp/aur-artifacts
curl -fL --retry 5 --retry-all-errors --retry-delay 2 "$asset_url" -o /tmp/aur-artifacts/openwork-desktop-linux-amd64.deb
echo "RESOLVED_RELEASE_TAG=${release_tag}" >> "$GITHUB_ENV"
echo "RESOLVED_ASSET_URL_X86_64=${asset_url}" >> "$GITHUB_ENV"
- name: Download local workflow artifact
if: env.ARTIFACT_SOURCE == 'local-build-artifact' && env.INPUT_ASSET_URL_X86_64 == ''
uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact_name || 'openwork-desktop-linux-amd64-deb' }}
run-id: ${{ inputs.artifact_run_id }}
github-token: ${{ github.token }}
path: /tmp/aur-artifacts
- name: Download x86_64 asset URL override
if: env.ARTIFACT_SOURCE == 'local-build-artifact' && env.INPUT_ASSET_URL_X86_64 != ''
shell: bash
run: |
set -euo pipefail
mkdir -p /tmp/aur-artifacts
curl -fL --retry 5 --retry-all-errors --retry-delay 2 "${INPUT_ASSET_URL_X86_64}" -o /tmp/aur-artifacts/openwork-desktop-linux-amd64.deb
- name: Resolve local artifact path
if: env.ARTIFACT_SOURCE == 'local-build-artifact'
shell: bash
run: |
set -euo pipefail
deb_path=$(find /tmp/aur-artifacts -maxdepth 3 -type f -name '*.deb' | head -n 1)
if [ -z "${deb_path:-}" ]; then
echo "No .deb file found in /tmp/aur-artifacts" >&2
exit 1
fi
if [ "$deb_path" != "/tmp/aur-artifacts/openwork-desktop-linux-amd64.deb" ]; then
cp "$deb_path" /tmp/aur-artifacts/openwork-desktop-linux-amd64.deb
fi
- name: Resolve version, URL, and checksum
id: resolve
shell: bash
run: |
set -euo pipefail
deb_path="/tmp/aur-artifacts/openwork-desktop-linux-amd64.deb"
if [ ! -f "$deb_path" ]; then
echo "Expected $deb_path to exist" >&2
exit 1
fi
version="${INPUT_VERSION:-}"
if [ -z "$version" ] && [ -n "${RESOLVED_RELEASE_TAG:-}" ]; then
version="${RESOLVED_RELEASE_TAG#v}"
fi
if [ -z "$version" ]; then
version="$(awk -F= '$1 == "pkgver" {print $2; exit}' packaging/aur/PKGBUILD)"
fi
if [ -z "$version" ]; then
echo "Unable to determine version. Pass inputs.version." >&2
exit 1
fi
sha256="$(sha256sum "$deb_path" | awk '{print $1}')"
source_url="${RESOLVED_ASSET_URL_X86_64:-}"
if [ -z "$source_url" ] && [ -n "${INPUT_ASSET_URL_X86_64:-}" ]; then
source_url="${INPUT_ASSET_URL_X86_64}"
fi
if [ -z "$source_url" ]; then
source_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/v${version}/openwork-desktop-linux-amd64.deb"
fi
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "sha256=${sha256}" >> "$GITHUB_OUTPUT"
echo "source_url=${source_url}" >> "$GITHUB_OUTPUT"
- name: Patch PKGBUILD for x86_64 validation
shell: bash
env:
RESOLVED_VERSION: ${{ steps.resolve.outputs.version }}
RESOLVED_SHA256: ${{ steps.resolve.outputs.sha256 }}
RESOLVED_SOURCE_URL: ${{ steps.resolve.outputs.source_url }}
run: |
set -euo pipefail
python3 - <<'PY'
import pathlib
import re
import os
path = pathlib.Path("packaging/aur/PKGBUILD")
text = path.read_text()
version = os.environ["RESOLVED_VERSION"]
sha256 = os.environ["RESOLVED_SHA256"]
source_url = os.environ["RESOLVED_SOURCE_URL"]
text = re.sub(r"^pkgver=.*$", f"pkgver={version}", text, flags=re.M)
text = re.sub(r"^pkgrel=.*$", "pkgrel=1", text, flags=re.M)
text = re.sub(r"^arch=.*$", "arch=('x86_64')", text, flags=re.M)
text = re.sub(
r"^source_x86_64=.*$",
"source_x86_64=(\"${pkgname}-${pkgver}.deb::" + source_url + "\")",
text,
flags=re.M,
)
text = re.sub(
r"^sha256sums_x86_64=.*$",
f"sha256sums_x86_64=('{sha256}')",
text,
flags=re.M,
)
text = re.sub(r"^source_aarch64=.*\n?", "", text, flags=re.M)
text = re.sub(r"^sha256sums_aarch64=.*\n?", "", text, flags=re.M)
path.write_text(text)
PY
- name: Regenerate .SRCINFO
shell: bash
run: |
set -euo pipefail
workspace_path="$GITHUB_WORKSPACE"
sudo -u builder bash -lc "cd '$workspace_path/packaging/aur' && makepkg --printsrcinfo > .SRCINFO"
- name: Build package with makepkg
shell: bash
run: |
set -euo pipefail
workspace_path="$GITHUB_WORKSPACE"
sudo -u builder bash -lc "cd '$workspace_path/packaging/aur' && makepkg -f --syncdeps --noconfirm --cleanbuild"
- name: Run namcap checks
if: env.MODE == 'publish-ready'
shell: bash
run: |
set -euo pipefail
cd packaging/aur
namcap PKGBUILD || true
pkg_file=$(ls -1 openwork-*.pkg.tar.* | head -n 1)
namcap "$pkg_file" || true
- name: Install built package
shell: bash
run: |
set -euo pipefail
cd packaging/aur
pkg_file=$(ls -1 openwork-*.pkg.tar.* | head -n 1)
pacman -U --noconfirm "$pkg_file"
- name: Smoke launch
shell: bash
run: |
set -euo pipefail
pacman -Ql openwork > /tmp/openwork-installed-files.txt
if [ ! -s /tmp/openwork-installed-files.txt ]; then
echo "openwork package install listing is empty" >&2
exit 1
fi
launch_bin=""
for candidate in \
"$(command -v openwork 2>/dev/null || true)" \
/usr/bin/openwork \
/opt/openwork/openwork \
/opt/openwork/openwork-desktop \
/opt/openwork/opencode
do
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
launch_bin="$candidate"
break
fi
done
if [ -n "$launch_bin" ]; then
xvfb-run -a bash -lc '"$1" --help >/tmp/openwork-help.txt 2>&1 || "$1" --version >/tmp/openwork-version.txt 2>&1 || true' -- "$launch_bin"
else
echo "No runnable desktop binary found; install sanity check passed."
fi
- name: Publish to AUR
if: env.PUSH_TO_AUR == 'true'
env:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
RELEASE_TAG: v${{ steps.resolve.outputs.version }}
AUR_SKIP_UPDATE: "1"
shell: bash
run: |
set -euo pipefail
if [ -z "${AUR_SSH_PRIVATE_KEY:-}" ]; then
echo "AUR_SSH_PRIVATE_KEY not set; cannot push to AUR." >&2
exit 1
fi
mkdir -p "$HOME/.ssh"
touch "$HOME/.ssh/known_hosts"
ssh-keygen -R aur.archlinux.org >/dev/null 2>&1 || true
ssh-keyscan -t rsa,ecdsa,ed25519 aur.archlinux.org >> "$HOME/.ssh/known_hosts" 2>/dev/null
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
key_path="$tmp_dir/aur.key"
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > "$key_path"
chmod 600 "$key_path"
aur_remote="ssh://aur@aur.archlinux.org/${AUR_REPO}.git"
export GIT_SSH_COMMAND="ssh -i $key_path -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"
git clone "$aur_remote" "$tmp_dir/aur"
cp packaging/aur/PKGBUILD "$tmp_dir/aur/PKGBUILD"
cp packaging/aur/.SRCINFO "$tmp_dir/aur/.SRCINFO"
cd "$tmp_dir/aur"
if git diff --quiet -- PKGBUILD .SRCINFO; then
echo "AUR already up to date for ${AUR_REPO}."
exit 0
fi
git add PKGBUILD .SRCINFO
git -c user.name="OpenWork Release Bot" \
-c user.email="release-bot@users.noreply.github.com" \
commit -m "chore(aur): update PKGBUILD for ${RELEASE_TAG#v}"
current_branch="$(git symbolic-ref --short HEAD 2>/dev/null || true)"
if [ -z "$current_branch" ]; then
current_branch="master"
fi
git push origin "HEAD:${current_branch}"