MENU
  

run script github-repo-config check to validate repo config

rulesets:

Only allow commits from email ending with certain domain
# only allow commits from email ending with:
#  @example.com
#  @users.noreply.github.com (users that have "dont reveal my email" setting), or
#  @github.com (last one is used for github merge/squash commits)
# Note: to block commits with a specific author email domain, set "negate": true in "parameters"
gh api -X POST 'repos/{owner}/{repo}/rulesets' --input - <<'EOF'
{
  "name": "restrict-emails",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "include": ["~DEFAULT_BRANCH"],
      "exclude": []
    }
  },
  "rules": [
    {
      "type": "commit_author_email_pattern",
      "parameters": {
        "operator": "regex",
        "pattern": "(@example\\.com|@users\\.noreply\\.github\\.com|^noreply@github\\.com)$"
      }
    },
    {
      "type": "committer_email_pattern",
      "parameters": {
        "operator": "regex",
        "pattern": "(@example\\.com|@users\\.noreply\\.github\\.com|^noreply@github\\.com)$"
      }
    }
  ]
}
EOF
Protect the default branch: require a PR (approvals 0 or 1) + resolved review threads, block deletion/force-push, require linear history, bypass "pull requests only"
# One ruleset, because "bypass_actors" is per-ruleset: split these rules across
# several rulesets and you have to repeat the bypass config in each one, and a
# bypass on ruleset A does not bypass ruleset B. (GitHub does not force the
# grouping -- several rulesets can target the same branch and they stack, with
# every applicable rule having to pass.)
#
# "required_review_thread_resolution" is a parameter of the "pull_request" rule,
# not a rule of its own -- it is what forces codex review comments to be dealt
# with before merging.
#
# deletion / non_fast_forward / required_linear_history are three separate rule
# objects; bypass_actors is a top-level key, sibling of "rules", not inside it.
# bypass_mode "pull_request" = the actor can only bypass via a PR, never on a
# direct push, so an admin can force-merge a PR but nobody can push to main.
gh api -X POST 'repos/{owner}/{repo}/rulesets' --input - <<'EOF'
{
  "name": "protect-main",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "include": ["~DEFAULT_BRANCH"],
      "exclude": []
    }
  },
  "bypass_actors": [
    {
      "actor_id": 1,
      "actor_type": "OrganizationAdmin",
      "bypass_mode": "pull_request"
    }
  ],
  "rules": [
    {
      "type": "pull_request",
      "parameters": {
        "required_approving_review_count": 0,
        "dismiss_stale_reviews_on_push": false,
        "require_code_owner_review": false,
        "require_last_push_approval": false,
        "required_review_thread_resolution": true
      }
    },
    { "type": "deletion" },
    { "type": "non_fast_forward" },
    { "type": "required_linear_history" }
  ]
}
EOF

# actor ids are not guessable -- read a ruleset that already has the bypass you want:
gh api 'repos/{owner}/{repo}/rulesets' --jq '.[] | {id, name}'
gh api 'repos/{owner}/{repo}/rulesets/RULESET_ID' --jq '.bypass_actors'
Require code scanning results
# own ruleset: blocks merging while CodeQL has unresolved high/critical alerts
gh api -X POST 'repos/{owner}/{repo}/rulesets' --input - <<'EOF'
{
  "name": "require-code-scanning",
  "target": "branch",
  "enforcement": "active",
  "conditions": {
    "ref_name": {
      "include": ["~DEFAULT_BRANCH"],
      "exclude": []
    }
  },
  "rules": [
    {
      "type": "code_scanning",
      "parameters": {
        "code_scanning_tools": [
          {
            "tool": "CodeQL",
            "security_alerts_threshold": "high_or_higher",
            "alerts_threshold": "errors"
          }
        ]
      }
    }
  ]
}
EOF

repo settings:

Allow auto-merge
# enable auto-merge for a repo
gh repo edit OWNER/REPO --enable-auto-merge

# check if auto-merge is enabled
gh repo view --json autoMergeAllowed
Restrict the `prod` environment to deploy from `main` only
# 1) switch the environment to custom branch policies
gh api -X PUT 'repos/{owner}/{repo}/environments/prod' --input - <<'EOF'
{
  "deployment_branch_policy": {
    "protected_branches": false,
    "custom_branch_policies": true
  }
}
EOF

# 2) allow only main (repeat the POST for each extra pattern you want to allow)
gh api -X POST 'repos/{owner}/{repo}/environments/prod/deployment-branch-policies' \
  -f name='main' -f type='branch'

# check what is allowed now
gh api 'repos/{owner}/{repo}/environments/prod/deployment-branch-policies' --jq '.branch_policies[].name'

other: