'Can I add a new branch protection rule via GitHub API?
I know you can update an existing branch protection rule via the API, but I cannot find any references in the v3 API docs WRT creating a new rule. For example, if I want to add a rule to a repo that matches a new branch prefixed with "dev_", I have to add it through the GUI, using the "Apply rule to" field, then I can use the API to update those rule settings. Ideally, I'd like to have a hook that does this automatically if a new branch is introduced to the repo, but does not match an existing rule. I should be able to create that rule through the API. Is there a way to do this?
Solution 1:[1]
It appears that GitHub has decided to add new features via the GraphQL API only, not via the REST API. So while this is impossible with the REST API, you can do it using the GraphQL createBranchProtectionRule mutation.
In case it helps anyone, I wrote a script to do this, using GitHub's gh CLI:
#!/bin/bash
set -ue
err() { echo 1>&2 "$*"; }
die() { err "ERROR: $*"; exit 1; }
mustBool() {
[[ "${1#*=}" = "true" || "${1#*=}" = "false" ]] ||
die "bad boolean property value: $1"
}
mustInt() {
[[ "${1#*=}" =~ [0-9]+ ]] ||
die "bad integer property value: $1"
}
[ $# -ge 4 ] || {
err "usage: $0 HOSTNAME ORG REPO PATTERN [PROPERTIES...]"
err " where PROPERTIES can be:"
err " dismissesStaleReviews=true|false"
err " requiresApprovingReviewCount=INTEGER"
err " requiresApprovingReviews=true|false"
err " requiresCodeOwnerReviews=true|false"
err " restrictPushes=true|false"
exit 1
}
hostname="$1"
org="$2"
repo="$3"
pattern="$4"
shift 4
repoNodeId="$(gh api --hostname "$hostname" "repos/$org/$repo" --jq .node_id)"
[[ -n "$repoNodeId" ]] || die "could not determine repo nodeId"
graphql="
mutation createBranchProtectionRule {
createBranchProtectionRule(input: {
repositoryId: \"$repoNodeId\"
pattern: \"$pattern\""
seen=()
requiredStatusCheckContexts=()
for property in "$@"; do
for eSeen in "${seen[@]:-}"; do
[[ "${eSeen%%=*}" = "${property%%=*}" ]] &&
# Allow duplication of multivalued properties
[[ "${eSeen%%=*}" != "requiredStatusCheckContexts" ]] &&
die "Duplicate property: $property"
done
seen+=("${property}")
case "$property" in
requiredStatusCheckContexts=*)
requiredStatusCheckContexts+=("${property#*=}")
;;
\
allowsDeletions=* | \
allowsForcePushes=* | \
dismissesStaleReviews=* | \
isAdminEnforced=* | \
requiresApprovingReviews=* | \
requiresCodeOwnerReviews=* | \
requiresCommitSignatures=* | \
requiresLinearHistory=* | \
requiresStatusChecks=* | \
requiresStrictStatusChecks=* | \
restrictPushes=* | \
restrictsPushes=* | \
restrictsReviewDismissals=* \
)
mustBool "$property"
graphql="$graphql
${property%%=*}: ${property#*=}"
;;
requiredApprovingReviewCount=*)
mustInt "$property"
graphql="$graphql
${property%%=*}: ${property#*=}"
;;
*)
die "unknown property: $property"
esac
done
if [ -n "${requiredStatusCheckContexts[*]:-}" ]; then
graphql="$graphql
requiredStatusCheckContexts: [
"
i=0
for context in "${requiredStatusCheckContexts[@]}"; do
[ $i -ne 0 ] && graphql="$graphql,
"
i=$((1+$i))
graphql="$graphql"$'\t\t\t'"\"$context\""
done
graphql="$graphql
]
"
fi
graphql="$graphql
}) {
branchProtectionRule {
allowsDeletions
allowsForcePushes
creator { login }
databaseId
dismissesStaleReviews
isAdminEnforced
pattern
repository { nameWithOwner }
requiredApprovingReviewCount
requiresApprovingReviews
requiredStatusCheckContexts
requiresCodeOwnerReviews
requiresCommitSignatures
requiresLinearHistory
requiresStatusChecks
requiresStrictStatusChecks
restrictsPushes
restrictsReviewDismissals
}
clientMutationId
}
}"
gh api --hostname "$hostname" graphql -F "query=$graphql" ||
die "GraphQL update failed: $graphql"
echo ""
echo "SUCCESS: Branch protection rule successfully created"
Here is an example of invoking it it:
./createBranchProtectionRule.sh github.example.com skissane my-repo v* requiresApprovingReviews=true requiresCodeOwnerReviews=true requiredApprovingReviewCount=1 requiresStatusChecks=true requiresStrictStatusChecks=false requiredStatusCheckContexts=continuous-integration/jenkins/pr-merge requiresLinearHistory=true
which produces the following output:
{
"data": {
"createBranchProtectionRule": {
"branchProtectionRule": {
"allowsDeletions": false,
"allowsForcePushes": false,
"creator": {
"login": "skissane"
},
"databaseId": 1729,
"dismissesStaleReviews": false,
"isAdminEnforced": false,
"pattern": "v*",
"repository": {
"nameWithOwner": "skissane/my-repo"
}
"requiredApprovingReviewCount": 1,
"requiresApprovingReviews": true,
"requiredStatusCheckContexts": [
"continuous-integration/jenkins/pr-merge"
],
"requiresCodeOwnerReviews": true,
"requiresCommitSignatures": false,
"requiresLinearHistory": true,
"requiresStatusChecks": true,
"requiresStrictStatusChecks": false,
"restrictsPushes": false,
"restrictsReviewDismissals": false
},
"clientMutationId": null
}
}
}
SUCCESS: Branch protection rule successfully created
Solution 2:[2]
This did it for me for GitHub Enterprise.
curl --location --request PUT 'https://github.company.com/api/v3/repos/ORG/REPO-NAME/branches/BRANCH-NAME/protection' \
--header 'Accept: application/vnd.github.v3+json' \
--header 'Accept: application/vnd.github.luke-cage-preview+json' \
--header 'Authorization: Basic .................... \
--header 'Content-Type: text/plain' \
--data-raw '{
"required_status_checks": {
"strict": false,
"contexts": ["continuous-integration/jenkins/BRANCH-NAME"]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"dismissal_restrictions": {},
"dismiss_stale_reviews": true,
"require_code_owner_reviews": true,
"required_approving_review_count": 1
},
"restrictions": {
"users": ["users"],
"teams": ["teams"],
"apps": ["apps"]
}
}
'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Filip Nikolov |
