d9e471cbcc
CI / Patch overlay integrity (push) Failing after 4s
CI / all-required (push) Failing after 3s
Build and Publish Patched OpenClaw / sync-upstream (pull_request) Has been skipped
CI / Patch overlay integrity (pull_request) Successful in 4s
CI / all-required (pull_request) Successful in 3s
Build and Publish Patched OpenClaw / test (pull_request) Failing after 15m55s
main branch protection requires 'CI / all-required (pull_request)' but the sole workflow was build-and-publish.yml (name 'Build and Publish Patched OpenClaw'), so that context was NEVER posted -> every PR sat permanently grey/pending. Add a hermetic, deterministic CI workflow (no upstream fetch / registry / index) that validates the patch overlay this repo owns and emits the required all-required context. The heavy networked build stays in build-and-publish.yml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
108 lines
4.4 KiB
YAML
108 lines
4.4 KiB
YAML
name: CI
|
|
|
|
# Phantom-required-check fix (CI-robustness).
|
|
#
|
|
# main branch protection requires exactly the status context
|
|
# `CI / all-required (pull_request)`
|
|
# but the only workflow in this repo was `build-and-publish.yml`
|
|
# (workflow name "Build and Publish Patched OpenClaw"), so NOTHING ever
|
|
# posted `CI / all-required` → every PR sat permanently grey/pending and
|
|
# could never satisfy branch protection. This workflow emits that exact
|
|
# context.
|
|
#
|
|
# It is deliberately HERMETIC and deterministic: it validates the patch
|
|
# OVERLAY that this repo owns (it stores only the patched files on top of
|
|
# upstream openclaw) WITHOUT any network access — no upstream clone, no
|
|
# registry, no package index — so a transient network/registry blip can
|
|
# never false-red the required merge gate. The heavy, network-dependent
|
|
# real build/publish continues to run in build-and-publish.yml (which
|
|
# already triggers on pull_request); this gate guarantees the overlay is
|
|
# structurally sound before that runs.
|
|
|
|
on: [push, pull_request]
|
|
|
|
env:
|
|
GITHUB_SERVER_URL: https://git.moleculesai.app
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
overlay-integrity:
|
|
name: Patch overlay integrity
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Validate patch overlay (hermetic — no upstream fetch)
|
|
run: |
|
|
set -euo pipefail
|
|
fail() { echo "::error::$*"; exit 1; }
|
|
|
|
# 1. PATCHED_VERSION.md must exist and pin a concrete upstream ref.
|
|
test -f PATCHED_VERSION.md || fail "PATCHED_VERSION.md missing"
|
|
UPSTREAM_REF=$(awk '/^Upstream:/ {print $2; exit}' PATCHED_VERSION.md)
|
|
test -n "$UPSTREAM_REF" || fail "PATCHED_VERSION.md has no 'Upstream: <ref>' pin"
|
|
# A 40-hex commit SHA is what build-and-publish.yml checks out; a
|
|
# floating tag/branch would make the build non-reproducible.
|
|
echo "$UPSTREAM_REF" | grep -Eq '^[0-9a-f]{40}$' \
|
|
|| fail "Upstream ref '$UPSTREAM_REF' is not a 40-hex commit SHA (build must pin a reproducible ref)"
|
|
echo "::notice::upstream pinned at $UPSTREAM_REF"
|
|
|
|
# 2. The patched source tree this repo overlays must be present and
|
|
# non-empty (build-and-publish.yml does `cp -r src/. upstream/src/`).
|
|
test -d src || fail "src/ patch tree missing"
|
|
test -n "$(find src -type f -print -quit)" || fail "src/ patch tree is empty — nothing to overlay"
|
|
|
|
# 3. Dockerfile must exist (the publish job builds it).
|
|
test -f Dockerfile || fail "Dockerfile missing"
|
|
|
|
echo "::notice::patch overlay OK ($(find src -type f | wc -l) patched file(s))"
|
|
|
|
- name: Validate workflow YAML parses (hermetic)
|
|
run: |
|
|
set -euo pipefail
|
|
python3 - <<'PY'
|
|
import glob, sys, yaml
|
|
bad = 0
|
|
for f in sorted(glob.glob(".gitea/workflows/*.yml") + glob.glob(".gitea/workflows/*.yaml")):
|
|
try:
|
|
yaml.safe_load(open(f, encoding="utf-8"))
|
|
print("ok ", f)
|
|
except Exception as e:
|
|
print(f"::error::{f}: {e}"); bad += 1
|
|
sys.exit(1 if bad else 0)
|
|
PY
|
|
|
|
# Stable aggregate required-context. Branch protection pins exactly
|
|
# `CI / all-required (pull_request)`; this job emits it and is green IFF
|
|
# every real job above succeeded. Mirrors the fleet `all-required`
|
|
# convention (molecule-external-workspace-sdk / template-langgraph / -hermes).
|
|
all-required:
|
|
name: all-required
|
|
needs: [overlay-integrity]
|
|
if: ${{ always() }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 2
|
|
steps:
|
|
- name: Assert every required job succeeded
|
|
run: |
|
|
set -euo pipefail
|
|
results='${{ toJSON(needs) }}'
|
|
echo "$results"
|
|
echo "$results" | python3 -c '
|
|
import json, sys
|
|
ns = json.load(sys.stdin)
|
|
# Green only if each needed job is success (or a legitimate skip).
|
|
# failure/cancelled/None => NOT green => hard-fail the required gate.
|
|
bad = [(k, v.get("result")) for k, v in ns.items()
|
|
if v.get("result") not in ("success", "skipped")]
|
|
if bad:
|
|
print("FAIL: required jobs not green:", file=sys.stderr)
|
|
for k, r in bad:
|
|
print(f" - {k}: {r}", file=sys.stderr)
|
|
sys.exit(1)
|
|
print(f"OK: all {len(ns)} required job(s) succeeded")
|
|
'
|