From b6214f22a3401b41bde30d0a349c282af4a45489 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Jun 2026 14:59:07 +0800 Subject: [PATCH] fix(cd): release deploy jobs with in-job guards --- .gitea/workflows/cd.yaml | 74 ++++++++++++++++++++++++++++++++++++++++ apps/api/.cd-trigger | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index d50294a56..db8b48b0b 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -312,6 +312,11 @@ jobs: # 2026-04-30 Codex: Docker builds run on the host runner. Long docker build # steps were killing the transient act job container with RWLayer=nil. needs: tests + # 2026-06-27 Codex: Gitea 1.25 can leave a dependent job blocked on the + # implicit success() condition even after the upstream job is green. Keep + # the dependency ordering, but make the job schedulable and fail closed in + # the first step if the tests job is not actually green. + if: ${{ always() }} timeout-minutes: 60 runs-on: awoooi-host steps: @@ -325,6 +330,39 @@ jobs: - uses: actions/checkout@v4 + - name: Assert Tests Job Succeeded + run: | + python3 - <<'PY' + import os + import re + import sys + import time + import urllib.request + + sha = os.environ["GITHUB_SHA"] + url = f"http://192.168.0.110:3001/wooo/awoooi/commit/{sha}" + + def page_text() -> str: + html = urllib.request.urlopen(url, timeout=20).read().decode("utf-8", "replace") + text = re.sub(r"<[^>]+>", " ", html) + return re.sub(r"\s+", " ", text) + + for _ in range(20): + text = page_text() + match = re.search(r"CD Pipeline / tests \(push\) ([^D]{0,120}) Details", text) + status = match.group(1).strip() if match else "unknown" + print(f"tests job status: {status}") + if "Successful" in status: + sys.exit(0) + if any(token in status for token in ("Failure", "Cancelled", "Timed out")): + print("tests job did not pass; refusing to deploy", file=sys.stderr) + sys.exit(1) + time.sleep(15) + + print("tests job status did not become successful before deploy guard timeout", file=sys.stderr) + sys.exit(1) + PY + - name: Get Commit Info id: commit run: | @@ -1218,6 +1256,9 @@ jobs: post-deploy-checks: needs: build-and-deploy + # Same Gitea dependency workaround as build-and-deploy: schedule the job, + # then fail closed if build-and-deploy did not really finish successfully. + if: ${{ always() }} timeout-minutes: 30 # 2026-04-30 Codex: keep post-deploy on the host runner too. Playwright # install-deps can also kill the act-managed job container with RWLayer=nil. @@ -1233,6 +1274,39 @@ jobs: - uses: actions/checkout@v4 + - name: Assert Build Deploy Job Succeeded + run: | + python3 - <<'PY' + import os + import re + import sys + import time + import urllib.request + + sha = os.environ["GITHUB_SHA"] + url = f"http://192.168.0.110:3001/wooo/awoooi/commit/{sha}" + + def page_text() -> str: + html = urllib.request.urlopen(url, timeout=20).read().decode("utf-8", "replace") + text = re.sub(r"<[^>]+>", " ", html) + return re.sub(r"\s+", " ", text) + + for _ in range(20): + text = page_text() + match = re.search(r"CD Pipeline / build-and-deploy \(push\) ([^D]{0,120}) Details", text) + status = match.group(1).strip() if match else "unknown" + print(f"build-and-deploy job status: {status}") + if "Successful" in status: + sys.exit(0) + if any(token in status for token in ("Failure", "Cancelled", "Timed out")): + print("build-and-deploy job did not pass; refusing post-deploy checks", file=sys.stderr) + sys.exit(1) + time.sleep(15) + + print("build-and-deploy job status did not become successful before post-deploy guard timeout", file=sys.stderr) + sys.exit(1) + PY + - name: Get Commit Info id: commit run: | diff --git a/apps/api/.cd-trigger b/apps/api/.cd-trigger index a6f4b5097..da8f1a647 100644 --- a/apps/api/.cd-trigger +++ b/apps/api/.cd-trigger @@ -1 +1 @@ -# 2026-06-27 retry AI automation closure deploy without dependent-job override +# 2026-06-27 retry AI automation closure deploy with in-job status guards