Created
September 3, 2024 15:47
-
-
Save qoomon/1138485cddf23aea7a23ec14a5d1ee3e to your computer and use it in GitHub Desktop.
Script to iterate over all GitHub org repos
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
import {Octokit} from 'octokit'; | |
import * as process from 'node:process' | |
import YAML from 'yaml'; | |
const metaDataPath = '.github/metadata.yaml'; | |
const input = { | |
token: process.env.GITHUB_TOKEN ?? _throw(new Error('environment variable GITHUB_TOKEN is required')), | |
owner: process.env.GITHUB_OWNER ?? _throw(new Error('environment variable GITHUB_OWNER is required')), | |
} | |
const githubClient = new Octokit({auth: input.token}); | |
const summary = { | |
repositories: 0, | |
metaData: 0, | |
errors: 0, | |
missing: 0 | |
} | |
await forEachOrgRepository(githubClient, async ({owner, repo}, index) => { | |
console.log(`repo ${index.toString().padStart(4)}: ${owner}/${repo}`); | |
const metaData = await getRepositoryMetaData(githubClient, {owner, repo}) | |
.then((metaData) => { | |
if (metaData) summary.metaData++; | |
else summary.missing++; | |
return metaData; | |
}) | |
.catch((error) => { | |
summary.errors++; | |
console.error("ERROR", error); | |
return null; | |
}) | |
.then((metaData) => { | |
summary.repositories++; | |
return metaData; | |
}); | |
if (metaData) { | |
console.log(JSON.stringify({ | |
repository: `${owner}/${repo}`, | |
metaData, | |
}, null, 2)); | |
} | |
}) | |
console.log('summary:', JSON.stringify(summary, null, 2)); | |
// --- functions ------------------------------------------------------------------------------------------------------- | |
export function _throw(error: unknown): never { | |
throw error | |
} | |
async function forEachOrgRepository( | |
octokit: Octokit, | |
callback: ({owner, repo}: { owner: string, repo: string }, index: number) => Promise<void>): Promise<void> { | |
let index = 0; | |
const iterator = octokit.paginate.iterator(octokit.rest.repos.listForOrg, { | |
org: input.owner, per_page: 100 | |
}); | |
for await (const {data: repos} of iterator) { | |
await Promise.all(repos.map(async (repo) => callback({owner: repo.owner.login, repo: repo.name}, index++))); | |
} | |
} | |
async function getRepositoryFileContent(octokit: Octokit, {owner, repo, path}: { | |
owner: string, | |
repo: string, | |
path: string, | |
}): Promise<string | null> { | |
return octokit.rest.repos.getContent({owner, repo, path}) | |
.then((res) => { | |
if ('type' in res.data && res.data.type === 'file') { | |
return Buffer.from(res.data.content, 'base64').toString(); | |
} | |
throw new Error('Unexpected file content'); | |
}) | |
.catch((error) => { | |
if (error.status === 404) return null; | |
throw error; | |
}); | |
} | |
async function getRepositoryMetaData(octokit: Octokit, {owner, repo}: { owner: string, repo: string }) { | |
return getRepositoryFileContent(octokit, {owner, repo, path: metaDataPath}) | |
.then((content) => YAML.parse(content ?? '')) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment