Powershell github branch protection rules
Goal: we want to make sure that all repositories has reasonable settings configured (at very minimum we want to protect main branch from deletion)
$ErrorActionPreference = "Stop"
$token = '***********' # GitHub App Password
$query = @"
query GetPrivateRepositoryProtectionRules(`$after: String) {
organization(login:"rabotaua") {
repositories(first:100, privacy: PRIVATE, after: `$after) {
pageInfo {
endCursor
hasNextPage
}
nodes {
name
id
defaultBranchRef {
name
}
branchProtectionRules(first:10) {
nodes {
id
pattern
allowsDeletions
allowsForcePushes
requiredApprovingReviewCount
requiresApprovingReviews
requiresCodeOwnerReviews
requiresStatusChecks
}
}
}
}
}
}
"@
$createRule = @"
mutation CreateRule(`$repositoryId: ID!, `$pattern: String!) {
createBranchProtectionRule(input:{
repositoryId: `$repositoryId
pattern: `$pattern
allowsDeletions: false
allowsForcePushes: false
requiredApprovingReviewCount: 1
requiresApprovingReviews: true
requiresCodeOwnerReviews: true
requiresStatusChecks: true
}) {
branchProtectionRule {
id
pattern
allowsDeletions
allowsForcePushes
requiredApprovingReviewCount
requiresApprovingReviews
requiresCodeOwnerReviews
requiresStatusChecks
}
}
}
"@
$updateRule = @"
mutation UpdateRule(`$ruleId: ID!, `$pattern: String!) {
updateBranchProtectionRule(input:{
branchProtectionRuleId: `$ruleId
pattern: `$pattern
allowsDeletions: false
allowsForcePushes: false
requiredApprovingReviewCount: 1
requiresApprovingReviews: true
requiresCodeOwnerReviews: true
requiresStatusChecks: true
}) {
branchProtectionRule {
id
pattern
allowsDeletions
allowsForcePushes
requiredApprovingReviewCount
requiresApprovingReviews
requiresCodeOwnerReviews
requiresStatusChecks
}
}
}
"@
$variables = @{
after = $null
}
$repositories = @()
do {
$res = Invoke-RestMethod -Method Post https://api.github.com/graphql -Headers @{Authorization = "Bearer $token"} -ContentType 'application/json' -Body (@{ query = $query; variables = $variables } | ConvertTo-Json)
$repositories += $res.data.organization.repositories.nodes
$variables.after = $res.data.organization.repositories.pageInfo.endCursor
} while ($res.data.organization.repositories.pageInfo.hasNextPage)
$repositoriesToSkip = @(
'alliance',
'test-owners'
)
foreach($repository in $repositories) {
$name = $repository.name
if ($repositoriesToSkip.Contains($name)) {
Write-Host $name -ForegroundColor Cyan
continue
}
$defaultBranchName = $repository.defaultBranchRef.name
$rule = $null
foreach($node in $repository.branchProtectionRules.nodes) {
if ($node.pattern -eq $defaultBranchName) {
$rule = $node
break
}
}
if ($rule) {
$shouldUpdate = $false
if ($rule.allowsDeletions) {
$shouldUpdate = $true
}
if ($rule.allowsForcePushes) {
$shouldUpdate = $true
}
if ($rule.requiredApprovingReviewCount -lt 1) {
$shouldUpdate = $true
}
if (-not ($rule.requiresApprovingReviews)) {
$shouldUpdate = $true
}
if (-not ($rule.requiresCodeOwnerReviews)) {
$shouldUpdate = $true
}
if (-not ($rule.requiresStatusChecks)) {
$shouldUpdate = $true
}
if (-not ($shouldUpdate)) {
Write-Host $name -ForegroundColor Cyan
continue
}
$variables = @{
ruleId = $rule.id
pattern = $defaultBranchName
}
$res = Invoke-RestMethod -Method Post https://api.github.com/graphql -Headers @{Authorization = "Bearer $token"} -ContentType 'application/json' -Body (@{ query = $updateRule; variables = $variables } | ConvertTo-Json)
if ($res.errors) {
$res.errors | select -ExpandProperty message
Write-Host $name -ForegroundColor Red
} else {
Write-Host $name -ForegroundColor Yellow
}
} else {
$variables = @{
repositoryId = $repository.id
pattern = $defaultBranchName
}
$res = Invoke-RestMethod -Method Post https://api.github.com/graphql -Headers @{Authorization = "Bearer $token"} -ContentType 'application/json' -Body (@{ query = $createRule; variables = $variables } | ConvertTo-Json)
if ($res.errors) {
$res.errors | select -ExpandProperty message
Write-Host $name -ForegroundColor Red
} else {
Write-Host $name -ForegroundColor Green
}
}
Start-Sleep -Seconds 1
}