Skip to content

Instantly share code, notes, and snippets.

@qoomon
Created September 3, 2024 15:47
Show Gist options
  • Save qoomon/1138485cddf23aea7a23ec14a5d1ee3e to your computer and use it in GitHub Desktop.
Save qoomon/1138485cddf23aea7a23ec14a5d1ee3e to your computer and use it in GitHub Desktop.
Script to iterate over all GitHub org repos
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