How to determine the latest release on GitHub

Introduction

This post is about programmatically determining the latest version of a GitHub release. We are doing this using shell scripting.

Accessing release data on the command-line

Let’s say you want to determine the latest release of the gohugo.io static site generator. All release information needed can be accessed through the GitHub API. For hugo, it can be queried using the following URL:

https://api.github.com/repos/gohugoio/hugo/releases/latest

The data behind this URL is JSON encoded. When accessing it using a web browser, a handy UI typically allows to fold/unfold sections of it. This way one can determine the interesting sections to be further processed.

On the command-line, a HTTP client such as curl allows to access this information like this:

curl https://api.github.com/repos/gohugoio/hugo/releases/latest 2>/dev/null

All information related to the HTTP data transfer is actually redirected to the /dev/null device. It is not needed. The actual data is provided on STDOUT. Now the JSON data needs to be processed using some JSON parser.

Enter jq

This is, where jq comes in handy. It allows to access (say: slice, filter, and map) JSON data on the command-line to allow for further processing using the typical suspects such as sed, ask, or grep. Let’s find out what the .name field of the returned data gives us:

curl https://api.github.com/repos/gohugoio/hugo/releases/latest 2>/dev/null \
       | jq .name

At the time of writing it is: "v0.80.0". Now, let’s assume we want to get rid of the "s and the v. This can easily be done by stripping these characters.

Post-processing

Stripping characters from strings on the command-line can be achieved using some builtin functions of jq:

curl https://api.github.com/repos/gohugoio/hugo/releases/latest 2>/dev/null \
       | jq -r '.name | ltrimstr("v")'

The -r switch to jq requests it to output raw data, resulting in stripping the quotes from the output.

Now, what remains is what we have been looking for: 0.80.0

Bonus: using it in Ansible playbooks

The following shows an Ansible playbook which downloads the latest release of gohugo.io for ARM based systems and installs it in the /usr/local/bin directory:

---
- name: Make gohugo available
  hosts: all
  tasks:
    - name: Install needed packages
      become: true
      apt:
        name:
          - jq
          - curl
        state: present
    - name: Get hugo latest release version
      shell:
        cmd: curl https://api.github.com/repos/gohugoio/hugo/releases/latest | jq -r '.name | ltrimstr("v")'
      register: hugo_version
    - name: Get hugo latest release
      get_url:
        url: https://github.com/gohugoio/hugo/releases/download/v{{ hugo_version.stdout }}/hugo_{{ hugo_version.stdout }}_Linux-ARM.tar.gz
        dest: /tmp/hugo.tar.gz
    - name: Unarchive hugo latest release
      unarchive:
        src: /tmp/hugo.tar.gz
        remote_src: true
        dest: /usr/local/bin

Sure, this playbook is not perfect. But it should give a good idea of how using jq can help to automatize repetitive tasks when combined with Ansible.

comments powered by Disqus