Skip to content

Commit b5c651d

Browse files
committed
feat: introduce github-add-repo-permissions script for flexible team permission management
1 parent b547df5 commit b5c651d

4 files changed

Lines changed: 134 additions & 73 deletions

File tree

‎.github/copilot-instructions.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ GIT_URL_PREFIX=${GIT_URL_PREFIX:-'https://github.com'}
3939
**Critical patterns:**
4040
- Use `${VARIABLE:-''}` for all env vars to provide empty defaults
4141
- Set `API_URL_PREFIX` and `GIT_URL_PREFIX` for GitHub Enterprise Server compatibility
42-
- Include `sleep 5` between API calls to avoid rate limits (see `github-add-repo-admin.sh`)
43-
- Use pagination helpers for org-level operations (pattern in `github-add-repo-admin.sh` lines 20-28)
42+
- Include `sleep 5` between API calls to avoid rate limits (see `github-add-repo-permissions.sh` line 99)
43+
- Use pagination helpers for org-level operations (pattern in `github-add-repo-permissions.sh` lines 45-51)
4444
- **ALWAYS validate required variables** with explicit `if [ -z "${VAR}" ]` checks before any API calls
4545
- **ALWAYS validate GITHUB_TOKEN** by calling `/user` endpoint and checking for 200 status code
4646
- Use `curl -s -o /dev/null -w "%{http_code}"` pattern to check HTTP status codes
@@ -62,7 +62,7 @@ done
6262
```
6363

6464
### Rate Limiting
65-
**Always include `sleep 5`** between repository-level operations to stay under GitHub's rate limits. See `github-add-repo-admin.sh` line 40.
65+
**Always include `sleep 5`** between repository-level operations to stay under GitHub's rate limits. See `github-add-repo-permissions.sh` line 99 or `github-repo-from-template.sh` lines 77, 83.
6666

6767
### Authentication Headers
6868
- Use `Bearer` token for enterprise endpoints: `-H "Authorization: Bearer ${GITHUB_TOKEN}"`
@@ -86,10 +86,12 @@ All scripts follow this validation sequence:
8686

8787
## Script-Specific Behaviors
8888

89-
### `github-add-repo-admin`
90-
- Grants team admin permissions to ALL repos in an org
89+
### `github-add-repo-permissions`
90+
- Grants team permissions to ALL repos in an org across 5 permission levels
9191
- Uses pagination + 5-second delay between repos
92-
- Variable: `REPO_ADMIN` is a **team slug** (not team name)
92+
- Variables: `REPO_ADMIN`, `REPO_MAINTAIN`, `REPO_PUSH`, `REPO_TRIAGE`, `REPO_PULL` (all accept **space-separated team slugs**)
93+
- At least one permission level variable must be set
94+
- Loops through teams with helper function `apply_team_permissions()`
9395

9496
### `github-repo-from-template`
9597
- `REPO_ADMIN` and `REPO_WRITE` are **space-separated lists** of team slugs

‎README.md‎

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,30 +89,48 @@ Scripts use environment variables for configuration. Common variables include:
8989

9090
Each script is a self-contained utility designed for a specific task. Navigate to the script's directory, set the required environment variables, and execute.
9191

92-
### Add Repository Admin
92+
### Add Repository Permissions
9393

94-
**Script:** `github-add-repo-admin/github-add-repo-admin.sh`
94+
**Script:** `github-add-repo-permissions/github-add-repo-permissions.sh`
9595

96-
Grants a team admin permissions across all repositories in an organization. Useful for ensuring platform or operations teams have access to all repos.
96+
Grants team permissions across all repositories in an organization. Supports multiple permission levels (admin, maintain, push, triage, pull) and multiple teams per permission level.
9797

9898
**Required variables:**
9999
```bash
100100
export GITHUB_TOKEN="your_token"
101101
export ORG="your-org"
102-
export REPO_ADMIN="platform-team" # Team slug, not display name
102+
103+
# Set one or more permission levels (space-separated team slugs)
104+
export REPO_ADMIN="platform-team ops-team" # Admin permissions
105+
export REPO_MAINTAIN="maintainers" # Maintain permissions
106+
export REPO_PUSH="developers contributors" # Write/push permissions
107+
export REPO_TRIAGE="support-team" # Triage permissions
108+
export REPO_PULL="external-auditors" # Read/pull permissions
103109
```
104110

105111
**Usage:**
106112
```bash
107-
cd github-add-repo-admin
108-
./github-add-repo-admin.sh
113+
cd github-add-repo-permissions
114+
./github-add-repo-permissions.sh
109115
```
110116

111117
**What it does:**
112118
- Retrieves all repositories in the organization (paginated)
113-
- Grants the specified team admin permissions on each repository
119+
- Grants permissions to specified teams based on permission level
120+
- Supports multiple teams per permission level (space-separated)
121+
- Processes all five GitHub permission levels: admin, maintain, push, triage, pull
114122
- Includes 5-second delays between repos to avoid rate limits
115123

124+
**Permission levels:**
125+
- `admin` - Full repository access including settings and team management
126+
- `maintain` - Repository management without admin privileges
127+
- `push` - Read and write access to code
128+
- `triage` - Read access plus ability to manage issues and pull requests
129+
- `pull` - Read-only access to code
130+
131+
> [!NOTE]
132+
> At least one permission level must be set. Team slugs should be lowercase and hyphenated (e.g., "Platform Team" → `platform-team`).
133+
116134
---
117135

118136
### Create Repository from Template

‎github-add-repo-admin/github-add-repo-admin.sh‎

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/usr/bin/env /bin/bash
2+
set -euo pipefail
3+
4+
### GLOBAL VARIABLES
5+
GITHUB_TOKEN=${GITHUB_TOKEN:-''}
6+
ORG=${ORG:-''}
7+
API_URL_PREFIX=${API_URL_PREFIX:-'https://api.github.com'}
8+
GIT_URL_PREFIX=${GIT_URL_PREFIX:-'https://github.com'}
9+
10+
# Permission-specific team variables (space-separated team slugs)
11+
REPO_ADMIN=${REPO_ADMIN:-''}
12+
REPO_MAINTAIN=${REPO_MAINTAIN:-''}
13+
REPO_PUSH=${REPO_PUSH:-''}
14+
REPO_TRIAGE=${REPO_TRIAGE:-''}
15+
REPO_PULL=${REPO_PULL:-''}
16+
17+
# Check if GITHUB_TOKEN is set
18+
if [ -z "${GITHUB_TOKEN}" ]; then
19+
echo "GITHUB_TOKEN is empty. Please set your token and try again"
20+
exit 1
21+
fi
22+
23+
# Check if ORG is set
24+
if [ -z "${ORG}" ]; then
25+
echo "ORG is empty. Please set your organization and try again"
26+
exit 1
27+
fi
28+
29+
# Check if at least one permission level is set
30+
if [ -z "${REPO_ADMIN}" ] && [ -z "${REPO_MAINTAIN}" ] && [ -z "${REPO_PUSH}" ] && [ -z "${REPO_TRIAGE}" ] && [ -z "${REPO_PULL}" ]; then
31+
echo "Error: At least one permission level must be set."
32+
echo "Available variables: REPO_ADMIN, REPO_MAINTAIN, REPO_PUSH, REPO_TRIAGE, REPO_PULL"
33+
exit 1
34+
fi
35+
36+
# Validate GITHUB_TOKEN by calling GitHub API
37+
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/user")
38+
39+
if [ "${RESPONSE}" -ne 200 ]; then
40+
echo "Error: GITHUB_TOKEN is invalid or does not have required permissions."
41+
exit 1
42+
fi
43+
44+
get_repo_pagination () {
45+
repo_pages=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" -I "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100" | grep -Eo '&page=[0-9]+' | grep -Eo '[0-9]+' | tail -1;)
46+
echo "${repo_pages:-1}"
47+
}
48+
49+
limit_repo_pagination () {
50+
seq "$(get_repo_pagination)"
51+
}
52+
53+
apply_team_permissions () {
54+
local REPO_NAME=$1
55+
local PERMISSION=$2
56+
local TEAM_SLUGS=$3
57+
58+
# Loop through space-separated team slugs
59+
for TEAM in ${TEAM_SLUGS}; do
60+
echo " Granting ${PERMISSION} permission to team ${TEAM}"
61+
curl -s -X PUT -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" "${API_URL_PREFIX}/orgs/${ORG}/teams/${TEAM}/repos/${ORG}/${REPO_NAME}" -d "{\"permission\":\"${PERMISSION}\"}"
62+
done
63+
}
64+
65+
process_repos () {
66+
for PAGE in $(limit_repo_pagination); do
67+
for REPO in $(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/orgs/${ORG}/repos?page=${PAGE}&per_page=100&sort=full_name" | jq -r 'sort_by(.name) | .[] | .name'); do
68+
echo "Processing repo ${REPO}"
69+
70+
# Apply admin permissions
71+
if [ -n "${REPO_ADMIN}" ]; then
72+
apply_team_permissions "${REPO}" "admin" "${REPO_ADMIN}"
73+
fi
74+
75+
# Apply maintain permissions
76+
if [ -n "${REPO_MAINTAIN}" ]; then
77+
apply_team_permissions "${REPO}" "maintain" "${REPO_MAINTAIN}"
78+
fi
79+
80+
# Apply push (write) permissions
81+
if [ -n "${REPO_PUSH}" ]; then
82+
apply_team_permissions "${REPO}" "push" "${REPO_PUSH}"
83+
fi
84+
85+
# Apply triage permissions
86+
if [ -n "${REPO_TRIAGE}" ]; then
87+
apply_team_permissions "${REPO}" "triage" "${REPO_TRIAGE}"
88+
fi
89+
90+
# Apply pull (read) permissions
91+
if [ -n "${REPO_PULL}" ]; then
92+
apply_team_permissions "${REPO}" "pull" "${REPO_PULL}"
93+
fi
94+
95+
# Add delay to prevent hitting GitHub rate limit
96+
sleep 5
97+
done
98+
done
99+
}
100+
101+
process_repos

0 commit comments

Comments
 (0)