Listing all GitHub repositories in a GitHub Organisation

Featured image for sharing metadata for article

In my post Analysing our dependency trees to determine where we should send Open Source contributions for Hacktoberfest, I mentioned that it can be handy to list all the repositories in a given GitHub organisation, to perform queries against them.

The previous solution I had was pretty awkward, and wasteful as it queried a lot of data via the RESTful API which it didn't then use. Fortunately, I've since dug into the GraphQL endpoint which allows us to query exactly what we need, which means we can write the following query, using the very handy auto-paginating GraphQL Octokit plugin:

const fs = require("fs");
const { Octokit } = require("@octokit/core");
const { paginateGraphql } = require("@octokit/plugin-paginate-graphql");
const MyOctokit = Octokit.plugin(paginateGraphql);
const octokit = new MyOctokit({ auth: process.env.GITHUB_TOKEN });

(async () => {
const resp = await octokit.graphql.paginate(
  `query paginate($cursor: String) {
    organization(login: "deliveroo") {
      repositories(first: 100, orderBy: {field: NAME, direction: ASC},
after: $cursor) {
        nodes {
          name
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }`
);

/*
resp = {
  organization: {
    repositories: {
      nodes: [
        {
          name: "deliveroo.engineering"
        },
        {
          name: "determinator"
        }
      ],
      pageInfo: {
        hasNextPage: false,
        endCursor: '...'
      }
    }
  }
}
*/

fs.writeFileSync('repos.txt', resp.organization.repositories.nodes.map((o) => o.name).join("\n"));
})();

When running this like so:

env GITHUB_TOKEN=... node get-repos.js

This then produces a file with one repo name per line, which is example what we wanted in the previous article.

Alternatively, in Go:

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/shurcooL/githubv4"
	"golang.org/x/oauth2"
)

func main() {
	client, err := newGitHubClient(context.Background(), os.Getenv("GITHUB_TOKEN"))
	if err != nil {
		log.Fatal(err)
	}

	repos, err := GetReposForOrg(context.Background(), client, "deliveroo")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("repos: %v\n", repos)
}

func newGitHubClient(ctx context.Context, githubToken string) (*githubv4.Client, error) {
	if githubToken == "" {
		return nil, nil, fmt.Errorf("no GITHUB_TOKEN environment variable was set")
	}

	src := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: githubToken},
	)
	httpClient := oauth2.NewClient(ctx, src)

	gqlClient := githubv4.NewClient(httpClient)

	return gqlClient, nil
}

func GetReposForOrg(ctx context.Context, client *githubv4.Client, org string) ([]string, error) {
	var q struct {
		Organization struct {
			Repositories struct {
				Nodes []struct {
					Name       string
				}
				PageInfo struct {
					EndCursor   githubv4.String
					HasNextPage bool
				}
			} `graphql:"repositories(first: 100, orderBy: {field: NAME, direction: ASC}, after: $cursor)"`
		} `graphql:"organization(login: $organization)"`
	}
	variables := map[string]interface{}{
		"organization": githubv4.String(org),
		"cursor":       (*githubv4.String)(nil),
	}

	var allRepos []string
	for {
		err := client.Query(ctx, &q, variables)
		if err != nil {
			return nil, err
		}

		for _, n := range q.Organization.Repositories.Nodes {
			allRepos = append(allRepos, fmt.Sprintf("%s/%s", org, n.Name))
		}

		if !q.Organization.Repositories.PageInfo.HasNextPage {
			break
		}
		variables["cursor"] = githubv4.NewString(q.Organization.Repositories.PageInfo.EndCursor)
	}

	return allRepos, nil
}

With the following go.mod:

module foo

go 1.22.1

require (
	github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc
	golang.org/x/oauth2 v0.19.0
)

require github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect

Which can then be run like so:

env GITHUB_TOKEN=... go run .

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #github #graphql.

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.