From 81ab384e481531daee8599449b014ab1f9a809d5 Mon Sep 17 00:00:00 2001 From: Meliox Date: Wed, 10 Jun 2026 19:26:37 +0200 Subject: [PATCH] Move test and CI to separate PR --- .github/workflows/install-test.yml | 44 ----- .github/workflows/test-release.yml | 71 -------- .github/workflows/weekly-patch-test.yml | 110 ------------- src/Scripts/test/fetch-proxmox-files.sh | 55 ------- src/Scripts/test/test-mods.sh | 206 ------------------------ 5 files changed, 486 deletions(-) delete mode 100644 .github/workflows/install-test.yml delete mode 100644 .github/workflows/test-release.yml delete mode 100644 .github/workflows/weekly-patch-test.yml delete mode 100644 src/Scripts/test/fetch-proxmox-files.sh delete mode 100644 src/Scripts/test/test-mods.sh diff --git a/.github/workflows/install-test.yml b/.github/workflows/install-test.yml deleted file mode 100644 index 9ba111e..0000000 --- a/.github/workflows/install-test.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Test Patches - Post Build - -# Runs automatically after every "Test Release Build" run completes (that build -# is itself triggered by the 'make-test-build' label on a PR). Reuses the .deb -# that build produced — no rebuild — installs it on a runner seeded with the -# latest upstream Proxmox files, and verifies that every mod's patches (and -# post-apply hooks) apply and revert cleanly. Fails if any mod fails. -# -# Wired via workflow_run, so it fires for builds on ANY branch/PR. (GitHub always -# runs the default-branch copy of this file, so it activates once merged to main.) - -on: - workflow_run: - workflows: ["Test Release Build"] - types: [completed] - -permissions: - contents: read - actions: read # required to download the triggering run's artifact - -jobs: - patch-install-test: - runs-on: ubuntu-latest - if: github.event.workflow_run.conclusion == 'success' - steps: - - uses: actions/checkout@v4 - - - name: Download deb from the build run - uses: actions/download-artifact@v4 - with: - run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - pattern: pve-mods-test-build-* - merge-multiple: true - path: deb - - - name: Fetch upstream Proxmox files - run: bash src/Scripts/test/fetch-proxmox-files.sh - - - name: Install built package - run: sudo apt-get install -y ./deb/*.deb - - - name: Test all mods - run: sudo bash src/Scripts/test/test-mods.sh all diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml deleted file mode 100644 index 24d2947..0000000 --- a/.github/workflows/test-release.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Test Release Build - -# Runs when the 'make-test-build' label is applied to a pull request, -# or on any subsequent commit while that label is present. - -on: - pull_request: - types: [labeled, synchronize] - -jobs: - test-release-build: - runs-on: ubuntu-latest - if: contains(github.event.pull_request.labels.*.name, 'make-test-build') - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Install build deps - run: sudo apt-get install -y devscripts debhelper build-essential - - - name: Compute build version - id: version - run: | - BASE_VERSION=$(grep -m1 '(' debian/changelog | sed 's/.*(\(.*\)).*/\1/') - PR_NUMBER="${{ github.event.pull_request.number }}" - SHORT_SHA=$(git rev-parse --short HEAD) - BUILD_VERSION="${BASE_VERSION}+pr${PR_NUMBER}.${SHORT_SHA}" - echo "build_version=$BUILD_VERSION" >> "$GITHUB_OUTPUT" - echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT" - echo "Building version: $BUILD_VERSION" - - - name: Inject version into PVEMod_Config.pm - run: | - export BUILD_VERSION="${{ steps.version.outputs.build_version }}" - perl -i -pe 'BEGIN { $v = $ENV{BUILD_VERSION} } - s/(our \$VERSION\s*=\s*'"'"')[^'"'"']*'"'"';/$1'"'"'$v'"'"';/ - ' src/PVENodeInfo/PVEMod_Config.pm - - - name: Set package version in changelog - run: | - BUILD_VERSION="${{ steps.version.outputs.build_version }}" - sed -i -E "0,/\(([^)]*)\)/s//(${BUILD_VERSION})/" debian/changelog - - - name: Build package - run: dpkg-buildpackage -us -uc -b - - - name: Verify package contents - run: | - echo "=== Built files ===" - ls -la ../*.deb - echo "" - echo "=== Package info ===" - dpkg-deb --info ../*.deb - echo "" - echo "=== Package contents ===" - dpkg-deb --contents ../*.deb - - - name: Collect deb artifacts - id: collect - run: | - mkdir -p artifacts - cp ../*.deb artifacts/ - - - name: Upload deb as artifact - uses: actions/upload-artifact@v4 - with: - name: pve-mods-test-build-pr${{ github.event.pull_request.number }}-${{ steps.version.outputs.short_sha }} - path: artifacts/*.deb - retention-days: 3 \ No newline at end of file diff --git a/.github/workflows/weekly-patch-test.yml b/.github/workflows/weekly-patch-test.yml deleted file mode 100644 index 2b239dd..0000000 --- a/.github/workflows/weekly-patch-test.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Test Patches - Weekly - -# Weekly regression test of pve-mod against the latest upstream Proxmox VE files. -# Mods are auto-discovered from the [modules] section of src/pve-mod.conf and -# tested in parallel - adding a mod needs no change here. If a mod fails, an -# issue is opened (or updated) per failed mod explaining why. - -on: - schedule: - - cron: '0 6 * * 1' # Mondays 06:00 UTC - workflow_dispatch: - -permissions: - contents: read - issues: write - -jobs: - discover: - runs-on: ubuntu-latest - outputs: - mods: ${{ steps.list.outputs.mods }} - steps: - - uses: actions/checkout@v4 - - - name: Discover mods from [modules] - id: list - run: | - MODS=$(awk -F= ' - /^\[/ { in_sec = ($0 == "[modules]") } - in_sec && /^[^#=]+=/ { - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1); print $1 - }' src/pve-mod.conf | jq -R . | jq -cs .) - echo "Discovered mods: $MODS" - echo "mods=$MODS" >> "$GITHUB_OUTPUT" - - test: - needs: discover - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - mod: ${{ fromJSON(needs.discover.outputs.mods) }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # ── Build (mirrors test-release.yml) ─────────────────────────────────── - - name: Install build deps - run: sudo apt-get install -y devscripts debhelper build-essential - - - name: Inject version into PVEMod_Config.pm - run: | - VERSION=$(grep -m1 '(' debian/changelog | sed 's/.*(\(.*\)).*/\1/') - SHORT_SHA=$(git rev-parse --short HEAD) - export BUILD_VERSION="${VERSION}-test-${SHORT_SHA}" - perl -i -pe 'BEGIN { $v = $ENV{BUILD_VERSION} } - s/(our \$VERSION\s*=\s*'"'"')[^'"'"']*'"'"';/$1'"'"'$v'"'"';/ - ' src/PVENodeInfo/PVEMod_Config.pm - - - name: Build package - run: dpkg-buildpackage -us -uc -b - - # ── Test ─────────────────────────────────────────────────────────────── - - name: Fetch upstream Proxmox files - run: bash src/Scripts/test/fetch-proxmox-files.sh - - - name: Install built package - run: sudo apt-get install -y ../pve-mod_*.deb - - - name: Test mod - run: | - set -o pipefail - sudo bash src/Scripts/test/test-mods.sh "${{ matrix.mod }}" 2>&1 | tee test-output.log - - - name: Report failure as issue - if: failure() - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MOD: ${{ matrix.mod }} - run: | - TITLE="[patch-break] ${MOD}" - { - echo "The weekly patch test failed for mod \`${MOD}\` against the latest Proxmox VE files." - echo "" - echo "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - echo "" - echo "
Test log" - echo "" - echo '```' - tail -n 200 test-output.log 2>/dev/null || echo "(no log captured)" - echo '```' - echo "
" - } > issue-body.md - - # Ensure the label exists (no-op if it already does). - gh label create patch-break --color B60205 \ - --description "pve-mod patch failed against upstream Proxmox" 2>/dev/null || true - - EXISTING=$(gh issue list --state open --label patch-break \ - --search "$TITLE in:title" --json number,title \ - --jq ".[] | select(.title==\"$TITLE\") | .number" | head -n1) - - if [[ -n "$EXISTING" ]]; then - gh issue comment "$EXISTING" --body-file issue-body.md - echo "Commented on existing issue #$EXISTING" - else - gh issue create --title "$TITLE" --label patch-break --body-file issue-body.md - echo "Opened new issue for mod $MOD" - fi diff --git a/src/Scripts/test/fetch-proxmox-files.sh b/src/Scripts/test/fetch-proxmox-files.sh deleted file mode 100644 index 282cfa7..0000000 --- a/src/Scripts/test/fetch-proxmox-files.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -# src/Scripts/test/fetch-proxmox-files.sh -# -# Downloads the upstream Proxmox VE files that pve-mod patches and installs them -# at their real system paths, so the patch engine can be exercised on a plain -# (non-Proxmox) CI runner. -# -# Pulls pve-manager and proxmox-widget-toolkit from the Proxmox no-subscription -# repository (default: PVE 9 / Debian trixie) and extracts the target files. -# -# CI-only helper. The repo is added with [trusted=yes]: we are only fetching -# public, unmodified UI files to patch against, not installing Proxmox, so -# managing the signing key would add nothing. - -set -euo pipefail - -SUITE="${PVE_SUITE:-trixie}" -WORKDIR="$(mktemp -d)" -trap 'rm -rf "$WORKDIR"' EXIT - -# Files provided by the two packages, relative to the filesystem root. These are -# exactly the paths the .patch files (and the nag_screen post-apply hook) touch. -FILES=( - usr/share/perl5/PVE/API2/Nodes.pm - usr/share/pve-manager/js/pvemanagerlib.js - usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js - usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js -) - -echo "[fetch] Configuring Proxmox no-subscription repo ($SUITE)..." -echo "deb [trusted=yes] http://download.proxmox.com/debian/pve $SUITE pve-no-subscription" \ - | sudo tee /etc/apt/sources.list.d/pve-test.list >/dev/null -sudo apt-get update -qq - -echo "[fetch] Downloading pve-manager and proxmox-widget-toolkit..." -cd "$WORKDIR" -apt-get download pve-manager proxmox-widget-toolkit - -for deb in *.deb; do - echo "[fetch] Extracting $deb" - dpkg-deb -x "$deb" extract -done - -for rel in "${FILES[@]}"; do - src="extract/$rel" - dst="/$rel" - if [[ ! -f "$src" ]]; then - echo "[fetch] ERROR: expected file not found in packages: $rel" >&2 - exit 1 - fi - sudo install -Dm644 "$src" "$dst" - echo "[fetch] Installed $dst" -done - -echo "[fetch] Done." diff --git a/src/Scripts/test/test-mods.sh b/src/Scripts/test/test-mods.sh deleted file mode 100644 index 86d3bf1..0000000 --- a/src/Scripts/test/test-mods.sh +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env bash -# src/Scripts/test/test-mods.sh -# -# Exercises pve-mod's patch engine against the installed Proxmox files. -# -# Usage: test-mods.sh | all -# -# Mods are auto-discovered from the [modules] section of the main config, so -# adding a new mod (a new [modules] key plus a patches// directory) is -# picked up automatically - no edits to this script are required. -# -# For each target mod it: -# 1. enables only that mod in the main config -# 2. turns on every conditional flag referenced by the mod's patches.list, -# so all of the mod's patches are exercised (e.g. node_info's gpu_history) -# 3. runs apply-patches.sh and asserts it exits 0 -# 4. runs revert-patches.sh and asserts it reports no unclean reversions -# 5. restores the pristine Proxmox files before moving to the next mod -# -# Failure detection relies on the apply exit code: the patch engine performs an -# atomic preflight dry-run and exits non-zero if any mod cannot apply cleanly. -# -# Must run as root (it edits /etc/pve-mod and the patched system files). - -set -u - -MAIN_CONF="${PVE_MOD_MAIN_CONF:-/etc/pve-mod/pve-mod.conf}" -CONFD_DIR="${PVE_MOD_CONFD_DIR:-/etc/pve-mod/conf.d}" -PATCHES_DIR="${PVE_MOD_PATCHES_DIR:-/usr/lib/pve-mod/patches}" -APPLY="${PVE_MOD_APPLY:-/usr/lib/pve-mod/apply-patches.sh}" -REVERT="${PVE_MOD_REVERT:-/usr/lib/pve-mod/revert-patches.sh}" - -# Proxmox files touched by the patches; snapshotted before and restored after -# each mod so every mod is tested from a pristine baseline. -PROXMOX_FILES=( - /usr/share/perl5/PVE/API2/Nodes.pm - /usr/share/pve-manager/js/pvemanagerlib.js - /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js - /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js -) - -SNAP_DIR="$(mktemp -d)" -trap 'rm -rf "$SNAP_DIR"' EXIT - -info() { echo "[test] $*"; } -warn() { echo "[test] WARNING: $*" >&2; } - -# List the keys of the [modules] section in the main config, one per line. -list_modules() { - [[ -f "$MAIN_CONF" ]] || return 0 - awk -F= ' - /^\[/ { in_sec = ($0 == "[modules]") } - in_sec && /^[^#=]+=/ { - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1) - print $1 - } - ' "$MAIN_CONF" -} - -# set_conf
: set an existing key within a section. -set_conf() { - local file="$1" section="$2" key="$3" value="$4" - [[ -f "$file" ]] || { warn "config not found: $file"; return 1; } - awk -v sec="[$section]" -v k="$key" -v v="$value" ' - /^\[/ { in_sec = ($0 == sec) } - { - if (in_sec && $0 ~ "^[[:space:]]*"k"[[:space:]]*=") { - print k"="v - } else { - print - } - } - ' "$file" > "$file.tmp" && mv "$file.tmp" "$file" -} - -# Disable every discovered mod in the main config. -disable_all_modules() { - local m - for m in $(list_modules); do - set_conf "$MAIN_CONF" modules "$m" 0 - done -} - -# Turn on every conditional flag a mod's patches.list references, so all of its -# patches become active. Conditions look like: section.key=value -enable_conditions() { - local mod="$1" - local manifest="$PATCHES_DIR/$mod/patches.list" - local mod_conf="$CONFD_DIR/$mod.conf" - [[ -f "$manifest" ]] || return 0 - local line cond local_key want sect ckey - while IFS= read -r line; do - line="${line%%#*}" - line="$(echo "$line" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')" - [[ -z "$line" ]] && continue - # Skip lines without a condition (no whitespace after the patch name). - [[ "$line" == *[[:space:]]* ]] || continue - cond="$(echo "${line#*[[:space:]]}" | sed -E 's/^[[:space:]]+//')" - [[ "$cond" == *=* ]] || continue - local_key="${cond%%=*}" - want="${cond#*=}" - sect="${local_key%%.*}" - ckey="${local_key#*.}" - set_conf "$mod_conf" "$sect" "$ckey" "$want" \ - && info " enabled condition $local_key=$want in $(basename "$mod_conf")" - done < "$manifest" -} - -snapshot_files() { - rm -rf "$SNAP_DIR"/*; mkdir -p "$SNAP_DIR" - local i=0 f - for f in "${PROXMOX_FILES[@]}"; do - if [[ -e "$f" || -L "$f" ]]; then - cp -a "$f" "$SNAP_DIR/$i" - fi - i=$((i+1)) - done -} - -restore_files() { - local i=0 f - for f in "${PROXMOX_FILES[@]}"; do - if [[ -e "$SNAP_DIR/$i" || -L "$SNAP_DIR/$i" ]]; then - rm -f "$f" - cp -a "$SNAP_DIR/$i" "$f" - fi - i=$((i+1)) - done -} - -# Test a single mod end to end. Returns 0 on success, 1 on failure. -test_one_mod() { - local mod="$1" - echo "::group::Testing mod: $mod" - local ok=0 - - if [[ ! -f "$PATCHES_DIR/$mod/patches.list" ]]; then - warn "no patch manifest for mod '$mod' ($PATCHES_DIR/$mod/patches.list)" - echo "::endgroup::" - return 1 - fi - - disable_all_modules - set_conf "$MAIN_CONF" modules "$mod" 1 - enable_conditions "$mod" - snapshot_files - - info "Applying patches for '$mod'..." - local apply_log apply_rc - apply_log="$("$APPLY" 2>&1)"; apply_rc=$? - echo "$apply_log" - if [[ $apply_rc -ne 0 ]]; then - warn "apply-patches.sh failed for mod '$mod' (exit $apply_rc)" - ok=1 - fi - - if [[ $ok -eq 0 ]]; then - info "Reverting patches for '$mod'..." - local revert_log - revert_log="$("$REVERT" 2>&1)" - echo "$revert_log" - if echo "$revert_log" | grep -qE "could not be reverted cleanly|reported an error"; then - warn "revert-patches.sh reported an unclean revert for mod '$mod'" - ok=1 - fi - fi - - restore_files - - if [[ $ok -eq 0 ]]; then - info "PASS: $mod" - else - warn "FAIL: $mod" - fi - echo "::endgroup::" - return $ok -} - -# ── main ────────────────────────────────────────────────────────────────────── -[[ $# -eq 1 ]] || { echo "Usage: $0 |all" >&2; exit 2; } - -if ! command -v patch >/dev/null 2>&1; then - warn "'patch' command not found; install the 'patch' package." - exit 1 -fi - -targets=() -if [[ "$1" == "all" ]]; then - mapfile -t targets < <(list_modules) - [[ ${#targets[@]} -gt 0 ]] || { warn "no mods found in [modules] of $MAIN_CONF"; exit 1; } -else - targets=("$1") -fi - -failed=() -for mod in "${targets[@]}"; do - test_one_mod "$mod" || failed+=("$mod") -done - -echo "" -if [[ ${#failed[@]} -gt 0 ]]; then - warn "Failed mods: ${failed[*]}" - exit 1 -fi -info "All tested mods passed: ${targets[*]}" -exit 0