-
-
Save davidrleonard/bbdfa12f984af8d3ead0841946bb4670 to your computer and use it in GitHub Desktop.
/* | |
* Adds a team to all the repos in a Github organization. This is a tedious | |
* process in the UI. You'll need a newer version of node to run this (e.g 9+) | |
* because it uses async/await. | |
* | |
* Instructions: | |
* | |
* 1. Copy this file somewhere on your computer, e.g. ~/addteamrepos.js | |
* 2. Fill in the uppercase variables below with the right values | |
* 3. Run this file: `$ node ~/addteamrepos.js` | |
*/ | |
const GITHUB_ORG = 'xxxxx'; /* Name of the github organization the team is under and the repos are in */ | |
const GITHUB_ACCESS_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxx'; /* Create an access token here: https://github.com/settings/tokens */ | |
const TEAM_ID = '11111111'; /* Github team ID, not the same as the name, get it from the API */ | |
const TEAM_PERMISSION = 'push'; /* 'pull' or 'push' or 'admin' */ | |
const { exec } = require('child_process'); | |
function execPromise(command) { | |
return new Promise((resolve, reject) => { | |
exec(command, (err, stdout, stderr) => { | |
if (err) { | |
return reject(err); | |
} | |
resolve([stdout, stderr]); | |
}); | |
}); | |
} | |
async function fetchReposPage(org, page) { | |
const [response] = await execPromise( | |
`curl -i -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/${org}/repos?page=${page}` | |
); | |
const nextPageRe = /Link\: \<.+page\=([0-9])\>\; rel\=\"next\"/g; | |
const nextPageMatch = nextPageRe.exec(response); | |
const nextPage = nextPageMatch ? nextPageMatch[1] : null; | |
const repos = JSON.parse(response.slice(response.indexOf('['))); | |
return [repos, nextPage]; | |
} | |
async function fetchRepos(org) { | |
let repos = []; | |
let page = 1; | |
while (page) { | |
let [currentRepos, nextPage] = await fetchReposPage(org, page); | |
repos = [...repos, ...currentRepos]; | |
page = nextPage; | |
} | |
return repos; | |
} | |
async function addTeamToRepo(teamId, org, repo, permission) { | |
const [out,err] = await execPromise( | |
`curl -X PUT -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" -d '{"permission":"${permission}"}' https://api.github.com/teams/${teamId}/repos/${org}/${repo}` | |
); | |
console.log(`... Added team "${teamId}" to repo "${org}/${repo}" with permission "${permission}"`); | |
} | |
(async () => { | |
/* Fetch all repos names for org */ | |
console.log(`Fetching repos from organization "${GITHUB_ORG}"`); | |
const repos = await fetchRepos(GITHUB_ORG); | |
const repoNames = repos.map(r => r.name); | |
console.log(`... Found ${repoNames.length} repos`) | |
/* Add team to each repo */ | |
console.log(`Adding team "${TEAM_ID}" to ${repoNames.length} repos with permission "${TEAM_PERMISSION}"`); | |
for (let repo of repoNames) { | |
await addTeamToRepo(TEAM_ID, GITHUB_ORG, repo, TEAM_PERMISSION); | |
} | |
})(); |
Thanks @adailey14 !!!
This script was only fetching up to page 9 for me, so I added an asterisk to the nextPageRe regex so it looks like this now on mine and it will fetch all my repos even after page 9 now!
const nextPageRe = /link\: \<.+page\=([0-9]*)\>\; rel\=\"next\"/g;
@adailey14 capital "L" is working for me :)
-d "{\""permission\"":\""${permission}\""}"
should solve it
Thanks for this massive timesaver.
@adailey14 Lowercase "L" fixed multiple pages for me.
@steve-hart-lion The asterisk fixed going past page 9 for me.
This script is a lifesaver. Thank you!
Had to edit a few things to get it to work on my windows machine,
Following the API I've changed the following:
- Uppercase L is fine (again)
- Asterisk seems to work so i added that in
- Repo owner is now apparently a required field -> using the same as our organisation name worked, but in the case that certain repos have different owners you might want to check that; you can probably find it in repos.owner.login
- team id is now the team slug
- added logging for replies
- fixed the curl parsing issue when working from cmd
So here's the script with these changes that worked for me in windows using cmd:
/*
* Adds a team to all the repos in a Github organization. This is a tedious
* process in the UI. You'll need a newer version of node to run this (e.g 9+)
* because it uses async/await.
*
* Instructions:
*
* 1. Copy this file somewhere on your computer, e.g. ~/addteamrepos.js
* 2. Fill in the uppercase variables below with the right values
* 3. Run this file: `$ node ~/addteamrepos.js`
*/
const GITHUB_ORG = 'your-organisation'; /* Name of the github organization the team is under and the repos are in */
const GITHUB_ACCESS_TOKEN = 'ghp_yourtoken'; /* Create an access token here: https://github.com/settings/tokens */
const TEAM_SLUG = 'your-team-slug'; /* GitHub team slug, similar to the name, you can get it from the API (the url when checking team) */
const TEAM_PERMISSION = 'push'; /* 'pull' or 'push' or 'admin' */
const REPO_OWNER = 'name-of-owner' /* Possibly the same as your organisation */
const { exec } = require('child_process');
function execPromise(command) {
return new Promise((resolve, reject) => {
exec(command, (err, stdout, stderr) => {
if (err) {
return reject(err);
}
resolve([stdout, stderr]);
});
});
}
async function fetchReposPage(org, page) {
const [response] = await execPromise(
`curl -i -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/${org}/repos?page=${page}`
);
const nextPageRe = /Link\: \<.+page\=([0-9]*)\>\; rel\=\"next\"/g;
const nextPageMatch = nextPageRe.exec(response);
const nextPage = nextPageMatch ? nextPageMatch[1] : null;
const repos = JSON.parse(response.slice(response.indexOf('[')));
return [repos, nextPage];
}
async function fetchRepos(org) {
let repos = [];
let page = 1;
while (page) {
let [currentRepos, nextPage] = await fetchReposPage(org, page);
repos = [...repos, ...currentRepos];
page = nextPage;
}
return repos;
}
async function addTeamToRepo(teamSlug, org, repo, permission, owner) {
const [out,err] = await execPromise(
`curl -X PUT -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/${org}/teams/${teamSlug}/repos/${owner}/${repo} -d \"{\\"permission\\":\\"${permission}\\"}\"`
);
console.log(out);
console.log(`... Added team "${teamSlug}" to repo "${org}/${repo}" with permission "${permission}"`);
}
(async () => {
/* Fetch all repos names for org */
console.log(`Fetching repos from organization "${GITHUB_ORG}"`);
const repos = await fetchRepos(GITHUB_ORG);
const repoNames = repos.map(r => r.name);
console.log(`... Found ${repoNames.length} repos`)
/* Add team to each repo */
console.log(`Adding team "${TEAM_SLUG}" to ${repoNames.length} repos with permission "${TEAM_PERMISSION}"`);
for (let repo of repoNames) {
await addTeamToRepo(TEAM_SLUG, GITHUB_ORG, repo, TEAM_PERMISSION, REPO_OWNER);
}
})();
If you're not running in CMD you might want to change the
\"{\\"permission\\":\\"${permission}\\"}\"
back to
'{"permission":"${permission}"}'
With this script I am add only public repos and not the private repos.
You need to setup your token to access ALL repos
thank you!
Morning 👋
Could you add a note to the top on how you get the team ID?
something like run curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/${GITHUB_ORG}/teams#list-teams
In case this is useful to anyone looking at easy ways to obtain a token
# for those using GitHub CLI
# there is an easy way to obtain a token from command line
# post initial authenticating
# if I'm not wrong github access token prefix could differ
# -> classic token vs fine grained token
#
# below produced with
# gh version 2.4.0+dfsg1 (2022-03-23 Ubuntu 2.4.0+dfsg1-2)
# https://github.com/cli/cli/releases/latest
token_prefix="gho."
token=$(gh auth status --show-token 2>&1)
GITHUB_ACCESS_TOKEN=$(echo $token | grep -o -P "$token_prefix{37}")
# confirm token value is correct - before adding to scripts
echo $GITHUB_ACCESS_TOKEN
# with a token which relevant access to GitHub org
# curl
curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/infraxis/teams
# curl + jq
curl -s -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/infraxis/teams | jq -r '.[]'
# curl + jq - show id and name
curl -s -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" https://api.github.com/orgs/infraxis/teams | jq -r '.[] | "\(.id) \t \(.name)"'
# curl + jq + team name grep
curl -s -H "Authorization: token $GITHUB_ACCESS_TOKEN" https://api.github.com/orgs/infraxis/teams | jq -r '.[] | "\(.id) \t \(.name)"' | grep -i foo
To get this to work for multiple pages, I had to change the regex on line 35 to have a lowercase "L" in link. I'm guessing that changed since this was written. Otherwise works great thanks!