From 375bcc43762c038be0faf26b7766f43940e44117 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Thu, 30 Apr 2026 23:01:01 -0700 Subject: [PATCH] ci(validate-workspace-template): add Template validation aggregator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow was refactored from one `validate` job (display name "Template validation") into matrix-named validate-static + validate-runtime jobs ("(static)" / "(runtime)" suffixes) for fork-PR security. The new check names — `validate / Template validation (static)` and `validate / Template validation (runtime)` — never match the original `validate / Template validation` that template-repo branch protection requires. Result: auto-merge silently hangs in BLOCKED forever on every template repo because the required check never reports. Add a third aggregator job `template-validation` (display name "Template validation") that depends on both real jobs and emits the original check name. `if: always()` so it reports out even when validate-static fails — without that GitHub marks the aggregator SKIPPED and branch protection still blocks because the required check never reaches a final state. Treats `skipped` as pass for validate-runtime so fork PRs (where runtime is intentionally skipped on the security gate) don't become un-mergeable. Caught while shipping the boot-smoke fixes for openclaw#11 and hermes#29 — both PRs sat BLOCKED with all real checks green. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../workflows/validate-workspace-template.yml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/workflows/validate-workspace-template.yml b/.github/workflows/validate-workspace-template.yml index 0dba614..981c91f 100644 --- a/.github/workflows/validate-workspace-template.yml +++ b/.github/workflows/validate-workspace-template.yml @@ -164,3 +164,47 @@ jobs: - name: Docker build smoke test if: hashFiles('Dockerfile') != '' run: docker build -t template-test . --no-cache 2>&1 | tail -5 && echo "✓ Docker build succeeded" + + # Aggregator that emits a single `Template validation` check name — + # the caller's job (`validate:` in each template's ci.yml) plus this + # job's name produces `validate / Template validation`, which is what + # template-repo branch protection has historically required. + # + # Why it's needed: the workflow was refactored from one job into + # validate-static + validate-runtime (with matrix-suffixed display + # names) for fork-PR security. The matrix names never match the + # original required-check name, so PR auto-merge silently hung in + # BLOCKED forever on every template repo (caught while shipping + # fixes for the boot-smoke gate, openclaw#11 + hermes#29). + # + # `if: always()` so it reports out even when validate-static fails — + # without that, GitHub marks the aggregator as SKIPPED and branch + # protection still blocks because the required check never reports + # a final state. + # + # Fork-PR semantics: validate-runtime is intentionally skipped on + # fork PRs (security gate). Treat `skipped` as a pass for the + # aggregator on forks so static-only coverage doesn't make every + # external PR un-mergeable. + template-validation: + name: Template validation + runs-on: ubuntu-latest + needs: [validate-static, validate-runtime] + if: always() + timeout-minutes: 1 + steps: + - name: Aggregate + run: | + static="${{ needs.validate-static.result }}" + runtime="${{ needs.validate-runtime.result }}" + echo "validate-static: $static" + echo "validate-runtime: $runtime" + if [ "$static" != "success" ]; then + echo "::error::validate-static did not succeed: $static" + exit 1 + fi + if [ "$runtime" != "success" ] && [ "$runtime" != "skipped" ]; then + echo "::error::validate-runtime did not succeed: $runtime" + exit 1 + fi + echo "::notice::Template validation aggregate passed (static=$static, runtime=$runtime)"