Gruf, a Gerrit command line utility

Tue 16 February 2016 by Lars Kellogg-Stedman Tags gerrit gruf

(See also the followup to this article.)

I've recently started spending more time interacting with Gerrit, the code review tool used both by OpenStack, at, and by a variety of other open source projects at GerritForge's GitHub-linked I went looking for command line tools and was largely disappointed with what I found. Many of the solutions out there assume that you're regularly interacting with a single Gerrit instance, and that's seldom the case: more often, the Gerrit server in use varies from project to project.
I also found that many of the tools were opinionated in what sort of output they would produce.

For these reasons, I ended up rolling my own tool called Gruf. This is a wrapper for the Gerrit command line API that will let you query and review Gerrit change requests from the comfort of your command line. It is meant to supplement, not replace, the git-review tool that can be used to submit code for review and download patchsets for reviewing changes locally.

Gruf produces output by passing the result of Gerrit commands through Jinja templates, which means you can produce just about any sort of output you want without needing to modify the code.

Basic usage

You can use pretty much any of the Gerrit user commands as they are presented in the documentation. For example, to get a list of review requests that are owned by you:

$ gruf query status:open owner:self
262882   7   larsks   introduce global "nodes" configuration role

Or to review an existing review request:

$ gruf review --code-review +2 262882


Gruf will attempt to read configuration information from $HOME/.config/gruf/gruf.yml (unless $XDG_CONFIG_DIR in your environment points somewhere other than $HOME/.config). This is a YAML format file that can contain two keys:

  • querymap -- contains query term aliases
  • cmdalias -- contains command aliases

These are both discussed in more detail below.


Gruf produces output by passing the response from Gerrit through a template. You can provide an explicit template name on the command line using the -t flag. The previous gruf query example is exactly equivalent to:

$ gruf -t default query status:open owner:self

But you can instead ask to see all of the comments for the results:

$ gruf -t comments query status:open owner:self

Gruf looks for templates in two places:

  • In the gruf module directory.
  • In a templates directory co-located with your gruf.yml configuration file.

Within each directory, Gruf first looks for templates in a subdirectory named after the Python class used to process the response from Gerrit. For the query command, this is QueryResponse; which means that to override the default template for the query command, you would create $HOME/.config/gruf/templates/QueryResponse/default.

You can also provide Gruf with inline templates using the --inline-template (aka -T) command line option:

$ gruf -T '{{url}}' query here limit:5

If you want to see what attributes are available in the response from Gerrit, use the yaml template:

$ gruf -t yaml query here limit:1

This will dump the results from Gerrit as a YAML document.

Referring to changes

Gerrit itself allows you to refer to reviews using change numbers ("262882"), change IDs ("Id55e1baa0adf10f704dec2516e98a112be381d14"), and git commit IDs ("80ce4ea09ab7c16aeb5b356ad17e8fb740f3d22b").

Gruf adds the option of using git reference names (e.g., branches and tags) by prefixing a term with git:. So if you want to get an overview of a review associated with your current commit, you can ask for git:HEAD:

$ gruf -t patches query git:HEAD
262882   larsks   introduce global "nodes" configuration role

         [007] refs/changes/82/262882/7
               rdo-ci-centos   Verified -1
         [006] refs/changes/82/262882/6
               rdo-ci-centos   Verified -1
         [005] refs/changes/82/262882/5
               rdo-ci-centos   Verified -1
         [004] refs/changes/82/262882/4
               rdo-ci-centos   Verified -1
         [003] refs/changes/82/262882/3
               rdo-ci-centos   Verified -1
         [002] refs/changes/82/262882/2
               larsks          Code-Review -1
         [001] refs/changes/82/262882/1

This works for any other valid git reference (a relative reference like git:HEAD^, a branch or tag name like stable/liberty, or whatever).

Query aliases

The Gerrit query language supports a variety of query operators. For example, you can search for reviews that you own with owner:self and you can limit results to a particular project with something like project:redhat-openstack/triple-quickstart. While that's very useful, it can be annoying if you find yourself typing the same operators over and over.

Gruf supports a simple form of query aliasing. There are three built-in aliases:

  • mine expands to owner:self
  • open expands to status:open
  • here expands to project:{project}, where {project} is replaced by the name of the current project.

This allows you to simplify this:

gruf query status:open project:redhat-openstack/tripleo-quickstart


gruf query open here

You can define additional aliases in the querymap section of your gruf.yml file. For example, given the following:

  needsreview: status:open -is:reviewed

You can now find changes in the project that need review by running:

gruf query needsreview here

Command aliases

You may get tired of typing:

gruf -t patches query ...

If you create the following entry in your gruf.yml configuration file:

    cmd: query
    template: patches

You can now type something like:

gruf patches git:HEAD

And gruf will behave as if you typed:

gruf -t patches query git:HEAD

This is especially useful for simple inline templates. For example, given the following entry:

  cmd: query
  inline_template: >-

You can type:

gruf refs open here

And get output like:

262882 refs/changes/82/262882/7 80ce4ea09ab7c16aeb5b356ad17e8fb740f3d22b
263336 refs/changes/36/263336/2 5600951b5ce6e18b3d3fff75599518a00ea25384
263241 refs/changes/41/263241/1 27dbb595d58372b396cafe6dfacf97e58f43bc26
260561 refs/changes/61/260561/4 ee7f35d0528894990b736b8cece338d1c57ab0ac
262397 refs/changes/97/262397/1 6181c9b7361e4a804ab7069491a0780d119144f6
261934 refs/changes/34/261934/1 cf84db84bb67f201df5b59bbdf831dcf3d83056d
261218 refs/changes/18/261218/1 0d383494c579932e1edddfed23755da7fb2c9aae


The following example session assumes the following configuration:

    cmd: query
    template: comments
    cmd: query
    template: patches
  • Get a list of open reviews:

    $ gruf query open here
    262882   7   larsks   introduce global "nodes" configuration role
    263336   2   trown    WIP refactor and simplify image build
    263241   1   trown    Move mention of pre-downloaded image to lower section of README
    260561   4   trown    Make release rpm location configurable
    262397   1   sshnaidm Split mitaka installation playbook
    261934   1   trown    WIP Use IPA ramdisk for liberty deploy
    261218   1   trown    WIP self writing docs
  • See an overview of patch sets for a particular change:

    $ gruf patches 262882
    262882   larsks   introduce global "nodes" configuration role
             [007] refs/changes/82/262882/7
                   rdo-ci-centos   Verified -1
             [006] refs/changes/82/262882/6
                   rdo-ci-centos   Verified -1
             [005] refs/changes/82/262882/5
                   rdo-ci-centos   Verified -1
  • See comments for a particular change:

    $ gruf comments 262882
    From: Lars Kellogg-Stedman (larsks) <>
        Uploaded patch set 7.
    From:  (rdo-ci-centos) <>
        Patch Set 7: Verified-1
        Build Failed
    : Test failed
    : Test failed
    : Test failed
    : Test failed
    : Test ran successfully
  • Abandon a change (with a comment):

    $ gruf review -m 'this was a terrible idea' --abandon 262882,7

Future plans

I've already started using this regularly myself, but I'm sure that as I work with Gerrit I will develop a better understanding of what I want in a command-line tool. At the very least I need to implement some form of caching to avoid hammering the Gerrit servers with repeated requests for the same information.

Beyond that, I'm curious if anyone else finds this useful and if there are features you would like to see.

Happy hacking!