Skip to content

Commit 7840f13

Browse files
feat: add script to create org-level runner groups (#128)
1 parent 8134391 commit 7840f13

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

gh-cli/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,20 @@ Creates an organization webhook, with a secret, with some help from `jq`
427427

428428
Create a new repo from a repo template - note that it only creates as public or private, if you want internal you have to do a subsequent call (see `change-repository-visibility.sh`)
429429

430+
### create-runner-groups-in-organization.sh
431+
432+
Creates a runner group in an organization with configurable visibility and repository access.
433+
434+
Usage:
435+
436+
```shell
437+
./create-runner-groups-in-organization.sh my-org "Production Runners" all
438+
./create-runner-groups-in-organization.sh my-org "Production Runners" selected --repo-file repos.txt
439+
./create-runner-groups-in-organization.sh my-org "Production Runners" selected --allow-public --repo-file repos.txt
440+
```
441+
442+
The `repos.txt` file should contain one repository name per line. The script will automatically look up repository IDs.
443+
430444
### create-teams-from-list.sh
431445

432446
Loops through a list of teams and creates them.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/bin/bash
2+
3+
# Creates a runner group in an organization
4+
#
5+
# Usage: ./create-runner-groups-in-organization.sh <organization> <runner-group-name> <visibility> [--allow-public] [--repo-file <file>]
6+
#
7+
# Arguments:
8+
# organization - The organization name
9+
# runner-group-name - The name for the runner group
10+
# visibility - The visibility of the runner group (all or selected)
11+
# --allow-public - Optional flag: Allow public repositories to use this runner group
12+
# --repo-file - Optional: File containing repository names (one per line) when visibility is 'selected'
13+
#
14+
# Example: ./create-runner-groups-in-organization.sh my-org "Production Runners" all
15+
# Example: ./create-runner-groups-in-organization.sh my-org "Production Runners" selected --repo-file repos.txt
16+
# Example: ./create-runner-groups-in-organization.sh my-org "Production Runners" selected --allow-public --repo-file repos.txt
17+
#
18+
# Note: When visibility is 'selected', you must provide --repo-file with repository names.
19+
# The file should contain one repository name per line (e.g., "repo1" or "my-org/repo1")
20+
21+
if [ $# -lt 3 ]; then
22+
echo "Usage: $0 <organization> <runner-group-name> <visibility> [--allow-public] [--repo-file <file>]"
23+
echo "Example: ./create-runner-groups-in-organization.sh my-org \"Production Runners\" all"
24+
echo "Example: ./create-runner-groups-in-organization.sh my-org \"Production Runners\" selected --repo-file repos.txt"
25+
exit 1
26+
fi
27+
28+
org="$1"
29+
runner_group_name="$2"
30+
visibility="$3"
31+
allow_public="false"
32+
repos_file=""
33+
34+
# Parse optional arguments
35+
shift 3
36+
while [ $# -gt 0 ]; do
37+
case "$1" in
38+
--allow-public)
39+
allow_public="true"
40+
shift
41+
;;
42+
--repo-file)
43+
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
44+
repos_file="$2"
45+
shift 2
46+
else
47+
echo "❌ Error: --repo-file requires a file path argument"
48+
exit 1
49+
fi
50+
;;
51+
*)
52+
echo "❌ Error: Unknown argument: $1"
53+
exit 1
54+
;;
55+
esac
56+
done
57+
58+
# Validate visibility parameter
59+
if [[ ! "$visibility" =~ ^(all|selected)$ ]]; then
60+
echo "❌ Error: visibility must be one of: all or selected"
61+
exit 1
62+
fi
63+
64+
# Check if repos file is provided when visibility is selected
65+
if [ "$visibility" = "selected" ] && [ -z "$repos_file" ]; then
66+
echo "⚠️ Warning: --repo-file not provided for 'selected' visibility"
67+
echo "The runner group will be created but no repositories will be selected."
68+
echo "You can add repositories later using the GitHub UI or API."
69+
fi
70+
71+
# Check if repos file exists
72+
if [ -n "$repos_file" ] && [ ! -f "$repos_file" ]; then
73+
echo "❌ Error: Repository file not found: $repos_file"
74+
exit 1
75+
fi
76+
77+
echo "Creating runner group '$runner_group_name' in organization: $org"
78+
echo " Visibility: $visibility"
79+
echo " Allow public repositories: $allow_public"
80+
81+
# Build the JSON payload
82+
json_payload=$(jq -n \
83+
--arg name "$runner_group_name" \
84+
--arg visibility "$visibility" \
85+
--argjson allows_public "$allow_public" \
86+
'{name: $name, visibility: $visibility, allows_public_repositories: $allows_public}')
87+
88+
# Add selected repository IDs if provided
89+
if [ -n "$repos_file" ]; then
90+
echo " Looking up repository IDs..."
91+
repo_ids_array=()
92+
public_repos_found=()
93+
94+
while IFS= read -r repo_name || [ -n "$repo_name" ]; do
95+
# Skip empty lines
96+
if [ -z "$repo_name" ]; then
97+
continue
98+
fi
99+
100+
# Remove any leading/trailing whitespace
101+
repo_name=$(echo "$repo_name" | xargs)
102+
103+
# If the repo name includes the org (org/repo), extract just the repo name
104+
if [[ "$repo_name" == *"/"* ]]; then
105+
repo_name=$(echo "$repo_name" | cut -d'/' -f2)
106+
fi
107+
108+
echo " Looking up ID for: $repo_name"
109+
repo_info=$(gh api "/repos/$org/$repo_name" 2>/dev/null)
110+
111+
if [ -z "$repo_info" ]; then
112+
echo " ⚠️ Warning: Could not find repository '$repo_name', skipping..."
113+
continue
114+
fi
115+
116+
repo_id=$(echo "$repo_info" | jq -r '.id')
117+
repo_visibility=$(echo "$repo_info" | jq -r '.visibility')
118+
119+
# Check if repository is public and --allow-public is not set
120+
if [ "$repo_visibility" = "public" ] && [ "$allow_public" = "false" ]; then
121+
public_repos_found+=("$repo_name")
122+
fi
123+
124+
repo_ids_array+=("$repo_id")
125+
done < "$repos_file"
126+
127+
if [ ${#repo_ids_array[@]} -eq 0 ]; then
128+
echo "Error: No valid repository IDs found"
129+
exit 1
130+
fi
131+
132+
echo " Selected repositories: ${#repo_ids_array[@]} repositories"
133+
134+
# Add repository IDs to JSON payload as proper array of numbers
135+
repo_ids_json=$(printf '%s\n' "${repo_ids_array[@]}" | jq -s 'map(tonumber)')
136+
json_payload=$(echo "$json_payload" | jq --argjson ids "$repo_ids_json" '. + {selected_repository_ids: $ids}')
137+
fi
138+
139+
# Create the runner group
140+
gh api \
141+
--method POST \
142+
-H "Accept: application/vnd.github+json" \
143+
-H "X-GitHub-Api-Version: 2022-11-28" \
144+
"/orgs/$org/actions/runner-groups" \
145+
--input - <<< "$json_payload"
146+
147+
# Check if the command was successful
148+
if [ $? -ne 0 ]; then
149+
echo "Failed to create runner group: $runner_group_name"
150+
exit 1
151+
fi
152+
153+
echo "Successfully created runner group: $runner_group_name"
154+
155+
# Show warning about public repositories at the end if any were found
156+
if [ -n "$repos_file" ] && [ ${#public_repos_found[@]} -gt 0 ]; then
157+
echo ""
158+
echo "⚠️ Warning: Found ${#public_repos_found[@]} public repository(ies) but --allow-public flag is not set:"
159+
for public_repo in "${public_repos_found[@]}"; do
160+
echo " - $public_repo"
161+
done
162+
echo "These repositories will be added to the runner group, but the group won't allow public repositories."
163+
echo "Consider using --allow-public flag if you want to allow public repositories to use this runner group."
164+
fi

0 commit comments

Comments
 (0)