Introducing "gah", a command line client for GitHub Actions

Over the last eight months at Armory I've found myself working more and more with GitHub Actions, the CI tool built into GitHub. While it's mostly been pleasant, besides working with Yet Another YAML Tool™, dealing with logs via workflow management has become an acute pain point. I've been working across roughly a dozen repositories, each with very similar workflows, and usually needing just a single piece of information. If you're at all familiar with Actions you'll no doubt have felt the pain when a particular job has a lot of output, because oftentimes the log viewer for that job will take relative eons to load. Forget about searching those logs this century. Gah! More and more this feels like a Solved Problem™ that I don't want to use a browser interface for so... I've reached into my toolbox, pulled out the trusty Haskell hammer, and forged a new command line tool called gah that prints GitHub Actions logs and let's me manipulate them with the myriad shell tools at my disposal.

What can it do?

Well, currently just print logs for completed workflow runs to STDOUT with varying levels of information. You can specify just an org/repository or optionally add branch and workflow names to get different log outputs. You might be asking at this point if it's any good to which I'll say: yes!

Initial Setup

gah produces binaries for macOS and Linux1, so the easiest way to get started is to download them to your machine:

# On macOS
bash -c \
  'curl -fsSL -o gah "https://github.com/dogonthehorizon/gah/releases/latest/download/gah-macos-amd64"' \
  && chmod +x gah

# On Linux
bash -c \
  'curl -fsSL -o gah "https://github.com/dogonthehorizon/gah/releases/latest/download/gah-linux-amd64"' \
  && chmod +x gah

Once you have the binaries on your machine you'll want to define a shell variable that stores your GitHub API token. While you can access the GitHub API without a token, you'll be limited to something like sixty requests per hour and you won't be able to access logs for private repositories.

Creating an API token is easy, and even allows you to authorize SSO enabled organizations. If you have private repositories you want to access, make sure you grant the repo permission. Once you have the token, store it in a file somewhere (I call mine .github_token).

Finally, in your shell profile or session, make sure the GAH_API_TOKEN env variable is set:

# In a BASH session

export GAH_API_TOKEN=$(cat .github_token)

You're now ready to pump logs into your terminal session like the best of them!

Getting the Latest Workflow Logs

Let's say you have a pretty simple use case. You have a small repository with a single workflow that triggers on a single event (like a tag push). In this case all you really need to see logs would be the org and repo name. This is exactly what gah will do without any further context:

gah logs --organization dogonthehorizon --repository gah

If you're in a rush, or just don't feel like typing much, you can also pass -o and -r flags respectively to specify org and repo.

Since gah is just piping logs to STDOUT, we can use standard shell pipes to manipulate output. For example, let's look at the latest run for gah itself and make sure binaries were copied (implying a successful compile):

❯ gah logs -o dogonthehorizon -r gah | rg -C2 Copied\ executables
2020-08-21T04:20:08.5525795Z Copying from /home/runner/work/gah/gah/.stack-work/install/x86_64-linux-dk85569e7f830e7dc606115fd702e078fb/a1f6ef357abc27fe1678c1fd5e43a2f7ecef693436db61eb5e55de6af8c19d7e/8.8.3/bin/gah to /home/runner/work/gah/gah/.stack-work/docker/_home/.local/bin/gah
2020-08-21T04:20:08.5580481Z 
2020-08-21T04:20:08.5581182Z Copied executables to /home/runner/work/gah/gah/.stack-work/docker/_home/.local/bin:
2020-08-21T04:20:08.5581672Z - gah
2020-08-21T03:37:36.8689546Z Cleaning up orphan processes
--
2020-08-21T03:50:26.3977090Z Copying from /Users/runner/work/gah/gah/.stack-work/install/x86_64-osx/1ebd2948041280b7e477c1e2c3304bfb7603e1141c06f29e053498dbcbdd5464/8.8.3/bin/gah to /Users/runner/.local/bin/gah
2020-08-21T03:50:26.4153120Z 
2020-08-21T03:50:26.4153560Z Copied executables to /Users/runner/.local/bin:
2020-08-21T03:50:26.4154430Z - gah
2020-08-21T03:50:26.4174250Z 
--
2020-08-21T03:50:26.3977110Z Copying from /Users/runner/work/gah/gah/.stack-work/install/x86_64-osx/1ebd2948041280b7e477c1e2c3304bfb7603e1141c06f29e053498dbcbdd5464/8.8.3/bin/gah to /Users/runner/.local/bin/gah
2020-08-21T03:50:26.4153180Z 
2020-08-21T03:50:26.4153570Z Copied executables to /Users/runner/.local/bin:
2020-08-21T03:50:26.4154440Z - gah
2020-08-21T03:50:26.4174270Z 
--
2020-08-21T04:20:08.5525815Z Copying from /home/runner/work/gah/gah/.stack-work/install/x86_64-linux-dk85569e7f830e7dc606115fd702e078fb/a1f6ef357abc27fe1678c1fd5e43a2f7ecef693436db61eb5e55de6af8c19d7e/8.8.3/bin/gah to /home/runner/work/gah/gah/.stack-work/docker/_home/.local/bin/gah
2020-08-21T04:20:08.5580503Z 
2020-08-21T04:20:08.5581385Z Copied executables to /home/runner/work/gah/gah/.stack-work/docker/_home/.local/bin:
2020-08-21T04:20:08.5581678Z - gah
2020-08-21T04:20:09.0988161Z ##[group]Run actions/download-artifact@v2

Choosing a Specific Branch or Reference

Specifying an org/repo pair is well and good when the project is small, but what if you're working on a library or project that builds versions based off a branch or reference? By passing the --branch or -b flags we can specify exactly which logs we want to pay attention to:

gah logs -o dogonthehorizon -r gah -b v0.2.0

If I want to take a look at the logs in more detail but I may not know what I'm looking for, I can pipe them into more:

gah logs -o dogonthehorizon -r gah -b v0.2.0 | more

At which point you'll see something like the following screenshot and can browse logs at your leisure.

Selecting Desired Workflows

The last knob you can turn to tweak information is the workflow flag. For example, if you have a build running in one workflow, and release in another, you can opt to look at just the build by specifying the --workflow or -w flags:

gah logs -o armory -r spinnaker-operator -w spinnaker-operator

Note: make sure you give your workflows human readable names, otherwise you'll have to specify the path to your workflow. Nobody likes more typing!

Next Steps for Gah

While gah is already scratching an itch, there's a few more things I'd like to support that will increase the tool's utility. In no particular order:

  • Streaming logs from in-progress jobs
    • Pretty sure you can only look at completed jobs in the current iteration of gah
  • Managing secrets
    • Another common paint point; managing secrets is tedious. Being able to manage them from the CLI would definitely speed up rotating access tokens or adding new secrets.
  • Workflow lifecycle
    • Sometimes you push a commit you already know is broken, why waste precious compute minutes on your paid plan? Or maybe you need to re-trigger a build that failed in some transient way. Quickly cancelling from the CLI feels more natural than switching contexts to a browser, finding the repo, finding the run, finding the little cancel button, and clicking it.
  • Contextual error messages
    • For this MVP release there hasn't been a lot of effort put into useful error messages. Things are always bound to fail though, so it's important to give users enough context about an error to take some corrective action.

Keep an eye out for an upcoming post on the design of gah itself. Until then, happy hacking, and be sure to leave feedback in the issue tracker!

Footnotes

  1. If you're interested in Windows builds or Docker containers feel free to create an issue!