From e4cc92867ed759b241c6522824c09f1ee8e14dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bot=20of=20Thomas=20G=C3=BCttler?= Date: Sun, 7 Jun 2026 05:04:58 +0200 Subject: [PATCH] ci(website): add change detection to skip unconditional hourly deploys (#515) --- .forgejo/workflows/website.yml | 106 +++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/.forgejo/workflows/website.yml b/.forgejo/workflows/website.yml index ee5c575..ea67892 100644 --- a/.forgejo/workflows/website.yml +++ b/.forgejo/workflows/website.yml @@ -12,10 +12,116 @@ on: workflow_dispatch: jobs: + check-changes: + name: Detect Website Changes + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + has_changes: ${{ steps.diff.outputs.has_changes }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect website changes since last deploy + id: diff + shell: bash + env: + FORGEJO_TOKEN: ${{ github.token }} + run: | + # On push or workflow_dispatch always deploy + if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + HEAD_SHA=$(git rev-parse HEAD) + + # Find the most recent successful website.yml run where the deploy job + # actually ran (not merely skipped). Uses head_sha (not commit_sha which + # is always None in Forgejo's API). + LAST_DEPLOYED_SHA=$(python3 - << 'PYEOF' + import json, os, sys, urllib.request + token = os.environ.get("FORGEJO_TOKEN", "") + server = os.environ.get("GITHUB_SERVER_URL", "").rstrip("/") + repo = os.environ.get("GITHUB_REPOSITORY", "") + base_api = f"{server}/api/v1/repos/{repo}/actions" + url = f"{base_api}/runs?workflow_id=website.yml&status=success&limit=10" + req = urllib.request.Request(url, headers={"Authorization": f"token {token}"}) + try: + with urllib.request.urlopen(req) as r: + data = json.loads(r.read()) + runs = [ + r for r in data.get("workflow_runs", []) + if r.get("status") == "success" + ] + for run in runs: + run_id = run.get("id") + jobs_url = f"{base_api}/runs/{run_id}/jobs" + jobs_req = urllib.request.Request(jobs_url, headers={"Authorization": f"token {token}"}) + try: + with urllib.request.urlopen(jobs_req) as jr: + jobs_data = json.loads(jr.read()) + for job in jobs_data.get("workflow_jobs", []): + if "Build & Update Website" in job.get("name", "") and ( + job.get("conclusion") == "success" or + job.get("status") == "success" + ): + print(run.get("head_sha") or "") + sys.exit(0) + except Exception: + pass # skip this run if jobs API fails + print("") + except Exception as e: + print(f"::error::LAST_DEPLOYED_SHA lookup failed ({type(e).__name__}: {e})") + print("") + PYEOF + ) + + if [ -z "$LAST_DEPLOYED_SHA" ]; then + echo "::warning::Could not determine last successfully deployed SHA — deploying as a precaution" + echo "has_changes=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if [ "$HEAD_SHA" = "$LAST_DEPLOYED_SHA" ]; then + echo "::notice::Website deploy SKIPPED — HEAD $HEAD_SHA was already successfully deployed" + echo "has_changes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Diff from last successfully deployed commit to catch all changes since + # that deploy, not just the most recent commit. + if git cat-file -e "$LAST_DEPLOYED_SHA" 2>/dev/null; then + echo "Diffing from last deployed SHA $LAST_DEPLOYED_SHA" + CHANGED=$(git diff --name-only "$LAST_DEPLOYED_SHA" HEAD 2>/dev/null \ + || git show --name-only --format= HEAD) + else + echo "::warning::Last deployed SHA $LAST_DEPLOYED_SHA not in local history — deploying as a precaution" + echo "has_changes=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "Changed files:" + echo "$CHANGED" + + website_re='^(website/|scripts/website-verify\.sh|\.forgejo/workflows/website\.yml)' + + if echo "$CHANGED" | grep -qE "$website_re"; then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + echo "::notice::Website deploy TRIGGERED — website-relevant files changed since $LAST_DEPLOYED_SHA" + else + echo "has_changes=false" >> "$GITHUB_OUTPUT" + echo "::notice::Website deploy SKIPPED — diff $LAST_DEPLOYED_SHA..HEAD has no website-relevant changes" + fi + deploy: name: Build & Update Website runs-on: ubuntu-latest timeout-minutes: 60 + needs: [check-changes] + if: needs.check-changes.outputs.has_changes == 'true' steps: - name: Print runner wait time