Using the Github cli with Multiple Repos
Tags: github
Published: 2022-02-24 04:25:40

---
title: Using the Github cli with Multiple Repos
published: true
tags: github
cover_image: 
series: 
canonical_url: 
---

I have been using the [Github CLI](http://cli.github.com) for a while now, and
I've found it to be really great when working in the context of a single
repository. But what happens when you're responsible for work in *dozens* of
repos? Keeping tabs on many repos can be done, thanks to the `gh api`
subcommand, and aliases!

`gh api` allows you to call any github api endpoint, and for my particular
needs, the [`search/issues` endpoint](https://docs.github.com/en/rest/reference/search#search-issues-and-pull-requests) is the most useful, as it searches both
issues and PRs.

There are two different ways to format responses, either with the `--jq` flag,
which uses [`jq` syntax](https://stedolan.github.io/jq/manual/) or `--template`
which uses Go's [`text/template` syntax](https://pkg.go.dev/text/template). I
found prototyping with the `jq` syntax to be nice, but the color and formatting
choices in the `--template` flag won me over.

Note that the `--template` args in the below examples are all the same, and they
produce output like this (but with some tasteful terminal colors, which do not
appear in markdown):

```
[  98] chore(deps): update dependency maven to v3.6 (15 days ago)
  https://github.com/GoogleCloudPlatform/cloud-run-samples/pull/98
```

More information about formatting output can be found in the [`gh` formatting
help](https://cli.github.com/manual/gh_help_formatting).

#### My issues/PRs that need followup

In my job, my team is responsible for the care and feeding of many different
public github repositories, which requires a lot of issue triage, and pr
followups. Switching to each of these contexts to run `gh issues` and
`gh status` is pretty tiresome, so I made a command that uses `gh api` to search
for open issues and PRs in *any* repo that are assigned to me:

```shell
gh api -XGET search/issues -f q='assignee:@me is:open  ' \
  --template '{{range .items -}}[{{.number | printf "%5.f" |autocolor "blue" }}] {{.title}} ({{ timeago .updated_at | autocolor "yellow+d" }}) {{printf "\n  " }}{{.html_url}} {{print "\n" -}}{{end}}'
```
This command is not much fun to type, so I've created an alias within the `gh` tool (not to be confused with shell aliases). However, using the `gh alias set` command would require escaping the quotation marks, I chose to edit the config file directly - it lives at `~/.config/gh/config.yaml` on my linux machine. Adding the following block to the `aliases` list will create the alias:

```yaml
    mine: |
        api -XGET search/issues -f q="assignee:@me is:open"
        --template '{{range .items -}}
        [{{.number | printf "%4.f" |autocolor "blue" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }})
        {{print "\n  " }}{{.html_url}} {{print "\n" -}}
        {{end}}'"
```

(Note that the yaml heredoc syntax (`|`) is used to allow a multi-line string.
Ensure the indentation is the same for all lines, or there may be problems
parsing your config!)

Now I can see all my assigned issues and PRs by running `gh mine`!

#### Unclaimed team reviews

Tracking issues and PRs that are waiting on the team's review (that nobody has
claimed yet) can be done with a similar approach. Note that you'll need to update
the search query to use your own Github Organization and Team in the query below.

```shell
gh api -XGET search/issues -f q='team-review-requested:yourorg/yourteam is:open -review:approved no:assignee' \
--template '{{range .items -}}[{{.number | printf "%4.f" |autocolor "blue" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }}) {{printf "\n  " }}{{.html_url}} {{print "\n" -}}{{end}}'
```
or ready for your `config.yml`:
```yaml
    teamreviews: |
        api -XGET search/issues -f q="team-review-requested:yourorg/yourteam is:open -review:approved no:assignee"
        --template '{{range .items -}}
        [{{.number | printf "%4.f" |autocolor "blue" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }})
        {{printf "\n  " }}{{.html_url}} {{print "\n" -}}
        {{end}}'"
```
### Other useful searches

By replacing the search query (the `q` parameter) with another [github search
query](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests), you can use this method to display other handy results.

- Reviews I approved, but are not merged: `reviewed-by:@me review:approved
  is:open`
- Incomplete reviews I am assigned to directly: `user-review-requested:@me
  -review:approved` (note the `-` prefix, which will match un-approved PRs)

I found myself using the same display with different search queries, so I ended
up making an alias for doing arbitrary searches:

```yaml
    # global search, given a *quoted* github search string.
    gsearch: |
        api -XGET search/issues -f q="$1"
        --template '{{range .items -}}
        [{{.number | printf "%4.f" |autocolor "green" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }})
        {{- printf "\n  " }}{{.html_url}} {{println -}}
        {{end}}'
```

### Future directions

I would like to have a bit of extra information about these PRs, like whether or
not their required checks are passing, all in the same output. To achieve this,
I'd probably have to write a 
[gh-extension](https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions).
To avoid replicating existing `gh` functionality, there's a [go-gh
library](https://pkg.go.dev/github.com/cli/go-gh).

Do you use other github searches or `gh` tricks to keep tabs on your work? Tell
me about it in the comments!