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.

Revisions

  1. qoomon renamed this gist Sep 3, 2024. 1 changed file with 0 additions and 0 deletions.
  2. qoomon renamed this gist Sep 3, 2024. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. qoomon created this gist Sep 3, 2024.
    86 changes: 86 additions & 0 deletions forEachOrgRepository.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    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 ?? ''))
    }