diff --git a/.yamllint.yml b/.yamllint.yml index 51de3c0..d352ac5 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -1,5 +1,10 @@ extends: default rules: - line-length: {max: 160} + # Bumped to 280 to accommodate single-line brace-expansion targets in + # checkpoints.yaml (e.g. GH-02 enumerates ~24 LICENSE filenames). The + # checkpoint runner does not unfold YAML folded scalars (`>-`) for + # `target:` fields — its parser is bash regex, not a real YAML parser — + # so these long enumerations cannot be wrapped. + line-length: {max: 360} truthy: {allowed-values: ['true', 'false', 'on']} document-start: disable diff --git a/skills/github-project/checkpoints.yaml b/skills/github-project/checkpoints.yaml index 7baf069..458ce4b 100644 --- a/skills/github-project/checkpoints.yaml +++ b/skills/github-project/checkpoints.yaml @@ -12,35 +12,49 @@ mechanical: severity: error desc: "README.md must exist" + # Brace expansion covers SPDX-style split-license filenames common in + # multi-licence skill repos plus the GNU COPYING convention. Single line + # because the runner's YAML parser doesn't unfold `>-` folded scalars + # for `target:` fields (it's bash regex, not a real YAML parser). - id: GH-02 type: file_exists - target: LICENSE + target: "{LICENSE,LICENSE.md,LICENSE.txt,COPYING,COPYING.md,COPYING.txt,LICENSE-MIT,LICENSE-APACHE,LICENSE-APACHE-2.0,LICENSE-BSD,LICENSE-BSD-2-Clause,LICENSE-BSD-3-Clause,LICENSE-CC-BY-SA-4.0,LICENSE-CC0-1.0,LICENSE-GPL,LICENSE-GPL-2.0,LICENSE-GPL-3.0,LICENSE-LGPL,LICENSE-LGPL-3.0,LICENSE-AGPL,LICENSE-AGPL-3.0,LICENSE-MPL,LICENSE-MPL-2.0}" severity: error - desc: "LICENSE file must exist" + desc: >- + LICENSE file must exist. Accepts split-license naming (LICENSE-MIT, + LICENSE-CC-BY-SA-4.0, etc.) and the GNU COPYING convention. - id: GH-03 type: file_exists - target: SECURITY.md - severity: warning - desc: "SECURITY.md should exist for vulnerability reporting" + target: "{SECURITY.md,.github/SECURITY.md,docs/SECURITY.md}" + severity: info + desc: "SECURITY.md should exist for vulnerability reporting (satisfied org-wide if the GitHub owner has a `.github` repo providing these files)" - id: GH-04 type: file_exists - target: CONTRIBUTING.md - severity: warning - desc: "CONTRIBUTING.md should exist" + target: "{CONTRIBUTING.md,.github/CONTRIBUTING.md,docs/CONTRIBUTING.md}" + severity: info + desc: "CONTRIBUTING.md should exist (satisfied org-wide if the GitHub owner has a `.github` repo providing these files)" - id: GH-05 type: file_exists - target: .github/CODEOWNERS + target: "{.github/CODEOWNERS,CODEOWNERS,docs/CODEOWNERS}" severity: warning - desc: "CODEOWNERS should exist for review assignments" + desc: >- + CODEOWNERS must exist in the repository itself for GitHub to assign + reviewers (only `.github/`, root, or `docs/` on the default branch are + recognised). Org-wide `.github` does NOT satisfy CODEOWNERS — that + mechanism only provides templates and community-health files, not + review-routing rules. - id: GH-06 - type: command - target: "test -f .github/dependabot.yml || test -f renovate.json || test -f .github/renovate.json || test -f .renovaterc.json || test -f .renovaterc" + type: file_exists + target: "{.github/dependabot.yml,.github/dependabot.yaml,renovate.json,renovate.json5,.github/renovate.json,.github/renovate.json5,.renovaterc.json,.renovaterc.json5,.renovaterc}" severity: warning - desc: "Dependency update automation should be configured (Dependabot or Renovate)" + desc: >- + Dependency update automation should be configured (Dependabot or + Renovate). Accepts both .yml/.yaml for Dependabot and .json/.json5 + for Renovate. - id: GH-07 type: file_exists @@ -88,14 +102,14 @@ mechanical: # === DEPENDABOT CHECKS === - id: GH-13 type: regex - target: .github/dependabot.yml + target: "{.github/dependabot.yml,.github/dependabot.yaml}" pattern: 'package-ecosystem:\s*"?(composer|gomod|npm|pip)"?' severity: warning desc: "Dependabot should monitor language-specific dependencies (composer, gomod, npm, pip)" - id: GH-14 type: regex - target: .github/dependabot.yml + target: "{.github/dependabot.yml,.github/dependabot.yaml}" pattern: 'package-ecosystem:\s*"?github-actions"?' severity: warning desc: "Dependabot should monitor GitHub Actions" @@ -120,14 +134,14 @@ mechanical: # pattern as ER-19/20 in enterprise-readiness-skill PR #55. - id: GH-19 type: regex - target: .github/workflows/*.yml + target: ".github/workflows/*.{yml,yaml}" pattern: 'uses:[[:space:]]*github/codeql-action|uses:[[:space:]]*netresearch/[.]github/[.]github/workflows/codeql[.]yml' severity: warning desc: "CodeQL must be wired up (dedicated workflow or reusable-workflow job calling github/codeql-action or netresearch/.github/...codeql.yml)" - id: GH-20 type: regex - target: .github/workflows/*.yml + target: ".github/workflows/*.{yml,yaml}" pattern: 'uses:[[:space:]]*ossf/scorecard-action|uses:[[:space:]]*netresearch/[.]github/[.]github/workflows/scorecard[.]yml' severity: info desc: >- @@ -137,15 +151,15 @@ mechanical: # === SLSA PROVENANCE === - id: GH-21 type: regex_not - target: .github/workflows/*.yml + target: ".github/workflows/*.{yml,yaml}" pattern: 'slsa-framework/slsa-github-generator' severity: warning desc: "Should migrate from slsa-github-generator to actions/attest-build-provenance (cannot be SHA-pinned)" # === AUTO-MERGE WORKFLOW CHECKS === - id: GH-23 - type: command - target: "test -f .github/workflows/auto-merge-deps.yml || test -f .github/workflows/auto-merge.yml" + type: file_exists + target: "{.github/workflows/auto-merge-deps.yml,.github/workflows/auto-merge.yml}" severity: warning desc: "Auto-merge workflow should exist for Dependabot/Renovate PRs" @@ -167,28 +181,28 @@ mechanical: # contain the netresearch/... substring identically). - id: GH-24 type: regex - target: .github/workflows/auto-merge*.yml + target: ".github/workflows/auto-merge*.{yml,yaml}" pattern: 'netresearch/\.github/\.github/workflows/auto-merge-deps\.yml|on:[[:space:]]*\n[[:space:]]*pull_request_target:' severity: error desc: "Auto-merge workflow must delegate to netresearch reusable workflow OR use pull_request_target trigger for bot PR write permissions" - id: GH-25 type: regex - target: .github/workflows/auto-merge*.yml + target: ".github/workflows/auto-merge*.{yml,yaml}" pattern: 'netresearch/\.github/\.github/workflows/auto-merge-deps\.yml|github\.event\.pull_request\.user\.login' severity: warning desc: "Auto-merge should delegate to reusable workflow OR check github.event.pull_request.user.login (not github.actor which changes on reruns)" - id: GH-26 type: regex - target: .github/workflows/auto-merge*.yml + target: ".github/workflows/auto-merge*.{yml,yaml}" pattern: 'netresearch/\.github/\.github/workflows/auto-merge-deps\.yml|--auto' severity: warning desc: "Auto-merge should delegate to reusable workflow OR use gh pr merge --auto to respect branch protection and merge queues" - id: GH-27 type: regex - target: .github/workflows/auto-merge*.yml + target: ".github/workflows/auto-merge*.{yml,yaml}" pattern: 'netresearch/\.github/\.github/workflows/auto-merge-deps\.yml|gh api.*repos/\$' severity: info desc: "Auto-merge should delegate to reusable workflow OR dynamically detect merge strategy from repo settings" @@ -212,52 +226,30 @@ mechanical: reviewDecision) # === BRANCH PROTECTION AUDIT === + # GH-30 / GH-31 require GitHub API access (gh CLI + auth) which is outside + # the assessment runner's command allowlist. Declared as type: gh_api so the + # runner skips them with a clear evidence string instead of rejecting them. + # The semantically equivalent LLM-driven audit lives in GH-32 (llm_reviews). - id: GH-30 - type: command - target: | - # Require GitHub CLI and a valid GitHub remote slug - command -v gh >/dev/null 2>&1 || exit 1 - REPO_SLUG=$(git config --get remote.origin.url 2>/dev/null | sed -E 's|.*github\.com[:/](.+/[^.]+)(\.git)?$|\1|') - test -n "$REPO_SLUG" || exit 1 - DEFAULT_BRANCH=$(gh api "repos/$REPO_SLUG" --jq '.default_branch' 2>/dev/null || echo 'main') - RESULT=$(gh api "repos/$REPO_SLUG/branches/$DEFAULT_BRANCH/protection" --jq '.enforce_admins.enabled' 2>/dev/null || echo '') - if [ "$RESULT" = "true" ]; then - exit 0 - fi - # Fallback: check rulesets when classic branch protection is not configured - RULESET_OK=$(gh api "repos/$REPO_SLUG/rulesets" \ - --jq 'map(select(.enforcement == "active" and .target == "branch" and ((.bypass_actors // []) | length == 0))) | any' \ - 2>/dev/null || echo 'false') - test "$RULESET_OK" = "true" + type: gh_api + endpoint: "repos/{owner}/{repo}/branches/{default_branch}/protection" + json_path: ".enforce_admins.enabled" severity: error - desc: "enforce_admins must be true on default branch — without it admins can bypass required status checks and review requirements" + desc: >- + enforce_admins must be true on default branch — without it admins can + bypass required status checks and review requirements (audited by + GH-32 llm_review; runner skips because gh API access requires auth). - id: GH-31 - type: command - target: | - # Require GitHub CLI and a valid GitHub remote slug - command -v gh >/dev/null 2>&1 || exit 1 - REPO_SLUG=$(git config --get remote.origin.url 2>/dev/null | sed -E 's|.*github\.com[:/](.+/[^.]+)(\.git)?$|\1|') - test -n "$REPO_SLUG" || exit 1 - DEFAULT_BRANCH=$(gh api "repos/$REPO_SLUG" --jq '.default_branch' 2>/dev/null || echo 'main') - RESULT=$(gh api "repos/$REPO_SLUG/branches/$DEFAULT_BRANCH/protection" --jq '.required_conversation_resolution.enabled' 2>/dev/null || echo '') - if [ "$RESULT" = "true" ]; then - exit 0 - fi - # Fallback: check rulesets for review thread resolution - JQ_FILTER='map(select( - .enforcement == "active" and .target == "branch" - and ((.bypass_actors // []) | length == 0) - and any(.rules[]?; - .type == "pull_request" and - (.parameters.required_review_thread_resolution // false) - ) - )) | any' - RULESET_OK=$(gh api "repos/$REPO_SLUG/rulesets" \ - --jq "$JQ_FILTER" 2>/dev/null || echo 'false') - test "$RULESET_OK" = "true" + type: gh_api + endpoint: "repos/{owner}/{repo}/branches/{default_branch}/protection" + json_path: ".required_conversation_resolution.enabled" severity: error - desc: "required_conversation_resolution must be enabled — combined with enforce_admins, ensures unresolved review threads block ALL merges including admins" + desc: >- + required_conversation_resolution must be enabled — combined with + enforce_admins, ensures unresolved review threads block ALL merges + including admins (audited by GH-32 llm_review; runner skips because + gh API access requires auth). llm_reviews: # === BRANCH PROTECTION + MERGE QUEUE COMPATIBILITY ===