Last active
April 9, 2024 12:10
-
-
Save jdschleicher/e8aeec2f6092ecfc098f715a5b0aa2b8 to your computer and use it in GitHub Desktop.
POWERSHELL SETUP AND SCRIPT FOR BRANCH PROTECTIONS AS CODE IN GITHUB REPOSITORIES
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
PREREQUISITES: | |
1. GITHUB CLI IS INSTALLED IN YOUR MACHINE (https://cli.github.com/) | |
2. GITHUB CLI IS AUTHENTICATED (https://cli.github.com/manual/gh_auth_login) USING THE WEB BROWSER LOGIN IS REALLY SIMPLE IF ALREADY LOGGED IN BROWSER | |
3. KNOW YOUR GITHUB USERNAME OR GITHUB ORGANIZATION NAME | |
4. IF USING BYPASS ADMIN TEAM IN EXAMPLE BELOW, GET TEAM SLUG BY GOING TO TEAM AND GRABBING SLUG NAME FROM URL | |
DOCUMENTATION FOR CREATING BRANCH PROTECTOIN WITH GRAPH QL: https://docs.github.com/en/graphql/reference/mutations#createbranchprotectionrule | |
IF NEEDING TO VALIDATE "Branch Protections as Code" GROUNDWORK IS HERE: https://stackoverflow.com/questions/76384359/github-graphql-api-branch-protection-rule-how-do-i-get-require-a-pull-reque/76422520#76422520 | |
Where the dollar-sign character isn't ignored with a backtick, this will be a dynamic powershell variable that will be | |
updated as the repository and branch protections are looped and iterated over | |
#> | |
function get_expected_branch_protection_pattern_to_json_configurations { | |
$branch_protection_configurations = @{ | |
"feature*" = @" | |
{ | |
"query": "mutation (`$input: CreateBranchProtectionRuleInput!) { createBranchProtectionRule(input: `$input) { branchProtectionRule { id } } }", | |
"variables": { | |
"input": { | |
"repositoryId": "$repository_id", | |
"pattern": "$pattern", | |
"requiresApprovingReviews": true, | |
"dismissesStaleReviews": true, | |
"requiredApprovingReviewCount": 2 | |
} | |
} | |
} | |
"@ | |
"master" = @" | |
{ | |
"query": "mutation (`$input: CreateBranchProtectionRuleInput!) { createBranchProtectionRule(input: `$input) { branchProtectionRule { id } } }", | |
"variables": { | |
"input": { | |
"repositoryId" : "$repository_id", | |
"pattern" : "$pattern", | |
"requiredStatusCheckContexts" : ["pull-request-job-that-runs-on-opening-pull-request"], | |
"requiresApprovingReviews" : true, | |
"requiredApprovingReviewCount" : 1, | |
"allowsForcePushes" : false, | |
"dismissesStaleReviews" : true, | |
"requiresStatusChecks" : true, | |
"bypassPullRequestActorIds" : [ "$expected_team_admin_id" ] | |
} | |
} | |
} | |
"@ | |
} | |
$branch_protection_configurations | |
} | |
function get_expected_github_team_slug_id_by_team_name { | |
param( | |
[Parameter(Mandatory=$true)] | |
$github_team_slug, | |
[Parameter(Mandatory=$true)] | |
$github_organization | |
) | |
$gh_team_slug_response_json = gh api ` | |
-H "Accept: application/vnd.github+json" ` | |
-H "X-GitHub-Api-Version: 2022-11-28" ` | |
/orgs/$github_organization/teams/$github_team_slug | |
$gh_team_slug_response = $gh_team_slug_response_json | ConvertFrom-Json -Depth 12 | |
$gh_team_slug_response.node_id | |
} | |
$expected_github_organization = "ENTERPRISE_ORGANIZATION_OR_GITHUB_USERNAME" | |
### NEEDS TO BE IN STRUCTURE GITHUB_ORGANIZATION/REPOSITORY | |
$customer_repositories = @( | |
"$expected_github_organization/test-repo-1", | |
"$expected_github_organization/test-repo-2", | |
"$expected_github_organization/test-repo-3" | |
) | |
### IF NOT USING TEAM SLUG REMOVE FROM ABOVE JSON | |
$expected_github_slug_name = "EXPECTED_GITHUB_TEAM_SLUG_WOULDNT_WORK_ON_PERSONAL_REPOSITORIES" | |
$expected_team_admin_id = get_expected_github_team_slug_id_by_team_name -github_team_slug $expected_github_slug_name -github_organization $expected_github_organization | |
$branch_protection_configurations = get_expected_branch_protection_pattern_to_json_configurations | |
foreach ( $full_repository_name in $customer_repositories ) { | |
Write-Host "REPOSITORY TO PROCESS: $full_repository_name" | |
$github_repository = $full_repository_name.split("/")[1] | |
$repository_id = $( gh api graphql -f github_organization="$expected_github_organization" -F name="$github_repository" -f query=' | |
query($name: String!, $github_organization: String!) { | |
repository(owner: $github_organization, name: $name) { | |
id | |
} | |
} | |
' -q .data.repository.id) | |
Write-Host "REPOSITORY ID : $repository_id" | |
$pattern = $null | |
foreach ( $branch_protection_key in $branch_protection_configurations.Keys ) { | |
$pattern = $branch_protection_key | |
Write-Host "BRANCH PROTECTION TO PROCESS: $branch_protection_key" | |
$branch_protection_mutation_json = $branch_protection_configurations[$branch_protection_key] | |
$branch_protection_mutation = $branch_protection_mutation_json | ConvertFrom-Json -Depth 10 | |
$branch_protection_mutation.variables.input.pattern = $pattern | |
$branch_protection_mutation.variables.input.repositoryId = $repository_id | |
### ENSURE DEPTH IS SET HIGH SO ANY ARRAYS ARE CONVERTED CORRECTLY FROM POWERSHELL; | |
### THERE COULD BE ONLY 1 EXPECTED VALUE FOR bypassPullRequestActorIds SO PIPING WILL IMPLICITLY CONVERT THAT ARRAY WITH 1 ITEM INTO A STRING VALUE | |
$updated_branch_protection_mutation_json = ConvertTo-Json -Depth 10 $branch_protection_mutation | |
$json_branch_configurations_file = "branch_configurations.json" | |
if (Test-Path $json_branch_configurations_file ) { | |
Remove-Item -Force $json_branch_configurations_file | |
} | |
New-Item $json_branch_configurations_file -Value $updated_branch_protection_mutation_json | |
gh api graphql --input $json_branch_configurations_file | |
### UNCOMMENT IF YOU WANT TO IMMEDIATELY DELETE THE FILE FOLLOWING GH API CALL | |
# Remove-Item -Force $json_branch_configurations_file | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment