#!/usr/bin/env bash set -euo pipefail # 2026-05-05 ogt + Codex # Find or stop stale Gitea/act job containers on the 110 host. # # Default is dry-run: # bash scripts/ops/stop-stale-gitea-actions-jobs.sh # # Apply after reviewing candidates: # bash scripts/ops/stop-stale-gitea-actions-jobs.sh --apply # # Safety rules: # - Only touches Docker containers named GITEA-ACTIONS-* or repo-scoped # Gitea job containers such as awoooi-cd-5901-1-e2e-smoke. # - Defaults to containers older than 20 minutes. # - Known long-running workflows get a higher stop threshold than the alert threshold. # - Skips containers with recent log output unless --force is provided. MIN_AGE_SECONDS=1200 APPLY=0 FORCE=0 threshold_for_name() { local name="$1" case "$name" in *WORKFLOW-CD-Pipeline_JOB-deploy*|*-cd-*-deploy*) # .gitea/workflows/cd.yaml deploy job timeout is 60m. Give act/Gitea # cleanup a buffer before treating the container as abandoned. echo 4500 ;; *WORKFLOW-CD-Pipeline_JOB-tests*|*WORKFLOW-CD-Pipeline_JOB-post-deploy-checks*|*-cd-*-tests*|*-cd-*-post-deploy*) echo 2400 ;; *WORKFLOW-Code-Review_JOB-ai-code-review*|*-code-review-*) echo 720 ;; *-cd-*-e2e-smoke*|*-cd-*-source-link-smoke*) echo 900 ;; *WORKFLOW-Deploy-Alert-Rules_JOB-deploy-alerts*) echo 900 ;; *) echo "$MIN_AGE_SECONDS" ;; esac } while [[ $# -gt 0 ]]; do case "$1" in --apply) APPLY=1 shift ;; --force) FORCE=1 shift ;; --min-age-seconds) MIN_AGE_SECONDS="${2:?--min-age-seconds requires a value}" shift 2 ;; *) echo "Unknown argument: $1" >&2 exit 2 ;; esac done now="$(date +%s)" found=0 while read -r name; do [[ -n "$name" ]] || continue started_raw="$(docker inspect "$name" --format '{{.State.StartedAt}}')" started="$(date -u -d "$started_raw" +%s 2>/dev/null || echo 0)" age=$((now - started)) stop_threshold="$(threshold_for_name "$name")" [[ "$stop_threshold" -ge "$MIN_AGE_SECONDS" ]] || stop_threshold="$MIN_AGE_SECONDS" [[ "$age" -ge "$stop_threshold" ]] || continue found=1 log_tail="$(docker logs --since 5m --tail 5 "$name" 2>&1 || true)" has_recent_logs=0 if [[ -n "$log_tail" ]]; then has_recent_logs=1 fi printf 'candidate name=%s age_seconds=%s stop_threshold_seconds=%s recent_logs=%s\n' \ "$name" "$age" "$stop_threshold" "$has_recent_logs" if [[ "$has_recent_logs" == "1" ]]; then printf '%s\n' "$log_tail" | sed 's/^/ log: /' fi if [[ "$APPLY" == "1" ]]; then if [[ "$has_recent_logs" == "1" && "$FORCE" != "1" ]]; then echo "skip $name: recent logs exist; use --force only after manual review" continue fi docker stop "$name" fi done < <(docker ps --format '{{.Names}}' | grep -E '^(GITEA-ACTIONS-|[A-Za-z0-9][A-Za-z0-9_.-]*-(cd|code-review)-[0-9]+-)' || true) if [[ "$found" == "0" ]]; then echo "No stale Gitea Actions containers older than policy threshold (minimum ${MIN_AGE_SECONDS}s)." fi