Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defined **once** here and applied to **every** repo automatically.
| `github_organization_ruleset.org_branch_protection` | Quality gate on every default branch: required signatures, linear history, branch name pattern, strict Conventional Commits regex, PR thread resolution. **No bypass** — applies to everyone including org admins. |
| `github_organization_ruleset.org_review_gate` | Review gate on every default branch: 1 approving review + CODEOWNER review on PRs. **OrganizationAdmin bypass in `pull_request` mode** so admins can merge their own PRs; bots and other contributors must obtain the review. |
| `github_organization_ruleset.markdown_lint` | Requires the markdownlint workflow in the org's `.github` repo to pass on every ref of every repo. Single source of truth: the workflow + `.markdownlint-cli2.yaml` both live in `.github`. `do_not_enforce_on_create` so brand-new repos don't fail before their default branch exists. |
| `scripts/enable-codeql-default-setup.sh` | Idempotently enables free CodeQL default-setup on every public org repo with a supported language. Run on first apply and again whenever a new public repo joins. **Not** a Terraform resource (provider gap); script-managed until `integrations/terraform-provider-github` ships a `github_repository_code_scanning_default_setup` or equivalent. |

Imports needed on first apply (declared in `rulesets.tf` via `import`
blocks, executed automatically by `tofu apply`):
Expand All @@ -31,7 +32,11 @@ After successful apply, the `import` blocks can be removed in a follow-up
PR (they're idempotent but only useful once).

Next up (separate PRs): org Actions permissions, org-level settings, org
variables, per-repo labels and LICENSE files via `for_each`.
variables, per-repo labels and LICENSE files via `for_each`. Code scanning
default-setup is currently script-managed
(`scripts/enable-codeql-default-setup.sh`) pending a
`github_repository_*` resource in `integrations/terraform-provider-github`;
swap to the resource when upstream ships one.

## Layout

Expand Down
71 changes: 71 additions & 0 deletions scripts/enable-codeql-default-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
#
# Idempotently enable free CodeQL default-setup on every public org repo
# with a supported language.
#
# Code scanning default setup is FREE on public repos (no GHAS license
# consumed). Private repos are never targeted — they require GHAS Code
# Security ($30/committer/month, AGENTS.md "Cost policy") which the
# org policy keeps off as an org default. Per-repo opt-in on cost-approved
# private repos can be added later by extending the filter; not in scope
# here.
#
# Workaround for a provider gap: integrations/terraform-provider-github
# does not expose a resource for
# PUT /repos/{owner}/{repo}/code-scanning/default-setup. When upstream
# ships one this script becomes a one-shot import-style helper and the
# .tf takes over; until then, run after every new public repo joins.
#
# Idempotent: already-configured repos no-op; repos with no supported
# language are skipped. Re-running prints "skip" for every repo and
# touches nothing.
#
# Requires: gh (authenticated with admin:org or administration:write on
# the org — the ORG_ADMIN tier per AGENTS.md "Applying"), jq.

set -euo pipefail

OWNER="dryvist"
enabled=0
already_on=0
skipped_no_lang=0
errors=0

# --visibility public is the safety belt: a private repo name can never
# reach the PUT call. Belt-and-suspenders: GHAS isn't enabled at the org
# default level, so even an accidental PUT against a private repo would
# 403 — no charge can land.
while IFS= read -r name; do
body=$(gh api "repos/$OWNER/$name/code-scanning/default-setup" 2>/dev/null || echo "")
if [[ -z "$body" ]]; then
echo " ERROR $name (GET failed)"
errors=$((errors + 1))
continue
fi

state=$(printf %s "$body" | jq -r '.state // ""')
langs=$(printf %s "$body" | jq -r '.languages // [] | join(",")')

if [[ "$state" == "configured" ]]; then
echo " skip $name (already on)"
already_on=$((already_on + 1))
continue
fi

if [[ -z "$langs" ]]; then
echo " skip $name (no supported language)"
skipped_no_lang=$((skipped_no_lang + 1))
continue
fi

if gh api --method PUT "repos/$OWNER/$name/code-scanning/default-setup" -f state=configured > /dev/null 2>&1; then
echo " ON $name (langs: $langs)"
enabled=$((enabled + 1))
else
echo " ERROR $name (PUT failed)"
errors=$((errors + 1))
fi
done < <(gh repo list "$OWNER" --no-archived --visibility public --limit 100 --json name --jq '.[].name' | sort)

echo
echo "enabled: $enabled · already-on: $already_on · skipped-no-lang: $skipped_no_lang · errors: $errors"