Skip to content

feat: add composite actions, enhanced governance, and GitHub Issue reports#7

Merged
gamaware merged 9 commits intomainfrom
feat/enhanced-governance
Mar 11, 2026
Merged

feat: add composite actions, enhanced governance, and GitHub Issue reports#7
gamaware merged 9 commits intomainfrom
feat/enhanced-governance

Conversation

@gamaware
Copy link
Owner

@gamaware gamaware commented Mar 10, 2026

Summary

  • Add 3 composite actions (security-scan, sync-settings, update-pre-commit-composite) matching the pattern from system-design-course
  • Replace SMTP email delivery with GitHub Issues — drift creates/closes issues with settings-drift label, new repos use new-repo label. GitHub sends email notifications natively
  • Enhance sync script with: label standardization, default branch validation, Dependabot vulnerability alerts, repository metadata checks (description/topics)
  • Add vulnerability_alerts and labels sections to config/baseline.json
  • Refactor all 4 workflows to use composite actions
  • Add baseline JSON schema validation to quality checks
  • Comprehensive README documenting the purpose of every enforced setting

Test plan

  • Run ./scripts/sync-repo-settings.sh --dry-run locally to verify all checks
  • Trigger workflow manually with --dry-run to validate CI
  • Verify composite actions are referenced correctly
  • Confirm JSON schema validation catches missing sections
  • Verify GitHub Issue creation/closure logic

Summary by CodeRabbit

  • New Features

    • Consolidated security scan combining static analysis and vulnerability scanning with SARIF upload
    • Repository settings sync with drift detection, issue-based reporting, new-repo discovery, and optional remediation
    • Automated pre-commit hook update workflow
    • Enforcement additions: vulnerability alerts, standard labels, default-branch checks, and repository metadata validations
  • Documentation

    • Expanded docs, runbooks, onboarding guides, and user-facing skill/reference pages; updated README and contribution/security guidance

…ports

- Add composite actions: security-scan, sync-settings, update-pre-commit
- Replace SMTP email with GitHub Issues for drift reporting
- Add label sync, default branch check, vulnerability alerts, metadata checks
- Enhance README with detailed purpose for every enforced setting
- Add baseline schema validation to quality checks
- Refactor workflows to use composite actions
Copilot AI review requested due to automatic review settings March 10, 2026 06:16
@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

📝 Walkthrough

Walkthrough

Adds three composite GitHub Actions and refactors workflows to use them; extends baseline with vulnerability_alerts and labels; enhances the sync script with label, vulnerability, default-branch and metadata checks; updates CI/workflow behaviors and adds extensive docs, runbooks, and CLAUDE skills.

Changes

Cohort / File(s) Summary
Composite Actions
/.github/actions/security-scan/action.yml, /.github/actions/sync-settings/action.yml, /.github/actions/update-pre-commit-composite/action.yml
New composite actions: security-scan (Semgrep + Trivy SARIF upload), sync-settings (runs sync script, wires outputs), update-pre-commit-composite (updates pre-commit and creates PR).
Workflows (refactored to use composites)
/.github/workflows/security.yml, /.github/workflows/update-pre-commit-hooks.yml, /.github/workflows/quality-checks.yml, /.github/workflows/sync-settings.yml
Workflows now invoke local composites, add top-level permissions/concurrency, persist-credentials handling, baseline schema validation, job-summary reporting, artifact uploads, and updated drift/new-repo issue lifecycle.
Sync engine scripts & config
config/baseline.json, scripts/sync-repo-settings.sh
Baseline adds vulnerability_alerts and labels; sync script extended with sync_vulnerability_alerts, sync_labels, check_default_branch, check_repo_metadata, preserved status-check contexts, increased repo listing limit, and richer per-repo drift reporting.
Docs & Runbooks
README.md, CLAUDE.md, docs/architecture.md, docs/runbooks/*, .claude/skills/*
Large documentation and runbook additions plus CLAUDE skill docs describing new flows, onboarding, drift handling, and skills to manage overrides/exclusions.
Utilities & CI tooling
zizmor.yml, scripts/generate-report.sh, /.github/PULL_REQUEST_TEMPLATE.md
Minor CI/tooling tweaks: added secrets-outside-env ignore entries, removed drift_count extraction, added PR template and small script adjustments.
Metadata / Timestamps
.secrets.baseline
Updated generated_at timestamp only.

Sequence Diagram(s)

sequenceDiagram
    participant Workflow as "Workflow"
    participant Composite as "sync-settings Action"
    participant Script as "sync-repo-settings.sh"
    participant GitHub as "GitHub API"
    participant IssueMgr as "Issue Manager"

    Workflow->>Composite: invoke(mode, github_token)
    Composite->>Script: run sync (dry-run/apply)
    Script->>GitHub: list repos / fetch repo state
    GitHub-->>Script: repo list & current state
    Script->>Script: compute effective config (baseline + overrides)
    Script->>Script: detect drift (labels, vuln alerts, branch, metadata, etc.)
    alt apply mode
        Script->>GitHub: PATCH/PUT to apply fixes
        GitHub-->>Script: apply responses
    end
    Script-->>Composite: emit report + outputs
    Composite-->>Workflow: return report, outputs
    Workflow->>IssueMgr: if drift -> close old issues, create new drift issue
    IssueMgr->>GitHub: create/close issue(s)
    Workflow->>GitHub: upload artifact & update job summary
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibble at drift and tidy each tree,

Scanning and syncing so repos stay free,
Labels aligned, alerts turned bright,
PRs and runbooks stitched through the night,
A happy rabbit cheering CI harmony.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and specifically summarizes the main objectives: adding composite actions, enhancing governance features, and replacing email-based drift notifications with GitHub Issue reports.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/enhanced-governance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds composite actions, replaces SMTP email delivery with GitHub Issues for drift reporting, and enhances the sync script with several new checks (vulnerability alerts, label standardization, default branch validation, and repository metadata checks). The baseline JSON is extended with vulnerability_alerts and labels sections, workflows are refactored to use composite actions, and a baseline schema validation step is added to quality checks. The README is comprehensively rewritten to document every enforced setting.

Changes:

  • Add 3 composite actions (security-scan, sync-settings, update-pre-commit-composite) and refactor all workflows to use them; replace SMTP email delivery with GitHub Issue creation/closure for drift and new-repo reporting
  • Extend sync-repo-settings.sh with new functions: sync_vulnerability_alerts, sync_labels, check_default_branch, check_repo_metadata; add corresponding vulnerability_alerts and labels sections to config/baseline.json
  • Rewrite README.md with detailed documentation of every enforced setting, add baseline JSON schema validation to quality-checks.yml, and update CLAUDE.md with composite action and sync details

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/sync-repo-settings.sh Adds 4 new functions for vuln alerts, labels, default branch, and metadata checks; integrates them into main loop
config/baseline.json Adds vulnerability_alerts under security and a new labels array with 6 standard labels
README.md Complete rewrite with detailed settings tables, architecture diagram, and composite action documentation
CLAUDE.md Updates overview to mention composite actions, GitHub Issues, and details on sync script enforcement
.secrets.baseline Updated timestamp only
.github/workflows/sync-settings.yml Refactored to use sync-settings composite action; adds GitHub Issue creation/closure for drift and new repos
.github/workflows/security.yml Refactored to use security-scan composite action; adds actions: read permission
.github/workflows/quality-checks.yml Adds baseline JSON schema validation step
.github/workflows/update-pre-commit-hooks.yml Refactored to use update-pre-commit-composite action
.github/actions/sync-settings/action.yml New composite action wrapping sync script and report parsing
.github/actions/security-scan/action.yml New composite action wrapping Semgrep and Trivy scans
.github/actions/update-pre-commit-composite/action.yml New composite action wrapping pre-commit autoupdate and PR creation

Comment on lines +328 to +343
echo "$effective" | jq -c '.labels[]' 2>/dev/null | while read -r label_json; do
local name color description
name=$(echo "$label_json" | jq -r '.name')
color=$(echo "$label_json" | jq -r '.color')
description=$(echo "$label_json" | jq -r '.description')

if ! echo "$current_labels" | grep -qx "$name"; then
changes="${changes}- Label missing: \`$name\`\n"
if [ "$MODE" = "--apply" ]; then
gh api -X POST "repos/$OWNER/$repo/labels" \
-f name="$name" -f color="$color" -f description="$description" \
> /dev/null 2>&1 || log "WARN: Could not create label $name for $repo"
fi
fi
done
echo -e "$changes"
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The while read loop runs in a subshell because it's on the right side of a pipe (jq ... | while read). Any modifications to the changes variable inside the loop are lost when the subshell exits, so echo -e "$changes" on line 343 will always output an empty string. This means label drift is never reported.

Use process substitution instead of a pipe to keep the loop in the current shell, e.g., while read -r label_json; do ... done < <(echo "$effective" | jq -c '.labels[]' 2>/dev/null). This matches the pattern used elsewhere in the script where variables are accumulated.

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +40
run: |
./scripts/sync-repo-settings.sh "${{ inputs.mode }}"
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

${{ inputs.mode }} is directly interpolated into the run: shell command, which is a script injection risk. Although the current caller constrains this input to a choice type with fixed values, the composite action itself doesn't validate the input. If reused by another workflow that passes untrusted input, this could allow arbitrary command execution.

Pass the input via an environment variable instead (e.g., env: MODE: ${{ inputs.mode }}), and reference it as "$MODE" in the script. This follows the recommended GitHub Actions security practice of never using ${{ }} expressions directly in run: blocks.

Copilot uses AI. Check for mistakes.
Comment on lines +369 to +395
# Check for missing description and topics
check_repo_metadata() {
local repo="$1"
local changes=""

local current
current=$(gh api "repos/$OWNER/$repo" 2>/dev/null) || return

local description
description=$(echo "$current" | jq -r '.description // ""')
if [ -z "$description" ] || [ "$description" = "null" ]; then
changes="${changes}- **Missing repository description**\n"
fi

local topics
topics=$(echo "$current" | jq -r '.topics | length')
if [ "$topics" = "0" ]; then
changes="${changes}- **No repository topics configured**\n"
fi

if [ -n "$changes" ]; then
log "WARN: metadata gaps for $repo"
else
log "OK: metadata for $repo"
fi
echo -e "$changes"
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_default_branch, sync_repo_settings, sync_security, sync_vulnerability_alerts, and check_repo_metadata each independently call gh api "repos/$OWNER/$repo", resulting in at least 4-5 API calls to the same endpoint per repository per sync run. With the GitHub API rate limit of 5,000 requests/hour for authenticated users, this could become a problem as the number of repositories grows.

Consider fetching the repo data once at the start of the per-repo loop in main() and passing it to each function, or caching the result in a temporary variable.

Copilot uses AI. Check for mistakes.
color=$(echo "$label_json" | jq -r '.color')
description=$(echo "$label_json" | jq -r '.description')

if ! echo "$current_labels" | grep -qx "$name"; then
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grep -qx "$name" interprets $name as a regex pattern. If a label name ever contains regex metacharacters (e.g., ., +, *), this check could produce false matches or miss real matches. Consider using grep -qxF "$name" to treat the name as a fixed string.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/quality-checks.yml:
- Around line 82-96: The current "Validate baseline schema" shell block only
checks top-level section presence; update it to validate required nested keys
and label fields using jq so missing subkeys fail CI. Replace or extend the loop
that iterates sections (repo_settings, security, branch_protection, labels,
required_files) with explicit jq checks such as verifying
.security.vulnerability_alerts exists and is an object, and that .labels is an
array with every element containing .color and .description (e.g. fail if any
.labels[] | select(.color==null or .description==null) is found); ensure ERRORS
increments and the script exits non‑zero with a clear message when any nested
validation fails.

In @.github/workflows/sync-settings.yml:
- Around line 62-95: Change the "Create or update drift issue" flow to create
the new issue before closing old ones and only retire older "settings-drift"
issues if the gh issue create command (in the step named "Create or update drift
issue") succeeds; use the gh issue create output (or check its exit status) to
conditionally run the close loop that calls gh issue close, and ensure the
"Close drift issue if compliant" step still only runs when
steps.sync.outputs.has_drift == 'false'. Also add a workflow-level concurrency
key (e.g., concurrency: group: settings-drift-sync-${{ github.ref }} or a static
group) to serialize runs and avoid races between overlapping executions.
- Around line 18-20: Remove the unused ambient write permission by deleting the
"issues: write" entry from the permissions block (leave "contents: read"
intact); this ensures the workflow only requests least-privilege scopes since
the workflow uses secrets.ORG_SETTINGS_PAT for mutating calls rather than
GITHUB_TOKEN.
- Around line 117-119: The gh repo list invocation that populates NEW_REPOS
currently uses a hard cap (--limit 200) which can omit repos; update the command
used to set NEW_REPOS (the gh repo list ... --jq ... assignment) by removing the
--limit 200 flag entirely (or replace it with --limit 1000 if you want a
practical upper bound) so all non-archived repositories created after $WEEK_AGO
are returned.

In `@scripts/sync-repo-settings.sh`:
- Around line 347-358: The check_default_branch function currently hardcodes
"main" as the expected branch; update it to call get_effective_settings "$repo"
and read the expected branch from the resulting .branch_protection.branch value
(e.g., store it in a variable like expected_branch), then use that variable
instead of the literal "main" for both the comparison that sets changes and the
gh api -X PATCH default_branch payload (respecting MODE). Also adjust the
warning/log text to reference expected_branch and ensure you handle the case
when .branch_protection.branch is empty by falling back to a sensible default or
skipping the patch.
- Around line 334-340: The script only checks for presence of a label name via
current_labels/grep and never verifies color/description, so update logic should
compare existing label attributes and patch mismatches: when iterating each
desired label (variables name, color, description) fetch the existing label data
(using gh api repos/$OWNER/$repo/labels/$name or from the current_labels JSON),
parse its color and description, and if either differs then treat it as a change
and, when MODE="--apply", call gh api -X PATCH "repos/$OWNER/$repo/labels/$name"
with -f color and -f description to update the label; keep the existing create
branch if label missing, and reuse log for warnings (log function) when PATCH
fails.
- Around line 145-148: The current check uses want_alerts=$(echo "$effective" |
jq -r '.security.vulnerability_alerts // false') and returns whenever
want_alerts != "true", which skips explicit false overrides; change the logic so
the script only returns when the vulnerability_alerts field is absent/unset, not
when it's explicitly false. Use jq to detect presence (e.g., extract a sentinel
like null when the key is missing or use has("vulnerability_alerts") on
.security) and then: if the field is missing return, otherwise continue and
handle "true" vs "false" values from want_alerts; update the conditional around
the early return (the block using want_alerts, effective, and the return)
accordingly.
- Around line 328-343: The problem is the piped while loop reading jq output
runs in a subshell so updates to the shared variable "changes" are lost; to fix
it, initialize changes before the loop and replace the piped construct with a
same-shell loop (e.g., use process substitution: while read -r label_json; do
... done < <(echo "$effective" | jq -c '.labels[]' 2>/dev/null) or a
here-string) so modifications to "changes" persist, keeping the existing logic
that uses "name", "color", "description", "current_labels", "MODE" and the gh
api call unchanged. Ensure "changes" is declared/initialized in the parent shell
scope before the loop so echo -e "$changes" later prints the collected changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 86c5a0b7-4895-475a-8e4d-0be533841e06

📥 Commits

Reviewing files that changed from the base of the PR and between c464a06 and b239275.

📒 Files selected for processing (12)
  • .github/actions/security-scan/action.yml
  • .github/actions/sync-settings/action.yml
  • .github/actions/update-pre-commit-composite/action.yml
  • .github/workflows/quality-checks.yml
  • .github/workflows/security.yml
  • .github/workflows/sync-settings.yml
  • .github/workflows/update-pre-commit-hooks.yml
  • .secrets.baseline
  • CLAUDE.md
  • README.md
  • config/baseline.json
  • scripts/sync-repo-settings.sh

Comment on lines +82 to +96
- name: Validate baseline schema
run: |
ERRORS=0
for section in repo_settings security branch_protection labels required_files; do
if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
echo "ERROR: Missing section '$section' in baseline.json"
ERRORS=$((ERRORS + 1))
else
echo "OK: section '$section' present"
fi
done
if [ "$ERRORS" -gt 0 ]; then
echo "ERROR: baseline.json schema validation failed"
exit 1
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This only checks section presence, not the baseline schema.

Lines 85-91 will still pass if security.vulnerability_alerts is missing or if a label entry omits color/description. That leaves the new baseline contract effectively unvalidated.

Suggested jq-based validation
       - name: Validate baseline schema
         run: |
-          ERRORS=0
-          for section in repo_settings security branch_protection labels required_files; do
-            if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
-              echo "ERROR: Missing section '$section' in baseline.json"
-              ERRORS=$((ERRORS + 1))
-            else
-              echo "OK: section '$section' present"
-            fi
-          done
-          if [ "$ERRORS" -gt 0 ]; then
-            echo "ERROR: baseline.json schema validation failed"
-            exit 1
-          fi
+          jq -e '
+            (.repo_settings | type == "object") and
+            (.security | type == "object") and
+            (.security.vulnerability_alerts | type == "boolean") and
+            (.branch_protection | type == "object") and
+            (.required_files | type == "array") and
+            (.labels | type == "array") and
+            ([.labels[] | (.name | type == "string")
+                         and (.color | type == "string" and test("^[0-9A-Fa-f]{6}$"))
+                         and (.description | type == "string")] | all)
+          ' config/baseline.json >/dev/null || {
+            echo "ERROR: baseline.json schema validation failed"
+            exit 1
+          }

As per coding guidelines, "Confirm the JSON structure aligns with the baseline schema validated by quality checks (Validate baseline schema step) and used by per-repo drift/metadata checks."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Validate baseline schema
run: |
ERRORS=0
for section in repo_settings security branch_protection labels required_files; do
if ! jq -e ".$section" config/baseline.json > /dev/null 2>&1; then
echo "ERROR: Missing section '$section' in baseline.json"
ERRORS=$((ERRORS + 1))
else
echo "OK: section '$section' present"
fi
done
if [ "$ERRORS" -gt 0 ]; then
echo "ERROR: baseline.json schema validation failed"
exit 1
fi
- name: Validate baseline schema
run: |
jq -e '
(.repo_settings | type == "object") and
(.security | type == "object") and
(.security.vulnerability_alerts | type == "boolean") and
(.branch_protection | type == "object") and
(.required_files | type == "array") and
(.labels | type == "array") and
([.labels[] | (.name | type == "string")
and (.color | type == "string" and test("^[0-9A-Fa-f]{6}$"))
and (.description | type == "string")] | all)
' config/baseline.json >/dev/null || {
echo "ERROR: baseline.json schema validation failed"
exit 1
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/quality-checks.yml around lines 82 - 96, The current
"Validate baseline schema" shell block only checks top-level section presence;
update it to validate required nested keys and label fields using jq so missing
subkeys fail CI. Replace or extend the loop that iterates sections
(repo_settings, security, branch_protection, labels, required_files) with
explicit jq checks such as verifying .security.vulnerability_alerts exists and
is an object, and that .labels is an array with every element containing .color
and .description (e.g. fail if any .labels[] | select(.color==null or
.description==null) is found); ensure ERRORS increments and the script exits
non‑zero with a clear message when any nested validation fails.

Comment on lines +334 to +340
if ! echo "$current_labels" | grep -qx "$name"; then
changes="${changes}- Label missing: \`$name\`\n"
if [ "$MODE" = "--apply" ]; then
gh api -X POST "repos/$OWNER/$repo/labels" \
-f name="$name" -f color="$color" -f description="$description" \
> /dev/null 2>&1 || log "WARN: Could not create label $name for $repo"
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Existing labels are never standardized.

This only checks whether the label name exists. If bug is already present with the wrong color or description, it is treated as compliant and never patched, so the new baseline cannot actually standardize labels.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sync-repo-settings.sh` around lines 334 - 340, The script only checks
for presence of a label name via current_labels/grep and never verifies
color/description, so update logic should compare existing label attributes and
patch mismatches: when iterating each desired label (variables name, color,
description) fetch the existing label data (using gh api
repos/$OWNER/$repo/labels/$name or from the current_labels JSON), parse its
color and description, and if either differs then treat it as a change and, when
MODE="--apply", call gh api -X PATCH "repos/$OWNER/$repo/labels/$name" with -f
color and -f description to update the label; keep the existing create branch if
label missing, and reuse log for warnings (log function) when PATCH fails.

- Add docs/architecture.md with system context and component overview
- Add docs/runbooks/ with 4 operational procedures (exclude repo,
  add setting, handle drift, onboard repo)
- Add 3 Claude Code skills: /audit, /add-repo-override, /exclude-repo
- Update README and CLAUDE.md with new structure
… sync

The PUT endpoint replaces all branch protection. Without this fix,
repos without an explicit override in overrides.json would have their
status check contexts wiped to an empty array. Now the script reads
the current contexts from the repo and carries them forward.
Review fixes (CodeRabbit + Copilot):
- Fix sync_labels subshell scoping: use process substitution instead
  of piped while loop so changes variable propagates correctly
- Use grep -qxF instead of grep -qx to avoid regex interpretation
- Read default branch from config instead of hardcoding "main"
- Handle explicit false overrides for vulnerability_alerts
- Remove unused issues: write permission from workflow
- Create drift issue before closing old ones to prevent gap
- Add concurrency group to prevent parallel sync runs
- Raise repo list limit from 200 to 1000
- Add label field structure validation to baseline schema check

Dependabot bumps absorbed:
- actions/checkout v4.2.2 -> v6.0.2
- actions/setup-python v5.6.0 -> v6.2.0
- actions/upload-artifact v4.6.2 -> v7.0.0
- github/codeql-action v3.28.18 -> v4.32.6
- peter-evans/create-pull-request v7.0.8 -> v8.1.0

Lint fixes:
- Fix MD032 (blanks around lists) in skill SKILL.md
- Fix MD029 (ordered list prefix) in audit SKILL.md
- Fix MD041 (first line heading) in copilot-instructions and PR template
- Fix MD034 (bare URLs) in CONTRIBUTING.md and SECURITY.md
- Remove unused drift_count variable (shellcheck SC2034)
- markdownlint-cli2-action: update SHA to match v22.0.0 tag
- action-yamllint: update SHA to match v3.1.1 tag
- trivy-action: update from v0.31.0 to v0.35.0 with correct SHA
- zizmor: update from v1.5.0 to v1.23.1, use find for binary extraction
- Use env var instead of inline ${{ inputs.mode }} in sync-settings
  composite action to prevent shell injection (Semgrep finding)
- Add if: always() to Trivy step so it runs regardless of Semgrep
  result, preventing missing SARIF upload
- Set zizmor secrets-outside-env to info level (acceptable for
  single-admin personal account without GitHub Environments)
@github-advanced-security
Copy link

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
.github/actions/security-scan/action.yml (1)

13-16: Consider uploading Semgrep SARIF results.

Semgrep SAST results are not uploaded to GitHub Security. If Semgrep is configured to output SARIF (it can with additional options), you could upload those results alongside Trivy for consolidated security findings in the GitHub Security tab.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/actions/security-scan/action.yml around lines 13 - 16, Update the
"Run Semgrep SAST" step (uses: semgrep/semgrep-action@713efdd...) to output
SARIF (e.g., enable SARIF/format and write to a known file path) and add a
follow-up step (e.g., "Upload Semgrep SARIF") that uses the GitHub SARIF
uploader (github/codeql-action/upload-sarif@v2 or equivalent) to upload the
produced SARIF file; ensure the uploader step references the same SARIF file
path so Semgrep results appear in the GitHub Security tab.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/sync-settings.yml:
- Around line 90-99: The gh issue create step can fail and the script will still
proceed to close existing drift issues; fix this by capturing the output/exit
status of the create command (replace the bare `gh issue create --title "$TITLE"
--body "$BODY" --label "settings-drift"` with a form that assigns its
output/JSON to a variable and checks its exit code), ensure you extract the new
issue number into LATEST only if creation succeeded, log an error and exit
non-zero if creation failed, and only run the subsequent gh issue list / while
loop when LATEST is valid; reference the `gh issue create` invocation, the
`LATEST` assignment, and the closing loop to implement these checks.

In `@zizmor.yml`:
- Line 6: The rule entry "secrets-outside-env" in zizmor.yml is using an
unsupported bare value ("info"); update the configuration to use only allowed
per-rule options. Replace the line with a valid mapping such as
"secrets-outside-env: {}" or "secrets-outside-env: { disable: false }" (or
remove the line entirely) so the "secrets-outside-env" rule uses a supported
config shape instead of the invalid string.

---

Nitpick comments:
In @.github/actions/security-scan/action.yml:
- Around line 13-16: Update the "Run Semgrep SAST" step (uses:
semgrep/semgrep-action@713efdd...) to output SARIF (e.g., enable SARIF/format
and write to a known file path) and add a follow-up step (e.g., "Upload Semgrep
SARIF") that uses the GitHub SARIF uploader
(github/codeql-action/upload-sarif@v2 or equivalent) to upload the produced
SARIF file; ensure the uploader step references the same SARIF file path so
Semgrep results appear in the GitHub Security tab.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b892857f-0467-4896-a971-d47da98fd372

📥 Commits

Reviewing files that changed from the base of the PR and between b239275 and 6570f77.

📒 Files selected for processing (26)
  • .claude/skills/add-repo-override/SKILL.md
  • .claude/skills/audit/SKILL.md
  • .claude/skills/exclude-repo/SKILL.md
  • .github/PULL_REQUEST_TEMPLATE.md
  • .github/actions/security-scan/action.yml
  • .github/actions/sync-settings/action.yml
  • .github/actions/update-pre-commit-composite/action.yml
  • .github/copilot-instructions.md
  • .github/workflows/quality-checks.yml
  • .github/workflows/security.yml
  • .github/workflows/sync-settings.yml
  • .github/workflows/update-pre-commit-hooks.yml
  • .secrets.baseline
  • CLAUDE.md
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
  • docs/architecture.md
  • docs/runbooks/README.md
  • docs/runbooks/add-setting.md
  • docs/runbooks/exclude-repo.md
  • docs/runbooks/handle-drift.md
  • docs/runbooks/onboard-repo.md
  • scripts/generate-report.sh
  • scripts/sync-repo-settings.sh
  • zizmor.yml
💤 Files with no reviewable changes (1)
  • scripts/generate-report.sh
✅ Files skipped from review due to trivial changes (7)
  • .github/copilot-instructions.md
  • .claude/skills/audit/SKILL.md
  • docs/runbooks/onboard-repo.md
  • docs/runbooks/add-setting.md
  • docs/runbooks/README.md
  • docs/runbooks/handle-drift.md
  • SECURITY.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/actions/sync-settings/action.yml
  • .secrets.baseline

Use ignore directive with specific workflow files instead of
unsupported severity level string. These workflows use secrets
without GitHub Environments, which is acceptable for a single-admin
personal account.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@zizmor.yml`:
- Around line 7-9: The ignore entries currently suppress all findings for the
entire workflows by listing "sync-settings.yml" and
"update-pre-commit-hooks.yml"; update these to line-specific ignores by
replacing those entries with filename:line (e.g., "sync-settings.yml:40" and
"update-pre-commit-hooks.yml:25") so only the known acceptable lines are ignored
while preserving checks for other lines; locate the "ignore:" block in
zizmor.yml and change the two list items accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 78f9bb91-09a9-44d0-bc35-5081ce5f0d66

📥 Commits

Reviewing files that changed from the base of the PR and between 6570f77 and 86835e7.

📒 Files selected for processing (1)
  • zizmor.yml

zizmor.yml Outdated
Comment on lines +7 to +9
ignore:
- sync-settings.yml
- update-pre-commit-hooks.yml
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the exact lines that currently reference secrets in the ignored workflows,
# so the file-wide ignores can be replaced with line-scoped ignores.

fd '^(sync-settings|update-pre-commit-hooks)\.yml$' .github/workflows -x sh -c '
  file="$1"
  echo "== $file =="
  rg -n -C2 "\$\{\{\s*secrets\." "$file" || true
' sh {}

Repository: gamaware/github-org-settings

Length of output: 165


🏁 Script executed:

cat -n .github/workflows/sync-settings.yml

Repository: gamaware/github-org-settings

Length of output: 7095


🏁 Script executed:

cat -n .github/workflows/update-pre-commit-hooks.yml

Repository: gamaware/github-org-settings

Length of output: 797


🌐 Web query:

zizmor rules.secrets-outside-env.ignore syntax line column documentation

💡 Result:

zizmor lets you ignore the secrets-outside-env audit either inline (comment) or via config (zizmor.yml).

1) Inline ignore comment

Put a YAML comment in the span that triggers the finding:

# zizmor: ignore[secrets-outside-env]

You can also add a trailing explanation, and you can ignore multiple audits with commas. The comment must be a real YAML comment (not inside a string/block literal). [1]

2) zizmor.yml config: rules.secrets-outside-env.ignore

Use:

rules:
  secrets-outside-env:
    ignore:
      - workflow.yml          # ignore entire file
      - workflow.yml:123      # ignore line 123 (any column)
      - workflow.yml:123:45   # ignore line 123, column 45

The ignore entry format is:

filename.yml[:line[:column]]

line and column are optional and 1-based. [1][2]


Scope these ignores to specific lines, not entire workflows.

The current ignores suppress all secrets-outside-env findings in these workflow files, both present and future. Use line-specific ignores instead:

ignore:
  - sync-settings.yml:40
  - update-pre-commit-hooks.yml:25

This targets only the specific lines passing secrets to composite actions (which is acceptable), while preserving visibility into any other potential issues in these workflows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@zizmor.yml` around lines 7 - 9, The ignore entries currently suppress all
findings for the entire workflows by listing "sync-settings.yml" and
"update-pre-commit-hooks.yml"; update these to line-specific ignores by
replacing those entries with filename:line (e.g., "sync-settings.yml:40" and
"update-pre-commit-hooks.yml:25") so only the known acceptable lines are ignored
while preserving checks for other lines; locate the "ignore:" block in
zizmor.yml and change the two list items accordingly.

…eation

- Scope zizmor secrets-outside-env ignore to line-level where practical
  (update-pre-commit-hooks.yml:25), file-level for sync-settings.yml
  (5 secret usages make line-level unmaintainable)
- Add error handling for gh issue create in drift detection step to
  prevent closing existing issues when creation fails
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/sync-settings.yml (1)

157-166: Consider deduplicating new-repo issues to avoid noise.

Unlike the drift issue logic which closes superseded issues, this step creates a new issue on each run when new repos exist. If the workflow triggers multiple times within the 7-day window (e.g., manual dispatches), it may create duplicate issues for the same repositories.

Consider either:

  1. Closing previous new-repo issues before creating a new one (similar to drift logic)
  2. Checking if a recent new-repo issue already exists before creating
♻️ Optional: Add deduplication similar to drift issues
       - name: Create issue for new repos
         if: steps.newrepos.outputs.has_new == 'true'
         env:
           GH_TOKEN: ${{ secrets.ORG_SETTINGS_PAT }}
         run: |
           BODY=$(cat reports/new-repos.md)
-          gh issue create \
+          if ! gh issue create \
             --title "chore: new repositories discovered — $(date '+%Y-%m-%d')" \
             --body "$BODY" \
-            --label "new-repo"
+            --label "new-repo"; then
+            echo "::error::Failed to create new-repo issue"
+            exit 1
+          fi
+
+          # Close previous new-repo issues
+          LATEST=$(gh issue list --label "new-repo" --state open --json number --jq '.[0].number')
+          gh issue list --label "new-repo" --state open --json number --jq '.[].number' | while read -r num; do
+            if [ "$num" != "$LATEST" ]; then
+              gh issue close "$num" --comment "Superseded by new discovery run."
+            fi
+          done
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/sync-settings.yml around lines 157 - 166, The "Create
issue for new repos" step currently always runs when
steps.newrepos.outputs.has_new == 'true' and calls gh issue create with label
"new-repo", causing duplicates; modify that step to first query existing open
issues labeled "new-repo" (using gh issue list --label "new-repo" --state open
--json createdAt,body,number or equivalent) and either (a) skip creating a new
issue if a recent one (e.g., within 7 days) exists that covers the same repos,
or (b) close/mark as superseded any prior "new-repo" issues (gh issue close
<number>) before running gh issue create; keep the conditional on
steps.newrepos.outputs.has_new and the GH_TOKEN env intact and ensure the
check/close logic runs in the same step before the gh issue create command.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/sync-settings.yml:
- Around line 157-166: The "Create issue for new repos" step currently always
runs when steps.newrepos.outputs.has_new == 'true' and calls gh issue create
with label "new-repo", causing duplicates; modify that step to first query
existing open issues labeled "new-repo" (using gh issue list --label "new-repo"
--state open --json createdAt,body,number or equivalent) and either (a) skip
creating a new issue if a recent one (e.g., within 7 days) exists that covers
the same repos, or (b) close/mark as superseded any prior "new-repo" issues (gh
issue close <number>) before running gh issue create; keep the conditional on
steps.newrepos.outputs.has_new and the GH_TOKEN env intact and ensure the
check/close logic runs in the same step before the gh issue create command.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 05a7924a-b119-46b3-8940-caec878c945e

📥 Commits

Reviewing files that changed from the base of the PR and between 86835e7 and 60cf8e3.

📒 Files selected for processing (2)
  • .github/workflows/sync-settings.yml
  • zizmor.yml

@gamaware gamaware merged commit 74ee2cc into main Mar 11, 2026
8 checks passed
@gamaware gamaware deleted the feat/enhanced-governance branch March 11, 2026 04:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants