171 lines
8.0 KiB
YAML
171 lines
8.0 KiB
YAML
# .gitea/workflows/merge-gate.yaml
|
|
# CI merge gate (T-0.2, R-GITOPS-01, R-TEST-02/03) for the home k3s + Longhorn
|
|
# platform. Enforces docs/14 (Testing & Conformance) gating rules as Gitea Actions.
|
|
#
|
|
# Pipeline (docs/14 "Where each layer runs"):
|
|
# lint -> schema -> kyverno-L0 -> render==golden -> L1-render [no cluster; live now]
|
|
# |
|
|
# v
|
|
# conformance-L2-L3 (in-cluster Chainsaw Job)
|
|
# | [cluster-gated; SKIPS in Phase 0]
|
|
# v
|
|
# merge-gate decision [R-TEST-03: touched subset green]
|
|
#
|
|
# STATUS (corrected 2026-09-29): Gitea IS hosted and Gitea Actions IS enabled; the runner
|
|
# `incluster-dind` is online and gate.yml is the live PR gate. The text that used to sit here
|
|
# ("Today there is no Gitea host, so this file is dormant") was stale by weeks -- it was written
|
|
# as a plan and never revisited once the plan completed.
|
|
#
|
|
# THIS file is still dormant, but for the reason in the DORMANT block below -- its spec checkout
|
|
# and L2/L3 seams are unwired -- NOT because there is no host. Do not read a stale plan as a
|
|
# statement about the live platform; query the API instead (see infra/gitea/).
|
|
#
|
|
# CI holds NO broad kubeconfig. L2/L3 run as an in-cluster Chainsaw Job whose own
|
|
# scoped ServiceAccount holds cluster access (docs/14, R-SEC-02); CI uses only a
|
|
# narrow trigger credential referenced BY SECRET NAME below.
|
|
#
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# DORMANT (Phase 6, WS-GITOSS): the LIVE enforced gate is now gate.yml. This file is
|
|
# retained as the full-pipeline BLUEPRINT (spec-checkout via SPEC_REPO_SLUG + the L2/L3
|
|
# conformance job + the R-TEST-03 decision job) for when those seams are wired:
|
|
# - SPEC_REPO_SLUG must be set as a Gitea repo/org variable AND the spec repo hosted on
|
|
# this Gitea, or spec-immutability's second checkout fails the whole run.
|
|
# - vars.CLUSTER_AVAILABLE + a scoped conformance trigger credential must exist.
|
|
# Until then it must NOT auto-run (its spec checkout would fail with SPEC_REPO_SLUG unset),
|
|
# so its triggers are reduced to manual `workflow_dispatch`. Re-enable by restoring the
|
|
# pull_request/push triggers once the seams above are wired.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
---
|
|
name: merge-gate
|
|
|
|
on:
|
|
workflow_dispatch: {} # DORMANT blueprint — manual-only; gate.yml is the live gate
|
|
|
|
concurrency:
|
|
group: merge-gate-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# Repo/org variables & secrets (set in Gitea, referenced by NAME only — no plaintext):
|
|
# vars.CLUSTER_AVAILABLE 'true' once a dev/test cluster exists (Phase 1+); else L2/L3 SKIP
|
|
# secrets.CONFORMANCE_TRIGGER_KUBECONFIG narrow creds to CREATE/AWAIT the in-cluster Chainsaw Job only
|
|
env:
|
|
CLUSTER_AVAILABLE: ${{ vars.CLUSTER_AVAILABLE }}
|
|
|
|
jobs:
|
|
# R-TEST-06 hard gate: the specification is read-only to agents. Fail the whole run
|
|
# if the spec repo is modified or has drifted from ci/SPEC_PIN. Runs first; everything
|
|
# else depends on it. Requires the spec repo checked out beside the impl repo — wired
|
|
# for when Gitea hosts both. If the spec repo is unavailable the guard SKIPS (exit 3),
|
|
# tolerated here (surfaced, never a fake green); a real violation (exit 1) blocks.
|
|
spec-immutability:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 # the implementation repo
|
|
- name: Check out the read-only spec (sibling path)
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with:
|
|
repository: ${{ vars.SPEC_REPO_SLUG }} # e.g. Home-Network/home-network-k3s (set in Gitea)
|
|
path: ../home-network-k3s
|
|
fetch-depth: 0
|
|
- name: Verify spec immutability (R-TEST-06)
|
|
run: make -C ci spec-immutability || [ $? -eq 3 ]
|
|
|
|
lint:
|
|
runs-on: ubuntu-latest
|
|
needs: [spec-immutability]
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with: { fetch-depth: 0 } # full history so touched-requirement diff works
|
|
- name: Install pinned tools
|
|
run: ./.gitea/workflows/scripts/install-tools.sh lint
|
|
- name: yaml/lint
|
|
run: make -C ci lint
|
|
|
|
l0:
|
|
runs-on: ubuntu-latest
|
|
needs: [lint]
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with: { fetch-depth: 0 }
|
|
- name: Install pinned tools
|
|
run: ./.gitea/workflows/scripts/install-tools.sh l0
|
|
- name: Schema validation + Kyverno L0
|
|
run: make -C ci l0
|
|
|
|
render-golden:
|
|
runs-on: ubuntu-latest
|
|
needs: [l0]
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with: { fetch-depth: 0 }
|
|
- name: Install pinned tools
|
|
run: ./.gitea/workflows/scripts/install-tools.sh l1
|
|
- name: render==golden reproducibility (canonical render vs committed apps/)
|
|
run: make -C ci render-golden
|
|
|
|
l1:
|
|
runs-on: ubuntu-latest
|
|
needs: [render-golden]
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with: { fetch-depth: 0 }
|
|
- name: Install pinned tools
|
|
run: ./.gitea/workflows/scripts/install-tools.sh l1
|
|
- name: L1 rendering checks
|
|
run: make -C ci l1
|
|
- name: Upload L1 artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: l1-junit-coverage
|
|
path: |
|
|
results/junit/*.xml
|
|
reports/coverage/**
|
|
if-no-files-found: ignore
|
|
|
|
# L2/L3 behavioral + fault-injection. Runs ONLY when a cluster is declared.
|
|
# In Phase 0 (CLUSTER_AVAILABLE unset) this job is SKIPPED by Gitea — shown as
|
|
# skipped, never as passed. It never fakes green (HARD RULE 4).
|
|
conformance:
|
|
runs-on: ubuntu-latest
|
|
needs: [l1]
|
|
if: ${{ vars.CLUSTER_AVAILABLE == 'true' }}
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with: { fetch-depth: 0 }
|
|
- name: Install pinned tools
|
|
run: ./.gitea/workflows/scripts/install-tools.sh conformance
|
|
- name: Trigger in-cluster Chainsaw Job (L2/L3) and collect artifacts
|
|
env:
|
|
# Narrow trigger credential; the Job's scoped SA holds real cluster access.
|
|
CONFORMANCE_TRIGGER_KUBECONFIG: ${{ secrets.CONFORMANCE_TRIGGER_KUBECONFIG }}
|
|
run: make -C ci conformance
|
|
- name: Upload L2/L3 artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: conformance-junit-coverage
|
|
path: |
|
|
results/junit/*.xml
|
|
reports/coverage/**
|
|
if-no-files-found: ignore
|
|
|
|
# Final decision. Applies R-TEST-03 honestly over the upstream job results:
|
|
# any L0/L1 failure -> BLOCK
|
|
# conformance failed -> BLOCK
|
|
# conformance skipped + cluster available -> BLOCK (should have run)
|
|
# conformance skipped + no cluster -> report UNVERIFIED (Phase 0), do not fake green
|
|
merge-gate:
|
|
runs-on: ubuntu-latest
|
|
needs: [lint, l0, render-golden, l1, conformance]
|
|
if: always()
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
- name: Decide
|
|
env:
|
|
R_LINT: ${{ needs.lint.result }}
|
|
R_L0: ${{ needs.l0.result }}
|
|
R_L1: ${{ needs.l1.result }}
|
|
R_CONF: ${{ needs.conformance.result }}
|
|
run: ./.gitea/workflows/scripts/decide.sh
|