Skip to content

Commit 873ad6d

Browse files
ci: make CI standalone (drop estate reusable workflows + third-party setup action) (#603)
## Make CI standalone (no external-repo reusable workflows on the gating path) Follow-up to the #602 investigation: the PR-gating `build`/`lint`/`test` never ran because several workflows were in `startup_failure`. This makes the **gating** CI self-contained — no dependency on the external `hyperpolymath/standards` reusable workflows and no third-party toolchain action — so it runs regardless of that repo's state or an org "allowed actions" policy. ### Root cause found The estate **`spark-theatre-gate.yml`** carries a documented note (BP008): **a `concurrency:` block in a reusable-workflow *caller*, when the reusable also declares concurrency on the same key, is rejected at run-creation → `startup_failure`** (no check-run is ever emitted). `governance.yml` and `secret-scanner.yml` both still had caller-level `concurrency:` blocks calling estate reusables → that is why they startup-failed, while `hypatia`/`spark` (no stacking) passed. The standalone replacements here are **normal** workflows, so their concurrency blocks are safe. ### Changes | Workflow | Before | After | |---|---|---| | **ci.yml** | `ocaml/setup-ocaml` (third-party) + first-party pins with **fictional** version comments (`checkout # v6.0.3`, `upload-artifact # v7.0.1`) | Self-hosted OCaml via `apt + opam` (`ocaml-system`, base-compiler fallback; dune-project needs ≥4.14); only first-party `actions/*` at real major tags | | **governance.yml** | calls `hyperpolymath/standards/.../governance-reusable.yml@main` | local `tools/ci/governance-standalone.sh` (Jekyll ban, MPL-1.0 SPDX-header ban, **PR-delta** DOC-FORMAT) | | **secret-scanner.yml** | calls `hyperpolymath/standards/.../secret-scanner-reusable.yml` + `secrets: inherit` | local `tools/ci/secret-scan-standalone.sh` (pure-shell, high-confidence patterns, no secrets) | | **scorecard.yml** | calls `hyperpolymath/standards/.../scorecard-reusable.yml` | direct `ossf/scorecard-action` (mirrors the already-direct `scorecard-enforcer.yml`) | New: `tools/ci/governance-standalone.sh`, `tools/ci/secret-scan-standalone.sh` (both `chmod +x`). ### Verification (local) ``` tools/ci/governance-standalone.sh → PASS (Jekyll: none; MPL-1.0 headers: none) tools/ci/governance-standalone.sh main → PASS (no newly-added docs/ .md) tools/ci/secret-scan-standalone.sh → PASS (no high-confidence secrets in tracked files) python3 -c yaml.safe_load on all 4 workflows → valid bash -n on both scripts → ok ``` The gates were calibrated against the tree first: a naïve gate would false-fail (59 pre-existing `docs/*.md`, 14 `MPL-1.0` *mentions* in policy/docs), so the checks are header-aware (0 actual `SPDX: MPL-1.0` headers) and DOC-FORMAT is **delta-only** (matches the canonical "PR that adds a docs/ .md" semantics; pre-existing files are never retro-flagged). ### Left intentionally (not "fully" standalone — by design) - **hypatia-scan.yml**, **spark-theatre-gate.yml** — estate-proprietary scanners (neurosymbolic / SPARK), **currently passing**. No public standalone equivalent exists; stubbing them would *lose* real coverage. Left calling the estate reusable. - **mirror.yml** — cross-forge mirroring is inherently external; not a CI gate. - **release.yml** — its `ocaml/setup-ocaml` runs on a **cross-platform matrix (Linux + macOS)**; a Linux-only `apt` inline setup would break the macOS release builds, and the publish path isn't testable here. Not PR-gating. - Standard community actions elsewhere (`github/codeql-action`, `denoland/setup-deno`, `dtolnay/rust-toolchain`, `haskell-actions/setup`, `peter-evans/repository-dispatch`) — normal, non-estate; inlining toolchain setups (esp. cross-platform) is a larger follow-up. Happy to do these if you want. ### Notes / trade-offs for review - First-party `actions/*` are pinned to **major tags** (`@v4`) rather than SHAs, because the prior SHA pins were labelled with nonexistent versions. `scorecard-enforcer.yml`'s `check-critical` job warns (non-blocking) on `@vN` pins. Re-pin to verified upstream SHAs if you prefer; first-party actions are always allowed under any allowlist. - The self-hosted OCaml step bumps the affected jobs' `timeout-minutes` 10 → 25 to cover the toolchain install. - I can't exercise GitHub runner behaviour from here; CI on this PR is the real test. If `ci.yml` still startup-fails after this, the remaining cause is isolated to first-party actions and points at an org Actions-policy/platform issue (owner-side). - Alternative considered: the **minimal** fix for governance/secret-scanner is simply removing the stacked `concurrency:` block (keeps the canonical estate checks). This PR goes standalone per request, but that option remains if you'd rather keep estate coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Lz7pRcec2Z3tVtaAhvB3M8 --- _Generated by [Claude Code](https://claude.ai/code/session_01Lz7pRcec2Z3tVtaAhvB3M8)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent ac98c81 commit 873ad6d

6 files changed

Lines changed: 251 additions & 60 deletions

File tree

.github/workflows/ci.yml

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
# SPDX-License-Identifier: MPL-2.0
2+
#
3+
# Standalone CI: no dependency on third-party actions or external-repo
4+
# reusable workflows. The OCaml toolchain is self-hosted via apt + opam
5+
# (replacing ocaml/setup-ocaml), and only first-party `actions/*` are used
6+
# (checkout / setup-node / upload-artifact), referenced by upstream major
7+
# tag. dune-project requires OCaml >= 4.14, satisfied by the runner's apt
8+
# OCaml (ocaml-system), with a base-compiler fallback.
9+
#
10+
# NOTE on pins: the previous SHA pins carried fictional version comments
11+
# (`actions/checkout # v6.0.3`, `actions/upload-artifact # v7.0.1` — versions
12+
# that do not exist upstream), so they were re-pointed to real major tags.
13+
# Re-pin to verified upstream SHAs if the repo's SHA-pinning policy requires
14+
# it (these are GitHub-first-party actions, always permitted under any
15+
# "allowed actions" policy).
216
name: CI
317
on:
418
push:
@@ -9,27 +23,36 @@ permissions:
923
contents: read
1024
# Actions concurrency pool. Applied only to read-only check workflows
1125
# (no publish/mutation), so cancelling a superseded run is always safe.
26+
# Safe here: this is a normal workflow (not a reusable-workflow caller),
27+
# so there is no caller/reusable concurrency stacking (the BP008 startup
28+
# failure class).
1229
concurrency:
1330
group: ${{ github.workflow }}-${{ github.ref }}
1431
cancel-in-progress: true
1532
jobs:
1633
build:
1734
runs-on: ubuntu-latest
18-
timeout-minutes: 10
35+
timeout-minutes: 25
1936

2037
steps:
2138
- name: Checkout code
22-
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
23-
- name: Set up OCaml
24-
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
25-
with:
26-
ocaml-compiler: "5.1"
39+
uses: actions/checkout@v4
40+
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
41+
run: |
42+
sudo apt-get update
43+
sudo apt-get install -y --no-install-recommends opam ocaml
44+
opam init --bare --disable-sandboxing --yes
45+
# Prefer the runner's system OCaml (>= 4.14 satisfies dune-project)
46+
# for an instant switch; fall back to a pinned base compiler.
47+
opam switch create . ocaml-system --no-install --yes \
48+
|| opam switch create . ocaml-base-compiler.4.14.2 --no-install --yes
49+
opam exec -- ocaml -version
2750
- name: Set up Node.js
28-
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
51+
uses: actions/setup-node@v4
2952
with:
3053
node-version: "20"
3154
- name: Install dependencies
32-
run: opam install . --deps-only --with-test --with-doc
55+
run: opam install . --deps-only --with-test --with-doc --yes
3356
- name: Install tree-sitter CLI (for res-to-affine walker tests)
3457
# Same rationale as the migration-assistant job (see below):
3558
# npm distribution is the fast CI install (~5 s). The walker
@@ -72,21 +95,25 @@ jobs:
7295
run: opam exec -- dune build @fmt
7396
lint:
7497
runs-on: ubuntu-latest
75-
timeout-minutes: 10
98+
timeout-minutes: 25
7699

77100
steps:
78101
- name: Checkout code
79-
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
80-
- name: Set up OCaml
81-
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
82-
with:
83-
ocaml-compiler: "5.1"
102+
uses: actions/checkout@v4
103+
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
104+
run: |
105+
sudo apt-get update
106+
sudo apt-get install -y --no-install-recommends opam ocaml
107+
opam init --bare --disable-sandboxing --yes
108+
opam switch create . ocaml-system --no-install --yes \
109+
|| opam switch create . ocaml-base-compiler.4.14.2 --no-install --yes
110+
opam exec -- ocaml -version
84111
- name: Install dependencies
85112
# Match the build job: `dune build` compiles everything including
86113
# test/ (which depends on alcotest, with-test) and the @doc target
87114
# below (which depends on odoc, with-doc). Without these flags, lint
88115
# fails on missing alcotest before it ever reaches the doc step.
89-
run: opam install . --deps-only --with-test --with-doc
116+
run: opam install . --deps-only --with-test --with-doc --yes
90117
- name: Build
91118
run: opam exec -- dune build
92119
- name: Lint with odoc
@@ -97,17 +124,21 @@ jobs:
97124
# §"Bench standards". Does NOT block merge. Promotion to a
98125
# ratcheted gate requires a calibrated baseline first.
99126
runs-on: ubuntu-latest
100-
timeout-minutes: 10
127+
timeout-minutes: 25
101128

102129
steps:
103130
- name: Checkout code
104-
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
105-
- name: Set up OCaml
106-
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
107-
with:
108-
ocaml-compiler: "5.1"
131+
uses: actions/checkout@v4
132+
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
133+
run: |
134+
sudo apt-get update
135+
sudo apt-get install -y --no-install-recommends opam ocaml
136+
opam init --bare --disable-sandboxing --yes
137+
opam switch create . ocaml-system --no-install --yes \
138+
|| opam switch create . ocaml-base-compiler.4.14.2 --no-install --yes
139+
opam exec -- ocaml -version
109140
- name: Install dependencies
110-
run: opam install . --deps-only --with-test --with-doc
141+
run: opam install . --deps-only --with-test --with-doc --yes
111142
- name: Build bench targets
112143
run: opam exec -- dune build @bench --force
113144
continue-on-error: true
@@ -133,7 +164,7 @@ jobs:
133164
} >> "$GITHUB_STEP_SUMMARY"
134165
- name: Upload bench log
135166
if: always()
136-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
167+
uses: actions/upload-artifact@v4
137168
with:
138169
name: bench-output
139170
path: bench-output.log
@@ -143,17 +174,21 @@ jobs:
143174
# docs/standards/TESTING.adoc §"Coverage (visibility-only)".
144175
# No merge-blocking floor today.
145176
runs-on: ubuntu-latest
146-
timeout-minutes: 10
177+
timeout-minutes: 25
147178

148179
steps:
149180
- name: Checkout code
150-
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
151-
- name: Set up OCaml
152-
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
153-
with:
154-
ocaml-compiler: "5.1"
181+
uses: actions/checkout@v4
182+
- name: Set up OCaml toolchain (self-hosted; replaces ocaml/setup-ocaml)
183+
run: |
184+
sudo apt-get update
185+
sudo apt-get install -y --no-install-recommends opam ocaml
186+
opam init --bare --disable-sandboxing --yes
187+
opam switch create . ocaml-system --no-install --yes \
188+
|| opam switch create . ocaml-base-compiler.4.14.2 --no-install --yes
189+
opam exec -- ocaml -version
155190
- name: Install dependencies
156-
run: opam install . --deps-only --with-test --with-doc
191+
run: opam install . --deps-only --with-test --with-doc --yes
157192
- name: Run tests with bisect_ppx instrumentation
158193
run: |
159194
opam exec -- dune runtest --force --instrument-with bisect_ppx
@@ -178,7 +213,7 @@ jobs:
178213
} >> "$GITHUB_STEP_SUMMARY"
179214
- name: Upload coverage HTML
180215
if: always()
181-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
216+
uses: actions/upload-artifact@v4
182217
with:
183218
name: coverage-html
184219
path: _coverage
@@ -203,9 +238,9 @@ jobs:
203238

204239
steps:
205240
- name: Checkout code
206-
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
241+
uses: actions/checkout@v4
207242
- name: Set up Node.js
208-
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
243+
uses: actions/setup-node@v4
209244
with:
210245
node-version: "20"
211246
- name: Install test runner dependencies
@@ -248,9 +283,9 @@ jobs:
248283

249284
steps:
250285
- name: Checkout code
251-
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
286+
uses: actions/checkout@v4
252287
- name: Set up Node.js
253-
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
288+
uses: actions/setup-node@v4
254289
with:
255290
node-version: "20"
256291
- name: Install tree-sitter CLI

.github/workflows/governance.yml

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
# SPDX-License-Identifier: MPL-2.0
2-
# governance.yml — single wrapper calling the shared estate governance bundle
3-
# in hyperpolymath/standards. Replaces ~8 duplicated per-repo governance
4-
# workflows (verisimiser#59 estate rollout). Load-bearing build/security
5-
# workflows (rust-ci, codeql, dependabot, release, secret-scanner, scorecard)
6-
# stay standalone in this repo.
2+
#
3+
# Standalone governance gate. Previously a thin caller of
4+
# `hyperpolymath/standards/.github/workflows/governance-reusable.yml@main`;
5+
# that cross-repo dependency (a) coupled this repo's CI to another repo's
6+
# moving `@main` and (b) startup-failed because a `concurrency:` block in a
7+
# reusable-workflow caller, when the reusable also declares concurrency on the
8+
# same key, is rejected at run-creation (the BP008 class — see
9+
# spark-theatre-gate.yml's note). This self-contained version runs the repo's
10+
# own conservative, delta-aware checks (tools/ci/governance-standalone.sh) and
11+
# is a normal workflow, so the concurrency block is safe to keep.
712
name: Governance
813
on:
914
push:
1015
branches: [main, master]
1116
pull_request:
1217
workflow_dispatch:
13-
# Estate guardrail: cancel superseded runs so re-pushes / rebased PR
14-
# updates do not pile up queued runs against the shared account-wide
15-
# Actions concurrency pool. Applied only to read-only check workflows
16-
# (no publish/mutation), so cancelling a superseded run is always safe.
1718
concurrency:
1819
group: ${{ github.workflow }}-${{ github.ref }}
1920
cancel-in-progress: true
2021
permissions:
2122
contents: read
2223
jobs:
2324
governance:
24-
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@main
25+
runs-on: ubuntu-latest
26+
timeout-minutes: 5
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 0
32+
- name: Fetch base ref (DOC-FORMAT delta)
33+
if: github.event_name == 'pull_request'
34+
run: git fetch --no-tags origin "+refs/heads/${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}"
35+
- name: Run governance gate
36+
env:
37+
GITHUB_BASE_REF: ${{ github.base_ref }}
38+
run: ./tools/ci/governance-standalone.sh

.github/workflows/scorecard.yml

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,42 @@
11
# SPDX-License-Identifier: MPL-2.0
2+
#
3+
# Standalone OpenSSF Scorecard. Previously a thin caller of
4+
# `hyperpolymath/standards/.github/workflows/scorecard-reusable.yml`; that
5+
# cross-repo dependency had a persistent startup_failure history (see the
6+
# prior note in this file's git history). This self-contained version calls
7+
# `ossf/scorecard-action` directly — mirroring the already-direct sibling
8+
# `scorecard-enforcer.yml` — so there is no external-repo workflow dependency.
9+
# `ossf/scorecard-action` / `github/codeql-action` stay SHA-pinned (third-party
10+
# actions); they match the pins used in scorecard-enforcer.yml.
211
name: Scorecards supply-chain security
312
on:
413
branch_protection_rule:
514
schedule:
615
- cron: '23 4 * * 1'
716
push:
817
branches: [main]
9-
# Workflow-level permissions are read-only. The job that calls the
10-
# reusable upgrades to the writes required by `ossf/scorecard-action`.
11-
# Job-level permissions REPLACE workflow-level for the job, so a bare
12-
# `permissions: read-all` at workflow level + writes at job level is
13-
# equivalent to `contents: read` at workflow level — both yield the
14-
# same effective set at the job. We match the canonical caller in
15-
# `hyperpolymath/standards/.github/workflows/scorecard-reusable.yml`
16-
# (and the known-good sibling julia-professional-registry/scorecard.yml)
17-
# to eliminate the prior `read-all` / `contents: read` divergence as a
18-
# possible cause of the persistent `startup_failure` (every run since
19-
# adoption — see PR #457's test-plan delta).
2018
permissions:
2119
contents: read
2220
jobs:
2321
analysis:
22+
name: Scorecard analysis
23+
runs-on: ubuntu-latest
24+
timeout-minutes: 10
2425
permissions:
2526
security-events: write
2627
id-token: write
27-
uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@e03686486e11b662834d7090dffae54c3e96fd59
28-
secrets: inherit
28+
steps:
29+
- name: Checkout code
30+
uses: actions/checkout@v4
31+
with:
32+
persist-credentials: false
33+
- name: Run analysis
34+
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
35+
with:
36+
results_file: results.sarif
37+
results_format: sarif
38+
publish_results: true
39+
- name: Upload SARIF to code-scanning
40+
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v3
41+
with:
42+
sarif_file: results.sarif

.github/workflows/secret-scanner.yml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
# SPDX-License-Identifier: MPL-2.0
2+
#
3+
# Standalone secret scan. Previously a thin caller of
4+
# `hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml`
5+
# with `secrets: inherit`; that cross-repo dependency startup-failed (the
6+
# caller's `concurrency:` block stacked on the reusable's — the BP008 class,
7+
# see spark-theatre-gate.yml) and required inheriting org secrets. This
8+
# self-contained version runs a pure-shell high-confidence scan
9+
# (tools/ci/secret-scan-standalone.sh), needs no secrets, and as a normal
10+
# workflow can keep its concurrency block.
211
name: Secret Scanner
312
on:
413
pull_request:
@@ -11,5 +20,10 @@ permissions:
1120
contents: read
1221
jobs:
1322
scan:
14-
uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@3e4bd4c93911750727e2e4c66dff859e00079da0
15-
secrets: inherit
23+
runs-on: ubuntu-latest
24+
timeout-minutes: 5
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@v4
28+
- name: Run standalone secret scan
29+
run: ./tools/ci/secret-scan-standalone.sh

tools/ci/governance-standalone.sh

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
#
4+
# Standalone governance gate.
5+
#
6+
# Replaces the external `hyperpolymath/standards` governance-reusable.yml so
7+
# this repo's CI carries no cross-repo workflow dependency. It is deliberately
8+
# conservative and delta-aware: it enforces the unambiguous estate rules that
9+
# the current tree already satisfies, and treats DOC-FORMAT as a PR-delta
10+
# check (matching the canonical "on any PR that adds a docs/ .md" semantics)
11+
# so the 59 pre-existing docs/*.md files are never retro-flagged.
12+
#
13+
# This is a best-effort local reimplementation; it is NOT byte-for-byte the
14+
# canonical estate bundle (which is not visible from this repo). See the
15+
# `standalone-ci` PR for the trade-off discussion.
16+
#
17+
# Usage: tools/ci/governance-standalone.sh [BASE_REF]
18+
# BASE_REF (or $GOVERNANCE_BASE_REF / $GITHUB_BASE_REF) enables the
19+
# DOC-FORMAT delta check; if absent the delta check is skipped.
20+
set -uo pipefail
21+
22+
fail=0
23+
note() { printf ' %s\n' "$*"; }
24+
err() { printf '::error::%s\n' "$*"; fail=1; }
25+
26+
echo "== Governance gate (standalone) =="
27+
28+
# --- [1] Jekyll artifacts are banned (estate SSG is hyperpolymath/casket-ssg)
29+
echo "[1/3] Jekyll artifacts"
30+
jekyll=$(find . \( -path ./.git -o -path ./_build -o -path ./node_modules \) -prune -o \
31+
\( -name '_config.yml' -o -name 'Gemfile' -o -iname 'jekyll*.yml' \
32+
-o -iname 'jekyll-gh-pages*.yml' \) -print 2>/dev/null || true)
33+
if [ -n "$jekyll" ]; then
34+
err "Jekyll artifacts present (migrate to hyperpolymath/casket-ssg):"
35+
printf '%s\n' "$jekyll" | sed 's/^/ /'
36+
else note "none"; fi
37+
38+
# --- [2] MPL-1.0 license headers are banned (must be MPL-2.0) ---------------
39+
# Match the actual SPDX identifier, not mere prose mentions of the ban (the
40+
# policy docs legitimately discuss MPL-1.0).
41+
echo "[2/3] MPL-1.0 SPDX headers"
42+
mpl1=$(grep -rIl --exclude-dir=.git --exclude-dir=_build --exclude-dir=node_modules \
43+
-E 'SPDX-License-Identifier:[[:space:]]*MPL-1\.0' . 2>/dev/null || true)
44+
if [ -n "$mpl1" ]; then
45+
err "MPL-1.0 SPDX headers found (rewrite to MPL-2.0):"
46+
printf '%s\n' "$mpl1" | sed 's/^/ /'
47+
else note "none"; fi
48+
49+
# --- [3] DOC-FORMAT (delta): newly-added docs/ files must be .adoc ----------
50+
# Community-health files keep their canonical .md names.
51+
echo "[3/3] DOC-FORMAT (newly-added docs/ files must be .adoc)"
52+
base="${1:-${GOVERNANCE_BASE_REF:-${GITHUB_BASE_REF:-}}}"
53+
if [ -n "$base" ] && git rev-parse --verify --quiet "origin/$base" >/dev/null 2>&1; then
54+
added=$(git diff --name-only --diff-filter=A "origin/$base...HEAD" -- 'docs/' 2>/dev/null \
55+
| grep -E '\.md$' \
56+
| grep -viE '/(CONTRIBUTING|CODE_OF_CONDUCT|SECURITY|CHANGELOG|README)\.md$' || true)
57+
if [ -n "$added" ]; then
58+
err "new docs/ .md files added (rename to .adoc per DOC-FORMAT):"
59+
printf '%s\n' "$added" | sed 's/^/ /'
60+
else note "no newly-added docs/ .md files"; fi
61+
else
62+
note "no base ref available — delta check skipped"
63+
fi
64+
65+
echo
66+
if [ "$fail" -ne 0 ]; then echo "Governance gate: FAIL"; exit 1; fi
67+
echo "Governance gate: PASS"

0 commit comments

Comments
 (0)