diff --git a/.github/scripts/gh-purge-pipelines.sh b/.github/scripts/gh-purge-pipelines.sh new file mode 100755 index 0000000000..fc86a502ab --- /dev/null +++ b/.github/scripts/gh-purge-pipelines.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + echo "Usage: $0 --org ORG --repo REPO --branch BRANCH --keep N [--dry-run] [--debug]" + exit 1 +} + +log_info() { echo "[INFO] $*"; } +log_warn() { echo "[WARN] $*" >&2; } +log_error() { echo "[ERROR] $*" >&2; } + +ORG="" +REPO="" +BRANCH="" +KEEP=0 +DRY_RUN=0 +DEBUG=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --org) ORG="$2"; shift 2 ;; + --repo) REPO="$2"; shift 2 ;; + --branch) BRANCH="$2"; shift 2 ;; + --keep) KEEP="$2"; shift 2 ;; + --dry-run) DRY_RUN=1; shift ;; + --debug) DEBUG=1; shift ;; + *) usage ;; + esac +done + +if [[ -z "$ORG" || -z "$REPO" || -z "$BRANCH" || "$KEEP" -le 0 ]]; then + usage +fi + +if ! command -v gh >/dev/null 2>&1; then + log_error "GitHub CLI 'gh' is required but not found" + exit 1 +fi + +if [[ $DEBUG -eq 1 ]]; then + set -x +fi + +log_info "Fetching runs for $ORG/$REPO on branch $BRANCH ..." + +runs=$( + gh api \ + -H "Accept: application/vnd.github+json" \ + "/repos/$ORG/$REPO/actions/runs?branch=$BRANCH&per_page=100" \ + --jq '.workflow_runs[] | "\(.id)|\(.run_number)|\(.conclusion)|\(.created_at)"' | + sort -t'|' -k2,2nr +) + +mapfile -t entries <<<"$runs" + +declare -A keep + +# keep N most recent +count=0 +for entry in "${entries[@]}"; do + ((count++)) || true + run_id="${entry%%|*}" + if ((count <= KEEP)); then + keep["$run_id"]=1 + fi +done + +# helper to find neighbor entry (previous/next successful) +get_neighbor_entry() { + local idx="$1" + local direction="$2" # -1 for previous, +1 for next + local new_idx=$((idx + direction)) + if ((new_idx >= 0 && new_idx < ${#entries[@]})); then + echo "${entries[$new_idx]}" + fi +} + +# find broken runs and mark them + neighbors +for idx in "${!entries[@]}"; do + IFS='|' read -r run_id number conclusion created <<<"${entries[$idx]}" + jobs=$(gh run view "$run_id" -R "$ORG/$REPO" --json jobs \ + --template '{{range .jobs}}{{.conclusion}}{{"\n"}}{{end}}') + if echo "$jobs" | grep -qE 'failure|cancelled|timed_out'; then + keep["$run_id"]=1 + for dir in -1 1; do + neighbor=$(get_neighbor_entry "$idx" "$dir" || true) + if [[ -n "$neighbor" ]]; then + IFS='|' read -r n_id n_num n_conc n_created <<<"$neighbor" + if [[ "$n_conc" == "success" ]]; then + keep["$n_id"]=1 + fi + fi + done + fi +done + +# decide what to keep/delete +for entry in "${entries[@]}"; do + IFS='|' read -r run_id number conclusion created <<<"$entry" + if [[ -n "${keep[$run_id]:-}" ]]; then + log_info "KEEP run_id=$run_id number=$number status=$conclusion created=$created" + else + if [[ "$DRY_RUN" -eq 1 ]]; then + log_info "Would delete run $run_id ($number)" + else + log_info "Deleting run $run_id ($number)" + gh api --method DELETE -H "Accept: application/vnd.github+json" \ + "/repos/$ORG/$REPO/actions/runs/$run_id" || true + fi + fi +done diff --git a/.github/workflows/tidy-up-auto.yml b/.github/workflows/tidy-up-auto.yml index 4cb16db61a..56b9e90e16 100644 --- a/.github/workflows/tidy-up-auto.yml +++ b/.github/workflows/tidy-up-auto.yml @@ -6,12 +6,16 @@ on: branches: - master +permissions: + actions: write + contents: read + +env: + GH_TOKEN: ${{ github.token }} + jobs: - delete_old_workflow_runs: + delete_old_workflow_runs_timeout: runs-on: ubuntu-latest - permissions: - actions: write - contents: read steps: - name: delete workflow runs uses: Mattraks/delete-workflow-runs@v2 @@ -33,3 +37,109 @@ jobs: run: .github/scripts/github/gh-purge-deleted-branch-workflows.sh X11Libre/xserver env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + delete_old_workflow_runs_branches: + runs-on: ubuntu-latest + strategy: + matrix: + version: + - master + - maint-25.0 + - wip/AllocColor + - wip/ReadRequestFromClient + - wip/ReplyNotSwappd + - wip/XvPortNotifyPtr + - wip/always-pciaccess + - wip/build-rootless + - wip/ci + - wip/ci-deps + - wip/clang-tidy + - wip/cleanup + - wip/cleanup-api-xfree86 + - wip/cleanup-api-xfree86-backup + - wip/cleanup-grabs + - wip/cleanup-os-export + - wip/client_array + - wip/conflict-notify + - wip/conv-warnings + - wip/deps1 + - wip/dix-config + - wip/dixLookupWindowByXID + - wip/dixconf-1 + - wip/dixconfig-2 + - wip/dixgetproperty + - wip/fix-xnest + - wip/fix_347 + - wip/generations + - wip/geode + - wip/github-build + - wip/github-macos-deps + - wip/github-releases + - wip/glx_deferred + - wip/grouping + - wip/includes + - wip/includes-2 + - wip/intel-2 + - wip/io + - wip/log_req_len + - wip/logging + - wip/logging_submitted_1 + - wip/maint-xnest + - wip/malloc + - wip/marshal + - wip/matrix-test + - wip/meson + - wip/misc.h + - wip/mpbt + - wip/netbsd + - wip/new-cleanups + - wip/no-libsystemd + - wip/os-unexport + - wip/pciaccess + - wip/pedantic + - wip/pixmap-lifecycle + - wip/purge-master + - wip/reallocarray + - wip/redv + - wip/requests + - wip/rootless + - wip/saveset + - wip/screenkey + - wip/screenproc + - wip/screenproc-old + - wip/screenproc-wrap + - wip/screenproc-wrap-debug + - wip/sdk + - wip/swap0 + - wip/swapping + - wip/swapping_new + - wip/troubleshoot-randr + - wip/warn-conv + - wip/werror + - wip/win32_2 + - wip/xcb-marshal + - wip/xcb-visuals + - wip/xcb-xnest + - wip/xf86_platform_bus_newabi + - wip/xf86bigfont + - wip/xi-properties + - wip/xnest-multi + - wip/xnest-render + - wip/xnest-rootless + - wip/xnest-rootless-old + - wip/xnest-shm + - wip/xrandr-bug + - wip/xrandr-new-one + - wip/xv-fix + - wip/xv-generation + - wip/xwin + - wip/xwin-2 + steps: + - uses: actions/checkout@v4 + - name: purge older runs on ${{ github.event.repository.name }} branch + run: | + .github/scripts/gh-purge-pipelines.sh \ + --org ${{ github.repository_owner }} \ + --repo ${{ github.event.repository.name }} \ + --branch master \ + --keep 2