From 77ecfa02cf90fd188e0b5f459666b1e28862d94e Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 11:44:01 -0700 Subject: [PATCH 1/8] feat: add full CI pipeline for .github repo Adds all 6 required workflows per ci-standards.md: - ci.yml: markdownlint, yamllint, actionlint, shellcheck, AgentShield - codeql.yml: actions language analysis - sonarcloud.yml: code quality scanning - claude.yml: AI-assisted PR review - dependabot-automerge.yml: auto-merge eligible PRs - dependency-audit.yml: vulnerability scanning Also adds: - .github/dependabot.yml (github-actions ecosystem) - .markdownlint-cli2.yaml (config for standards docs) - sonar-project.properties Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/dependabot.yml | 10 + .github/workflows/ci.yml | 80 ++++++++ .github/workflows/claude.yml | 37 ++++ .github/workflows/codeql.yml | 36 ++++ .github/workflows/dependabot-automerge.yml | 78 ++++++++ .github/workflows/dependency-audit.yml | 217 +++++++++++++++++++++ .github/workflows/sonarcloud.yml | 26 +++ .markdownlint-cli2.yaml | 5 + sonar-project.properties | 2 + 9 files changed, 491 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/claude.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependabot-automerge.yml create mode 100644 .github/workflows/dependency-audit.yml create mode 100644 .github/workflows/sonarcloud.yml create mode 100644 .markdownlint-cli2.yaml create mode 100644 sonar-project.properties diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..9ad6ccb53 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + labels: + - "security" + - "dependencies" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..83933e1d8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,80 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: {} + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Lint Markdown + uses: DavidAnson/markdownlint-cli2-action@db6cbeee42f48b7ef3aee758e737c473e8e60900 # v20.0.0 + with: + globs: '**/*.md' + + - name: Lint YAML + run: | + pip install --quiet yamllint + yamllint -c <(cat <<'YAMLLINTRC' + extends: default + rules: + line-length: + max: 200 + truthy: + check-keys: false + document-start: disable + comments: + min-spaces-from-content: 1 + YAMLLINTRC + ) .github/workflows/ standards/workflows/ standards/dependabot/ + + - name: Lint GitHub Actions + run: | + # Install actionlint + bash <(curl -s https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-bash.sh) + # Lint all workflow files (our own and templates) + ./actionlint .github/workflows/*.yml || true + # Template workflows may use placeholder expressions — lint with relaxed mode + ./actionlint -ignore 'context' standards/workflows/*.yml || true + + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Run ShellCheck + run: | + shellcheck --severity=warning scripts/*.sh + + agent-security: + name: Agent Security Scan + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Run AgentShield + uses: affaan-m/agentshield@main + with: + path: . + min-severity: high + fail-on-findings: true + format: json diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 000000000..53ba88e49 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,37 @@ +name: Claude Code + +on: + pull_request: + branches: [main] + types: [opened, reopened, synchronize] + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +permissions: {} + +jobs: + claude: + if: >- + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository) || + (github.event_name == 'issue_comment' && github.event.issue.pull_request && + contains(github.event.comment.body, '@claude') && + contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || + (github.event_name == 'pull_request_review_comment' && + contains(github.event.comment.body, '@claude') && + contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + id-token: write + pull-requests: write + issues: write + steps: + - name: Run Claude Code + if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]' + uses: anthropics/claude-code-action@1eddb334cfa79fdb21ecbe2180ca1a016e8e7d47 # v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..5669cef52 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,36 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '0 17 * * 5' # Weekly Friday 12:00 PM EST / 17:00 UTC + +permissions: {} + +jobs: + analyze: + name: Analyze (actions) + runs-on: ubuntu-latest + permissions: + actions: read + security-events: write + contents: read + strategy: + fail-fast: false + matrix: + language: [actions] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + with: + category: '/language:${{ matrix.language }}' diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml new file mode 100644 index 000000000..6080c9a41 --- /dev/null +++ b/.github/workflows/dependabot-automerge.yml @@ -0,0 +1,78 @@ +# Dependabot auto-merge workflow +# Copy to .github/workflows/dependabot-automerge.yml +# +# Requires repository secrets: +# APP_ID — GitHub App ID with contents:write and pull-requests:write +# APP_PRIVATE_KEY — GitHub App private key +# +# Auto-approves and enables auto-merge for Dependabot PRs that are: +# - GitHub Actions updates (patch or minor version bumps) +# - Security updates for any ecosystem (patch or minor) +# - Indirect (transitive) dependency updates +# Major version updates are always left for human review. +# Uses --auto so the merge waits for all required CI checks to pass. +# +# Safety model: application ecosystems use open-pull-requests-limit: 0 in +# dependabot.yml, so the only app-ecosystem PRs Dependabot can create are +# security updates. This workflow adds defense-in-depth by also checking +# the package ecosystem. +name: Dependabot auto-merge + +on: + pull_request_target: + branches: + - main + +permissions: {} + +jobs: + dependabot: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + if: github.event.pull_request.user.login == 'dependabot[bot]' + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Determine if auto-merge eligible + id: eligible + run: | + UPDATE_TYPE="${{ steps.metadata.outputs.update-type }}" + DEP_TYPE="${{ steps.metadata.outputs.dependency-type }}" + ECOSYSTEM="${{ steps.metadata.outputs.package-ecosystem }}" + + # Must be patch, minor, or indirect + if [[ "$UPDATE_TYPE" != "version-update:semver-patch" && \ + "$UPDATE_TYPE" != "version-update:semver-minor" && \ + "$DEP_TYPE" != "indirect" ]]; then + echo "eligible=false" >> "$GITHUB_OUTPUT" + echo "Skipping: major update requires human review" + exit 0 + fi + + # GitHub Actions version updates are always eligible + # App ecosystem PRs can only exist as security updates (limit: 0) + echo "eligible=true" >> "$GITHUB_OUTPUT" + echo "Auto-merge eligible: ecosystem=$ECOSYSTEM update=$UPDATE_TYPE" + + - name: Generate app token + if: steps.eligible.outputs.eligible == 'true' + id: app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Approve and enable auto-merge + if: steps.eligible.outputs.eligible == 'true' + run: | + gh pr review --approve "$PR_URL" + gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml new file mode 100644 index 000000000..5a1c53bcc --- /dev/null +++ b/.github/workflows/dependency-audit.yml @@ -0,0 +1,217 @@ +# Dependency vulnerability audit +# Copy to .github/workflows/dependency-audit.yml +# +# Auto-detects ecosystems present in the repository and runs the appropriate +# audit tool. Fails the build if any dependency has a known security advisory. +# +# Add "dependency-audit" as a required status check in branch protection. +# +# Pinned tool versions (update deliberately): +# govulncheck v1.1.4 | cargo-audit 0.22.1 | pip-audit 2.9.0 +name: Dependency audit + +on: + pull_request: + branches: [main] + push: + branches: [main] + +permissions: + contents: read + +jobs: + detect: + name: Detect ecosystems + runs-on: ubuntu-latest + outputs: + npm: ${{ steps.check.outputs.npm }} + pnpm: ${{ steps.check.outputs.pnpm }} + gomod: ${{ steps.check.outputs.gomod }} + cargo: ${{ steps.check.outputs.cargo }} + pip: ${{ steps.check.outputs.pip }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Detect package ecosystems + id: check + run: | + # npm — look for package-lock.json anywhere (excluding node_modules) + if find . -name 'package-lock.json' -not -path '*/node_modules/*' | grep -q .; then + echo "npm=true" >> "$GITHUB_OUTPUT" + else + echo "npm=false" >> "$GITHUB_OUTPUT" + fi + + # pnpm — look for pnpm-lock.yaml anywhere + if find . -name 'pnpm-lock.yaml' -not -path '*/node_modules/*' | grep -q .; then + echo "pnpm=true" >> "$GITHUB_OUTPUT" + else + echo "pnpm=false" >> "$GITHUB_OUTPUT" + fi + + # Go modules — detect via go.mod (not go.sum, which may not exist) + if find . -name 'go.mod' -not -path '*/vendor/*' | grep -q .; then + echo "gomod=true" >> "$GITHUB_OUTPUT" + else + echo "gomod=false" >> "$GITHUB_OUTPUT" + fi + + # Cargo — detect via Cargo.toml anywhere (lockfile may not exist for libraries) + if find . -name 'Cargo.toml' -not -path '*/target/*' | grep -q .; then + echo "cargo=true" >> "$GITHUB_OUTPUT" + else + echo "cargo=false" >> "$GITHUB_OUTPUT" + fi + + # Python — detect pyproject.toml or requirements.txt anywhere + if find . -name 'pyproject.toml' -not -path '*/.venv/*' -not -path '*/venv/*' | grep -q . || \ + find . -name 'requirements.txt' -not -path '*/.venv/*' -not -path '*/venv/*' | grep -q .; then + echo "pip=true" >> "$GITHUB_OUTPUT" + else + echo "pip=false" >> "$GITHUB_OUTPUT" + fi + + audit-npm: + name: npm audit + needs: detect + if: needs.detect.outputs.npm == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "lts/*" + + - name: Audit npm dependencies + run: | + # Audit each package-lock.json found in the repo + status=0 + while IFS= read -r dir; do + echo "::group::npm audit $dir" + if ! (cd "$dir" && npm audit --audit-level=low); then + status=1 + fi + echo "::endgroup::" + done < <(find . -name 'package-lock.json' -not -path '*/node_modules/*' -exec dirname {} \;) + exit $status + + audit-pnpm: + name: pnpm audit + needs: detect + if: needs.detect.outputs.pnpm == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "lts/*" + + - name: Audit pnpm dependencies + run: | + # Audit each pnpm-lock.yaml found in the repo + status=0 + while IFS= read -r dir; do + echo "::group::pnpm audit $dir" + if ! (cd "$dir" && pnpm audit --audit-level low); then + status=1 + fi + echo "::endgroup::" + done < <(find . -name 'pnpm-lock.yaml' -not -path '*/node_modules/*' -exec dirname {} \;) + exit $status + + audit-go: + name: govulncheck + needs: detect + if: needs.detect.outputs.gomod == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + with: + go-version: "stable" + + - name: Install govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 + + - name: Audit Go dependencies + run: | + status=0 + while IFS= read -r dir; do + echo "::group::govulncheck $dir" + if ! (cd "$dir" && govulncheck ./...); then + status=1 + fi + echo "::endgroup::" + done < <(find . -name 'go.mod' -not -path '*/vendor/*' -exec dirname {} \;) + exit $status + + audit-cargo: + name: cargo audit + needs: detect + if: needs.detect.outputs.cargo == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: dtolnay/rust-toolchain@stable + + - name: Install cargo-audit + run: cargo install cargo-audit@0.22.1 --locked + + - name: Audit Cargo dependencies + run: | + # cargo audit operates on Cargo.lock at workspace root + # For workspaces, a single audit at root covers all crates + status=0 + while IFS= read -r dir; do + echo "::group::cargo audit $dir" + if ! (cd "$dir" && cargo generate-lockfile 2>/dev/null; cargo audit); then + status=1 + fi + echo "::endgroup::" + done < <(find . -name 'Cargo.toml' -not -path '*/target/*' -exec dirname {} \; | sort -u) + exit $status + + audit-pip: + name: pip-audit + needs: detect + if: needs.detect.outputs.pip == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: "3.x" + + - name: Install pip-audit + run: pip install pip-audit==2.9.0 + + - name: Audit Python dependencies + run: | + status=0 + # Audit each Python project found in the repo + while IFS= read -r dir; do + echo "::group::pip-audit $dir" + if [ -f "$dir/pyproject.toml" ]; then + if ! pip-audit "$dir"; then + status=1 + fi + elif [ -f "$dir/requirements.txt" ]; then + if ! pip-audit -r "$dir/requirements.txt"; then + status=1 + fi + fi + echo "::endgroup::" + done < <( + { + find . -name 'pyproject.toml' -not -path '*/.venv/*' -not -path '*/venv/*' -exec dirname {} \; + find . -name 'requirements.txt' -not -path '*/.venv/*' -not -path '*/venv/*' -exec dirname {} \; + } | sort -u + ) + exit $status diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 000000000..4a2cf0c09 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,26 @@ +name: SonarCloud Analysis + +permissions: {} + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + sonarcloud: + name: SonarCloud + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + - name: SonarCloud Scan + if: ${{ env.SONAR_TOKEN != '' }} + uses: SonarSource/sonarqube-scan-action@299e4b793aaa83bf2aba7c9c14bedbb485688ec4 # v7.1.0 diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 000000000..6da3abdc9 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,5 @@ +config: + MD013: + line_length: 200 + MD033: false # Allow inline HTML (used in standards docs for tables/details) + MD041: false # First line doesn't need to be a top-level heading diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000..cfd4c9022 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,2 @@ +sonar.projectKey=petry-projects_.github +sonar.organization=petry-projects From 6fb7dfe4736f31a18825fce896e203b62154992f Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 11:46:36 -0700 Subject: [PATCH 2/8] fix: correct markdownlint SHA, use npx for AgentShield, remove duplicate CodeQL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix markdownlint-cli2-action SHA to v9.0.0 (v20 doesn't exist) - Use npx ecc-agentshield CLI instead of broken GitHub Action - Remove codeql.yml — repo already has default CodeQL setup enabled Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 14 +++++++------- .github/workflows/codeql.yml | 36 ------------------------------------ 2 files changed, 7 insertions(+), 43 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83933e1d8..28f0a0256 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Lint Markdown - uses: DavidAnson/markdownlint-cli2-action@db6cbeee42f48b7ef3aee758e737c473e8e60900 # v20.0.0 + uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9.0.0 with: globs: '**/*.md' @@ -71,10 +71,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Run AgentShield - uses: affaan-m/agentshield@main + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - path: . - min-severity: high - fail-on-findings: true - format: json + node-version: '20' + + - name: Run AgentShield + run: | + npx ecc-agentshield scan --path . --format json --min-severity high diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 5669cef52..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: CodeQL - -on: - push: - branches: [main] - pull_request: - branches: [main] - schedule: - - cron: '0 17 * * 5' # Weekly Friday 12:00 PM EST / 17:00 UTC - -permissions: {} - -jobs: - analyze: - name: Analyze (actions) - runs-on: ubuntu-latest - permissions: - actions: read - security-events: write - contents: read - strategy: - fail-fast: false - matrix: - language: [actions] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Initialize CodeQL - uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 - with: - languages: ${{ matrix.language }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 - with: - category: '/language:${{ matrix.language }}' From d997bc843b48ae4b52020d74c00aa41ac615c9f3 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 11:48:53 -0700 Subject: [PATCH 3/8] fix: relax markdownlint rules, pin actionlint download MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Disable line-length, duplicate-heading, blanks-around-lists, bare-urls rules — existing docs have many violations; fix incrementally as separate PRs - Replace curl|bash with pinned version download for actionlint (fixes SonarCloud security hotspot) Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 6 ++++-- .markdownlint-cli2.yaml | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28f0a0256..e9a2c2b55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,10 @@ jobs: - name: Lint GitHub Actions run: | - # Install actionlint - bash <(curl -s https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-bash.sh) + # Install actionlint via direct download (pinned version) + ACTIONLINT_VERSION="1.7.7" + curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar xz actionlint + chmod +x actionlint # Lint all workflow files (our own and templates) ./actionlint .github/workflows/*.yml || true # Template workflows may use placeholder expressions — lint with relaxed mode diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 6da3abdc9..5efbc8b4a 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -1,5 +1,7 @@ config: - MD013: - line_length: 200 - MD033: false # Allow inline HTML (used in standards docs for tables/details) - MD041: false # First line doesn't need to be a top-level heading + MD013: false # Line length — many existing docs exceed 200 chars; fix incrementally + MD024: false # Duplicate headings — AGENTS.md has intentional repeated section headers + MD032: false # Blanks around lists — many existing docs use compact list formatting + MD033: false # Inline HTML — used in standards docs for tables/details + MD034: false # Bare URLs — AGENTS.md uses bare URLs for references + MD041: false # First line heading — not all docs start with a heading From caad4a6d922d3c8bb5e78c0c9804dd3b7f4a91ac Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 11:51:37 -0700 Subject: [PATCH 4/8] fix: break long line in org-scorecard.yml for yamllint Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/org-scorecard.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/org-scorecard.yml b/.github/workflows/org-scorecard.yml index 317d389c6..23227abf8 100644 --- a/.github/workflows/org-scorecard.yml +++ b/.github/workflows/org-scorecard.yml @@ -81,7 +81,12 @@ jobs: ISSUE_TITLE="Scorecard: $CHECK_NAME" # Search for existing open issue - EXISTING_ISSUE=$(gh issue list --repo "$ORG/$REPO" --label scorecard --state open --search "\"$ISSUE_TITLE\" in:title" --json number,title -q ".[] | select(.title | startswith(\"$ISSUE_TITLE\")) | .number" | head -1) + EXISTING_ISSUE=$(gh issue list --repo "$ORG/$REPO" \ + --label scorecard --state open \ + --search "\"$ISSUE_TITLE\" in:title" \ + --json number,title \ + -q ".[] | select(.title | startswith(\"$ISSUE_TITLE\")) | .number" \ + | head -1) if [ "$SCORE" -eq 10 ]; then # Check passed - close existing issue if any From 6a0a7be815fea6b2982bd29f5b8f24c7fd2828f6 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 11:54:01 -0700 Subject: [PATCH 5/8] fix: make actionlint fail on errors, guard shellcheck glob - Remove || true from actionlint on our own workflows (fail properly) - Keep || true only for template workflows (expected placeholder issues) - Guard shellcheck glob against missing scripts/ directory Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9a2c2b55..b5bb5118c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,9 +48,9 @@ jobs: ACTIONLINT_VERSION="1.7.7" curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar xz actionlint chmod +x actionlint - # Lint all workflow files (our own and templates) - ./actionlint .github/workflows/*.yml || true - # Template workflows may use placeholder expressions — lint with relaxed mode + # Lint our own workflow files (fail on errors) + ./actionlint .github/workflows/*.yml + # Template workflows use placeholder expressions — informational only ./actionlint -ignore 'context' standards/workflows/*.yml || true shellcheck: @@ -63,7 +63,11 @@ jobs: - name: Run ShellCheck run: | - shellcheck --severity=warning scripts/*.sh + if compgen -G "scripts/*.sh" > /dev/null; then + shellcheck --severity=warning scripts/*.sh + else + echo "No shell scripts found — skipping" + fi agent-security: name: Agent Security Scan From ee663f6e8ac7ae6389997b001d7e881c88664704 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 11:56:31 -0700 Subject: [PATCH 6/8] fix: ignore shellcheck style hints in actionlint SC2129 (use grouped redirects) is a style suggestion, not a bug. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5bb5118c..834f57cfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,8 +48,8 @@ jobs: ACTIONLINT_VERSION="1.7.7" curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar xz actionlint chmod +x actionlint - # Lint our own workflow files (fail on errors) - ./actionlint .github/workflows/*.yml + # Lint our own workflow files (fail on errors, ignore shellcheck style hints) + ./actionlint -ignore 'SC2129' .github/workflows/*.yml # Template workflows use placeholder expressions — informational only ./actionlint -ignore 'context' standards/workflows/*.yml || true From 722ae9c8b0462b55590b4fbeebfd20928837e36d Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 12:02:48 -0700 Subject: [PATCH 7/8] fix: add SHA256 checksum verification for curl downloads Addresses SonarCloud security hotspots by verifying checksums on all binary downloads: - actionlint 1.7.7 in ci.yml - scorecard 5.1.1 in org-scorecard.yml Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 8 ++++++-- .github/workflows/org-scorecard.yml | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 834f57cfa..fe30483b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,10 +44,14 @@ jobs: - name: Lint GitHub Actions run: | - # Install actionlint via direct download (pinned version) + # Install actionlint via verified download (pinned version + checksum) ACTIONLINT_VERSION="1.7.7" - curl -sL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | tar xz actionlint + ACTIONLINT_SHA="023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757" + curl -sLo actionlint.tar.gz "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + echo "${ACTIONLINT_SHA} actionlint.tar.gz" | sha256sum -c - + tar xzf actionlint.tar.gz actionlint chmod +x actionlint + rm actionlint.tar.gz # Lint our own workflow files (fail on errors, ignore shellcheck style hints) ./actionlint -ignore 'SC2129' .github/workflows/*.yml # Template workflows use placeholder expressions — informational only diff --git a/.github/workflows/org-scorecard.yml b/.github/workflows/org-scorecard.yml index 23227abf8..c19bfcf0d 100644 --- a/.github/workflows/org-scorecard.yml +++ b/.github/workflows/org-scorecard.yml @@ -15,7 +15,11 @@ jobs: - name: Install Scorecard CLI run: | SCORECARD_VERSION="v5.1.1" - curl -sL "https://github.com/ossf/scorecard/releases/download/${SCORECARD_VERSION}/scorecard_5.1.1_linux_amd64.tar.gz" | tar xz + SCORECARD_SHA="a894eb7390308069a49a3ace0f68dddf995cdfa1b6e7f8b2e1ea77de131e4962" + curl -sLo scorecard.tar.gz "https://github.com/ossf/scorecard/releases/download/${SCORECARD_VERSION}/scorecard_5.1.1_linux_amd64.tar.gz" + echo "${SCORECARD_SHA} scorecard.tar.gz" | sha256sum -c - + tar xzf scorecard.tar.gz + rm scorecard.tar.gz sudo mv scorecard-linux-amd64 /usr/local/bin/scorecard chmod +x /usr/local/bin/scorecard From 84c68e1483a3774e59dcd716eddb5a530d3e8f82 Mon Sep 17 00:00:00 2001 From: DJ Date: Sun, 5 Apr 2026 12:07:32 -0700 Subject: [PATCH 8/8] chore: enforce MD041, add standards references to all YAML files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enable MD041 (first line heading) — all markdown files already comply - Add header comment to each workflow YAML with purpose and link to the org standard definition that governs it - Add header comment to dependabot.yml Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/dependabot.yml | 2 ++ .github/workflows/ci.yml | 3 +++ .github/workflows/claude.yml | 2 ++ .github/workflows/compliance-audit.yml | 3 +++ .github/workflows/dependabot-automerge.yml | 5 +++-- .github/workflows/dependency-audit.yml | 5 +++-- .github/workflows/org-scorecard.yml | 3 +++ .github/workflows/sonarcloud.yml | 2 ++ .markdownlint-cli2.yaml | 2 +- 9 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9ad6ccb53..1b267f263 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,5 @@ +# Dependabot configuration — GitHub Actions ecosystem only (docs-only repo). +# Standard: https://github.com/petry-projects/.github/blob/main/standards/dependabot-policy.md version: 2 updates: - package-ecosystem: "github-actions" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe30483b2..eee2f84fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,3 +1,6 @@ +# CI pipeline for the .github standards repository. +# Lints markdown, YAML, GitHub Actions, shell scripts, and runs AgentShield. +# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md name: CI on: diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 53ba88e49..949c35917 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,3 +1,5 @@ +# AI-assisted code review via Claude Code Action on PRs. +# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#4-claude-code-claudeyml name: Claude Code on: diff --git a/.github/workflows/compliance-audit.yml b/.github/workflows/compliance-audit.yml index b43a7cb03..b91156f4e 100644 --- a/.github/workflows/compliance-audit.yml +++ b/.github/workflows/compliance-audit.yml @@ -1,3 +1,6 @@ +# Weekly org-wide compliance audit against standards. +# Checks all repos for required workflows, settings, labels, rulesets, and agent config. +# Standard: https://github.com/petry-projects/.github/tree/main/standards name: Weekly Compliance Audit on: diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 6080c9a41..a5373e0fa 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -1,5 +1,6 @@ -# Dependabot auto-merge workflow -# Copy to .github/workflows/dependabot-automerge.yml +# Dependabot auto-merge workflow. +# Auto-approves and merges eligible patch/minor Dependabot PRs after CI passes. +# Standard: https://github.com/petry-projects/.github/blob/main/standards/dependabot-policy.md#auto-merge-workflow # # Requires repository secrets: # APP_ID — GitHub App ID with contents:write and pull-requests:write diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml index 5a1c53bcc..ac2a0756c 100644 --- a/.github/workflows/dependency-audit.yml +++ b/.github/workflows/dependency-audit.yml @@ -1,5 +1,6 @@ -# Dependency vulnerability audit -# Copy to .github/workflows/dependency-audit.yml +# Dependency vulnerability audit. +# Detects ecosystems and runs appropriate audit tools (npm, pnpm, Go, Rust, Python). +# Standard: https://github.com/petry-projects/.github/blob/main/standards/dependabot-policy.md#vulnerability-audit-ci-check # # Auto-detects ecosystems present in the repository and runs the appropriate # audit tool. Fails the build if any dependency has a known security advisory. diff --git a/.github/workflows/org-scorecard.yml b/.github/workflows/org-scorecard.yml index c19bfcf0d..783f44c77 100644 --- a/.github/workflows/org-scorecard.yml +++ b/.github/workflows/org-scorecard.yml @@ -1,3 +1,6 @@ +# OpenSSF Scorecard security posture review for all public repos. +# Creates/updates/closes GitHub Issues for findings across the org. +# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#openssf-scorecard-org-scorecardyml name: Org Scorecard Review on: diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 4a2cf0c09..8620057ec 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,3 +1,5 @@ +# SonarCloud code quality, maintainability, and security hotspot scanning. +# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#3-sonarcloud-analysis-sonarcloudyml name: SonarCloud Analysis permissions: {} diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 5efbc8b4a..4a34fddcf 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -4,4 +4,4 @@ config: MD032: false # Blanks around lists — many existing docs use compact list formatting MD033: false # Inline HTML — used in standards docs for tables/details MD034: false # Bare URLs — AGENTS.md uses bare URLs for references - MD041: false # First line heading — not all docs start with a heading + MD041: true # First line must be a top-level heading