diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a91454d4..db0dd83c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,7 +16,14 @@ updates: # SHA-form re-bump attempt bypassed versions-scoped ignores — see # nexia-list#101/#104). Hold until upstream clears 4.38.1 or a newer # release verifies green; revisit deliberately, not weekly. - - dependency-name: "github/codeql-action" + # + # The trailing * is load-bearing. Workflows reference the SUBPATH + # actions (github/codeql-action/init, /analyze, /upload-sarif) and + # Dependabot treats each subpath as its own dependency name -- so a + # bare "github/codeql-action" entry matches NONE of them. That is how + # #977 re-bumped 4.38.0 -> 4.38.1 on 2026-09-22 straight through this + # hold, startup-killing codeql.yml and scorecard.yml (jobs=0). + - dependency-name: "github/codeql-action*" # github-actions major bumps are usually safe — the SHA pin is the real # version. Standards repo is the canonical-template host so we want the # PRs fast (daily) and grouped. If a specific action proves unstable on diff --git a/.github/workflows/codeql-reusable.yml b/.github/workflows/codeql-reusable.yml index 9f0158d4..4e13fa00 100644 --- a/.github/workflows/codeql-reusable.yml +++ b/.github/workflows/codeql-reusable.yml @@ -94,12 +94,12 @@ jobs: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: languages: ${{ inputs.language }} build-mode: ${{ inputs.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: category: "/language:${{ inputs.language }}" diff --git a/.github/workflows/hypatia-scan-reusable.yml b/.github/workflows/hypatia-scan-reusable.yml index 313f8679..e4dc53f8 100644 --- a/.github/workflows/hypatia-scan-reusable.yml +++ b/.github/workflows/hypatia-scan-reusable.yml @@ -242,7 +242,7 @@ jobs: # This flag only tolerates a genuine upload failure (e.g. Advanced # Security disabled on a private repo) once the job is actually running. continue-on-error: true - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: sarif_file: hypatia.sarif category: hypatia diff --git a/.github/workflows/scorecard-reusable.yml b/.github/workflows/scorecard-reusable.yml index 1f9da93f..7fa7240c 100644 --- a/.github/workflows/scorecard-reusable.yml +++ b/.github/workflows/scorecard-reusable.yml @@ -92,7 +92,7 @@ jobs: printf 'reconciled=false\n' >> "$GITHUB_OUTPUT" fi - name: Upload SARIF to code scanning - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v3 + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: sarif_file: ${{ steps.select-sarif.outputs.file }} @@ -225,7 +225,7 @@ jobs: printf 'reconciled=false\n' >> "$GITHUB_OUTPUT" fi - name: Upload SARIF to code scanning - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: sarif_file: ${{ steps.select-sarif.outputs.file }} - name: Retain scan evidence diff --git a/scripts/check-docs-presence.sh b/scripts/check-docs-presence.sh index fcdc8375..a386759a 100755 --- a/scripts/check-docs-presence.sh +++ b/scripts/check-docs-presence.sh @@ -24,7 +24,22 @@ # default; GitHub-required community-health files stay Markdown): # README.adoc | README.md # LICENSE | LICENSE.txt | LICENSE.md -# CONTRIBUTING.md | CONTRIBUTING.adoc | 3-practice/CONTRIBUTING.adoc +# CONTRIBUTING.md | CONTRIBUTING.adoc +# | .github/CONTRIBUTING.md | .github/CONTRIBUTING.adoc +# | docs/CONTRIBUTING.md | docs/CONTRIBUTING.adoc +# | 3-practice/CONTRIBUTING.adoc +# +# .github/ and docs/ are accepted because GitHub itself auto-discovers a +# community-health file in either location, and repos in this estate have been +# deliberately relocating theirs there (launch-scaffolder d426ea4d: "the estate +# canonical location is .github/CONTRIBUTING.md, which GitHub auto-discovers"). +# Before this change the gate asked "is there a CONTRIBUTING at the repo root?" +# while its consumers had been told to answer "is there a CONTRIBUTING GitHub +# can find?" -- a guard asking a different question than its consumer. A census +# of 516 local clones on 2026-09-22 found 19 repos reported missing that in fact +# carry the file under .github/ or docs/; 94 are genuinely missing it and are +# unaffected by this change. This strictly widens WHERE the gate looks; it does +# not weaken WHAT it asks. # # CONTRIBUTING.adoc at the repo root is accepted because the estate policy named # above makes AsciiDoc the default, and it is what the estate actually uses: @@ -75,7 +90,7 @@ if [ ! -d "$ROOT" ]; then exit 1 fi -# have ... -> 0 if any of the candidate filenames exists at the root. +# have ... -> 0 if any of the candidate paths exists, relative to the root. have() { local f for f in "$@"; do @@ -90,7 +105,10 @@ grace_missing="" have README.adoc README.md || blocking_missing="$blocking_missing README" have LICENSE LICENSE.txt LICENSE.md || blocking_missing="$blocking_missing LICENSE" -if ! have CONTRIBUTING.md CONTRIBUTING.adoc 3-practice/CONTRIBUTING.adoc; then +if ! have CONTRIBUTING.md CONTRIBUTING.adoc \ + .github/CONTRIBUTING.md .github/CONTRIBUTING.adoc \ + docs/CONTRIBUTING.md docs/CONTRIBUTING.adoc \ + 3-practice/CONTRIBUTING.adoc; then # String comparison is sound here: YYYY-MM-DD sorts chronologically, and both # operands are format-validated above. if [[ "$TODAY" < "$ENFORCE_CONTRIBUTING_FROM" ]]; then @@ -111,7 +129,12 @@ if [ -n "$blocking_missing" ]; then echo "Required at the repository root (either extension where two are listed):" echo " README.adoc (or README.md)" echo " LICENSE (or LICENSE.txt / LICENSE.md)" - echo " CONTRIBUTING.md (or 3-practice/CONTRIBUTING.adoc)" + echo + echo "CONTRIBUTING is accepted at any location GitHub auto-discovers:" + echo " CONTRIBUTING.md (or CONTRIBUTING.adoc)" + echo " .github/CONTRIBUTING.md (or .github/CONTRIBUTING.adoc)" + echo " docs/CONTRIBUTING.md (or docs/CONTRIBUTING.adoc)" + echo " 3-practice/CONTRIBUTING.adoc" echo echo "Estate policy: docs are AsciiDoc by default; see hyperpolymath/standards." exit 1 diff --git a/scripts/tests/governance-gates-505-test.sh b/scripts/tests/governance-gates-505-test.sh index e719abf0..f154fdc6 100755 --- a/scripts/tests/governance-gates-505-test.sh +++ b/scripts/tests/governance-gates-505-test.sh @@ -83,6 +83,40 @@ assert "root CONTRIBUTING.adoc still warns pre-cutoff only when ABSENT" 0 \ "NOT YET ENFORCED" \ env DOCS_TODAY="$BEFORE" "$DOCS" "$(mkrepo docs-adoc-root-absent README.adoc LICENSE)" +# Regression: GitHub auto-discovers a community-health file under .github/ or +# docs/, and estate repos have been deliberately relocating theirs there +# (launch-scaffolder d426ea4d). The gate looked only at the repo root, so it +# reported those repos "missing" a file that is present and discoverable — a +# guard asking a different question than its consumer. A 516-clone census on +# 2026-09-22 found 19 such repos. Each of the four new paths gets its own case: +# a single .github/CONTRIBUTING.md case would pass even if only that one path +# had been added to the candidate list. +r=$(mkrepo docs-github-md README.adoc LICENSE .github/CONTRIBUTING.md) +assert ".github/CONTRIBUTING.md accepted (regression: launch-scaffolder#37)" 0 \ + "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +r=$(mkrepo docs-github-adoc README.adoc LICENSE .github/CONTRIBUTING.adoc) +assert ".github/CONTRIBUTING.adoc accepted" 0 "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +r=$(mkrepo docs-docsdir-md README.adoc LICENSE docs/CONTRIBUTING.md) +assert "docs/CONTRIBUTING.md accepted" 0 "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +r=$(mkrepo docs-docsdir-adoc README.adoc LICENSE docs/CONTRIBUTING.adoc) +assert "docs/CONTRIBUTING.adoc accepted" 0 "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +# Anti-overreach: widening WHERE the gate looks must not widen WHAT it asks. +# A CONTRIBUTING at an arbitrary depth is NOT discoverable by GitHub and must +# still block. Without this case the four above could be "satisfied" by a +# recursive find, which would silently pass the 94 genuinely-missing repos. +r=$(mkrepo docs-deep-nested README.adoc LICENSE src/internal/CONTRIBUTING.md) +assert "CONTRIBUTING at an undiscoverable path still BLOCKS" 1 \ + "Missing required documentation: CONTRIBUTING" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + # README/LICENSE are BLOCKING NOW — the grace window must not shelter them. r=$(mkrepo docs-no-readme LICENSE CONTRIBUTING.md) assert "missing README fails even pre-cutoff" 1 "Missing required documentation: README" \