NEW: Gitea Actions CI pipeline (.gitea/workflows/pr-pipeline.yaml) - Stage 1 (validate): YAML frontmatter validation for all .prose.md files - Stage 2 (lint): Structural checks + regression detection * /grafana/ nginx route (reverted 2026-07-02) * Stale CT IDs (122→112, no 123) * Verified: .19/.122/.123 = correct bridge IPs - Stage 3 (ai-review): LiteLLM-powered diff review against ground truth - Stage 4 (auto-merge): Gate — safe to merge when all green NEW: scripts/prose-lint.sh — contract structure + regression checker NEW: scripts/prose-ai-review.sh — AI review via syslog-auto model FIXES FOUND BY LINT: - gpu-fleet.prose.md: removed /grafana/ from nginx routing diagram - gpu-fleet.prose.md: removed /grafana/ from config file description - infrastructure-control.prose.md: CT 122→112 in topology diagram - scripts/prose-lint.sh: updated to not flag .19 (verified correct IP)
84 lines
3.2 KiB
YAML
84 lines
3.2 KiB
YAML
name: PR Pipeline — Validate → Review → Merge
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened]
|
|
paths:
|
|
- '**.prose.md'
|
|
|
|
jobs:
|
|
validate:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: YAML frontmatter validation
|
|
run: |
|
|
echo "=== Prose Contract Frontmatter Validation ==="
|
|
FAILED=0
|
|
for f in $(find . -name "*.prose.md" -not -path "./.git/*" -not -path "./runs/*"); do
|
|
FM=$(sed -n '/^---$/,/^---$/p' "$f" | sed '1d;$d')
|
|
[ -z "$FM" ] && { echo " ❌ $f: No YAML frontmatter"; FAILED=1; continue; }
|
|
|
|
KIND=$(echo "$FM" | grep '^kind:' | awk '{print $2}')
|
|
case "$KIND" in
|
|
function|responsibility|gateway|pattern|test|template|architecture) echo " ✅ $f: kind=$KIND" ;;
|
|
*) echo " ❌ $f: Invalid kind='$KIND'"; FAILED=1 ;;
|
|
esac
|
|
|
|
echo "$FM" | grep -q '^name:' || { echo " ❌ $f: Missing name"; FAILED=1; }
|
|
echo "$FM" | grep -q '^description:' || { echo " ❌ $f: Missing description"; FAILED=1; }
|
|
done
|
|
[ $FAILED -eq 1 ] && { echo "❌ FRONTMATTER FAILED"; exit 1; }
|
|
echo "✅ Frontmatter validation passed"
|
|
|
|
lint:
|
|
runs-on: ubuntu-latest
|
|
needs: validate
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Structure + regression lint
|
|
run: bash scripts/prose-lint.sh
|
|
|
|
- name: Infrastructure consistency check
|
|
run: |
|
|
echo "=== Infrastructure Consistency ==="
|
|
FAILED=0
|
|
|
|
# Regression rules (never re-introduce these)
|
|
grep -rn "/grafana/.*harness-grafana\|location.*\/grafana\/" *.prose.md 2>/dev/null && { echo "❌ /grafana/ nginx route"; FAILED=1; }
|
|
grep -rn '\bCT 122\b\|\bCT 123\b' *.prose.md 2>/dev/null && { echo "❌ Stale CT 122/123"; FAILED=1; }
|
|
grep -rn '\.19.*Zulip\|Zulip.*\.19\|192\.168\.68\.19' *.prose.md 2>/dev/null && { echo "❌ Stale .19 IP"; FAILED=1; }
|
|
STRIX=$(grep -rn "192\.168\.68\.15:8080\|\.15:8080\b" *.prose.md 2>/dev/null | grep -v "firewalled\|firewall\|:116 only" || true)
|
|
[ -n "$STRIX" ] && { echo "❌ Strix :8080 without firewall note: $STRIX"; FAILED=1; }
|
|
|
|
[ $FAILED -eq 0 ] && echo "✅ Consistency check passed" || { echo "❌ FAILED"; exit 1; }
|
|
echo "✅ Consistency OK"
|
|
|
|
ai-review:
|
|
runs-on: ubuntu-latest
|
|
needs: validate
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: AI-powered contract review
|
|
env:
|
|
LITELLM_URL: ${{ secrets.LITELLM_URL }}
|
|
LITELLM_KEY: ${{ secrets.LITELLM_KEY }}
|
|
run: bash scripts/prose-ai-review.sh
|
|
|
|
auto-merge:
|
|
runs-on: ubuntu-latest
|
|
needs: [validate, lint, ai-review]
|
|
if: success()
|
|
steps:
|
|
- name: Merge PR
|
|
run: |
|
|
echo "All checks passed. Merging..."
|
|
# Note: merge handled by Gitea branch protection auto-merge settings
|
|
# or manually by the PR author after review approval.
|
|
# This job serves as a gate — when all checks are green, merge is safe.
|
|
echo "✅ PR is safe to merge. Awaiting branch protection or manual merge."
|