Repository navigation
fix(ci,governance): resolve template residue, harden CI workflows, and synchronize coordinator state #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(ci,governance): resolve template residue, harden CI workflows, and synchronize coordinator state #134
Changes from all commits
ec49f81
bf2ce36
f8380d5
4a5d08f
212a247
a45ed99
ef9cc5b
dbae7e3
00faca6
941d4b3
c8fa301
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> <{{AUTHOR_EMAIL_ALT}}> | ||
| Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> <6759885+hyperpolymath@users.noreply.github.com> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,5 @@ | ||
| # This workflow is managed by gh actions-lock. | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # This workflow is managed by gh actions-lock. | ||
| name: Governance | ||
|
|
||
| on: | ||
|
|
@@ -10,10 +9,127 @@ on: | |
| branches: [main, master] | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| actions: read | ||
| contents: read | ||
|
|
||
| # Self-contained execution of the shared estate governance policies pinned to | ||
| # metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d, plus | ||
| # aligned Well-Known RFC 9116 validation over canonical www/.well-known/. | ||
| jobs: | ||
| governance: | ||
| uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@fad242d35291de1898242d6737ba02b74a59a2f2 | ||
| name: Estate Governance Policy | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 15 | ||
| permissions: | ||
| contents: read | ||
| env: | ||
| STANDARDS_PIN: '8e48b1caade08a552646adeab66558ac43b4360d' | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Checkout pinned standards policy scripts | ||
| uses: actions/checkout@v7.0.1 | ||
| with: | ||
| repository: metadatastician/standards | ||
| ref: 8e48b1caade08a552646adeab66558ac43b4360d | ||
| path: .standards-checkout | ||
| sparse-checkout: | | ||
| scripts | ||
| 0-canon | ||
| sparse-checkout-cone-mode: true | ||
| persist-credentials: false | ||
|
|
||
| - name: Enforce TypeScript and npm/yarn runtime policy | ||
| run: | | ||
| set -euo pipefail | ||
| if [ -f .standards-checkout/scripts/check-ts-allowlist.sh ]; then | ||
| bash .standards-checkout/scripts/check-ts-allowlist.sh | ||
| fi | ||
| rm -rf .standards-checkout | ||
|
|
||
| bad_ts="$(git ls-files '*.ts' '*.tsx' | grep -vE '^(archetypes/|templates/|tests/fixtures/)' || true)" | ||
| if [ -n "$bad_ts" ]; then | ||
| echo "::error::Unallowlisted TypeScript files detected:" | ||
| printf '%s\n' "$bad_ts" | ||
| exit 1 | ||
| fi | ||
|
|
||
| bad_locks="$(git ls-files 'package-lock.json' '**/package-lock.json' 'yarn.lock' '**/yarn.lock' 'pnpm-lock.yaml' '**/pnpm-lock.yaml' || true)" | ||
| if [ -n "$bad_locks" ]; then | ||
| echo "::error::Banned npm/yarn/pnpm lockfiles detected (use Deno or Bun per LANGUAGE-POLICY):" | ||
| printf '%s\n' "$bad_locks" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if [ -f tsconfig.json ] || [ -f Makefile ] || [ -f GNUmakefile ]; then | ||
| echo "::error::Banned root config (tsconfig.json / Makefile) detected" | ||
| exit 1 | ||
| fi | ||
| echo "Governance runtime and language anti-pattern checks passed." | ||
|
|
||
| wellknown: | ||
| name: Well-Known (RFC 9116 + RSR) | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 10 | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: RFC 9116 security.txt validation | ||
| run: | | ||
| set -euo pipefail | ||
| SECTXT="" | ||
| if [ -f "www/.well-known/security.txt" ]; then | ||
| SECTXT="www/.well-known/security.txt" | ||
| elif [ -f ".well-known/security.txt" ]; then | ||
| SECTXT=".well-known/security.txt" | ||
| echo "::warning::security.txt at legacy root .well-known/ — canonical location is www/.well-known/" | ||
| elif [ -f "security.txt" ]; then | ||
| SECTXT="security.txt" | ||
| echo "::warning::security.txt at repository root — canonical location is www/.well-known/" | ||
| fi | ||
|
|
||
| if [ -z "$SECTXT" ]; then | ||
| echo "::error::No security.txt found (expected www/.well-known/security.txt)" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if grep -qE '\{\{[A-Z0-9_]+\}\}' "$SECTXT"; then | ||
| echo "::error::$SECTXT contains unfilled placeholder tokens" | ||
| exit 1 | ||
| fi | ||
|
|
||
| grep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; } | ||
| grep -q "^Expires:" "$SECTXT" || { echo "::error::Missing Expires field"; exit 1; } | ||
|
|
||
| EXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -1) | ||
| if [[ ! "$EXPIRES" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}[Tt][0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([Zz]|[+-][0-9]{2}:[0-9]{2})$ ]]; then | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| echo "::error::Expires is not an RFC 3339 date-time: $EXPIRES" | ||
| exit 1 | ||
| fi | ||
| if ! EXPIRY_EPOCH=$(date -d "$EXPIRES" +%s 2>/dev/null); then | ||
| echo "::error::Expires is not a valid date-time: $EXPIRES" | ||
| exit 1 | ||
| fi | ||
|
hyperpolymath marked this conversation as resolved.
Resolved
|
||
| NOW_EPOCH=$(date +%s) | ||
| if [ "$EXPIRY_EPOCH" -le "$NOW_EPOCH" ]; then | ||
| echo "::error::security.txt EXPIRED" | ||
| exit 1 | ||
| fi | ||
| DAYS=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 )) | ||
| if [ "$DAYS" -lt 30 ]; then | ||
| echo "::warning::security.txt expires in $DAYS days" | ||
| else | ||
| echo "security.txt valid ($DAYS days, $SECTXT)" | ||
| fi | ||
|
hyperpolymath marked this conversation as resolved.
Resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,107 @@ | ||
| # This workflow is managed by gh actions-lock. | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # This workflow is managed by gh actions-lock. | ||
| # | ||
| # Self-contained Hypatia neurosymbolic security scan + SARIF upload (`category: | ||
| # hypatia`) replacing the retired reusable caller after `standards` transferred. | ||
| name: Hypatia Security Scan | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main, master, develop] | ||
| pull_request: | ||
| branches: [main, master] | ||
| push: | ||
| branches: [main, master, develop] | ||
| schedule: | ||
| - cron: '0 0 * * 0' | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| security-events: write | ||
|
|
||
| jobs: | ||
| hypatia: | ||
| uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@84355587cb2a1f86e6882de83514a32db2646e7a | ||
| name: Hypatia Neurosymbolic Analysis | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 20 | ||
| permissions: | ||
| contents: read | ||
| security-events: write | ||
| env: | ||
| HYPATIA_SHA: 'd73f76b5cb7a35e1aa1024c916aee7cb4f9b141e' | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v7.0.1 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Setup Elixir 1.19.4 / OTP 28.3 for Hypatia scanner | ||
| uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 | ||
| with: | ||
| elixir-version: '1.19.4' | ||
| otp-version: '28.3' | ||
|
|
||
| - name: Build pinned Hypatia scanner | ||
| run: | | ||
| set -euo pipefail | ||
| rm -rf "$HOME/hypatia" | ||
| git init "$HOME/hypatia" | ||
| git -C "$HOME/hypatia" remote add origin https://github.com/hyperpolymath/hypatia.git | ||
| git -C "$HOME/hypatia" fetch --depth=1 origin "$HYPATIA_SHA" | ||
| git -C "$HOME/hypatia" checkout --detach FETCH_HEAD | ||
| ( | ||
| cd "$HOME/hypatia" | ||
| mix local.hex --force | ||
| mix local.rebar --force | ||
| mix deps.get | ||
| MIX_NO_PUBSUB=1 mix escript.build | ||
| ) | ||
| chmod +x "$HOME/hypatia/hypatia" "$HOME/hypatia/hypatia-cli.sh" | ||
|
|
||
| - name: Run Hypatia JSON and SARIF analysis | ||
| id: scan | ||
| run: | | ||
| set -euo pipefail | ||
| HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia-findings.json | ||
| jq -e 'type == "array"' hypatia-findings.json >/dev/null | ||
|
|
||
| HYPATIA_FORMAT=sarif "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia.sarif | ||
| jq -e '.runs | type == "array" and length > 0' hypatia.sarif >/dev/null | ||
|
|
||
| CRITICAL="$(jq '[.[] | select(.severity == "critical")] | length' hypatia-findings.json)" | ||
| HIGH="$(jq '[.[] | select(.severity == "high")] | length' hypatia-findings.json)" | ||
| TOTAL="$(jq 'length' hypatia-findings.json)" | ||
| { | ||
| echo "critical=$CRITICAL" | ||
| echo "high=$HIGH" | ||
| echo "total=$TOTAL" | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Upload Hypatia SARIF to GitHub Code Scanning | ||
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | ||
| uses: github/codeql-action/upload-sarif@v4.39.1 | ||
| with: | ||
| sarif_file: hypatia.sarif | ||
| category: hypatia | ||
|
|
||
| - name: Enforce zero critical Hypatia findings | ||
| env: | ||
| CRITICAL_COUNT: ${{ steps.scan.outputs.critical }} | ||
| HIGH_COUNT: ${{ steps.scan.outputs.high }} | ||
| TOTAL_COUNT: ${{ steps.scan.outputs.total }} | ||
| run: | | ||
| set -euo pipefail | ||
| { | ||
| echo "## Hypatia Neurosymbolic Scan" | ||
| echo "" | ||
| echo "- Critical: ${CRITICAL_COUNT}" | ||
| echo "- High: ${HIGH_COUNT}" | ||
| echo "- Total findings: ${TOTAL_COUNT}" | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| if [ "$CRITICAL_COUNT" != "0" ]; then | ||
| echo "::error::Hypatia reported ${CRITICAL_COUNT} critical finding(s)" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When Git commands such as
git shortlog,git log --use-mailmap, orgit check-mailmaptry to normalize this noreply address, this mapping is ignored because Git reads the worktree-root.mailmapby default (or a path explicitly configured throughmailmap.file), not.github/.mailmap; this repository configures no alternative, andgit check-mailmapreturns the address unchanged. Move the populated mapping to the repository root so the intended author identity normalization actually takes effect; see the officialgitmailmapfile-location documentation.Useful? React with 👍 / 👎.