How to retrieve the latest release tag of a GitHub repository using Ansible playbook

Written by - 0 comments

Published on - Listed in Git Ansible


Sometimes you need to install software or scripts from a GitHub repository. Of course Ansible is perfectly capable of doing this in several ways. One easy but efficient way is to use a simple get_url task for this purpose.

During a new server-setup (the first time the playbook runs) this works fine. The software is not yet installed (checked upon using stat) and therefore the latest version is automatically retrieved from the remote GitHub repository.

But what about future/newer releases? Should I re-download and overwrite the currently installed software on every playbook run? That's just using resources (compute, bandwidth) for nothing - especially if we're talking of large software or scripts. And the playbook will run longer without any benefit.

My goal in this situation is to compare the currently installed version to the latest available version on the GitHub repo. Only if the versions don't match, the software should be re-downloaded and re-installed. And this should all be handled by Ansible - of course.

Ansible getting latest version from GitHub repository using REST API

Get tags using GitHub API

GitHub offers a free REST API which allows to list repository tags on a repository (an alternative is to list releases). Let's use the public repository of the monitoring plugin check_rancher2 for this purpose:

ck@mint ~ $ curl https://api.github.com/repos/Napsty/check_rancher2/tags
[
  {
    "name": "1.12.0",
    "zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.12.0",
    "tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.12.0",
    "commit": {
      "sha": "3774696ae67e9160f4f5f5f8b429c26bda79bbec",
      "url": "https://api.github.com/repos/Napsty/check_rancher2/commits/3774696ae67e9160f4f5f5f8b429c26bda79bbec"
    },
    "node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjEyLjA="
  },
  {
    "name": "1.11.0",
    "zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.11.0",
    "tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.11.0",
    "commit": {
      "sha": "f36626b1509fd2cd61a6c6395be130d0a9e730cf",
      "url": "https://api.github.com/repos/Napsty/check_rancher2/commits/f36626b1509fd2cd61a6c6395be130d0a9e730cf"
    },
    "node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjExLjA="
  },
[...]

The response is a JSON document which contains all the tags of this repository. The relevant key is "name". To only list the tags, we can use the jq command, filtering only for the "name" key:

ck@mint ~ $ curl https://api.github.com/repos/Napsty/check_rancher2/tags | jq -r ".[].name"
1.12.0
1.11.0
1.10.0
1.9.0
1.8.0
1.7.1
1.7.0
1.6.1
1.6.0
1.5.0
1.4.0
1.3.0
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0

And if we only want the latest tag, we specifically select the first array index [0] of the output:

ck@mint ~ $ curl https://api.github.com/repos/Napsty/check_rancher2/tags | jq -r ".[0].name"
1.12.0

Nice. This works. But how do we translate this into an Ansible playbook?

Handle GitHub API in Ansible playbook

So far we know: We need to use a HTTP request to the GitHub API and parse the JSON response for the first "name" key in the array. But which module should we use? This answer on StackOverflow gives a very good hint to use the uri module, although the question has nothing to do with tags.

  - name: CHECK_RANCHER2 - Check for newest available check_rancher2 version
    uri:
     url: https://api.github.com/repos/Napsty/check_rancher2/tags
      method: GET
      return_content: yes
      status_code: 200
      body_format: json
    register: result

By defining body_format: json will save/convert the response as JSON and is therefore re-usable inside the Ansible run.

The full output of this task is saved in the "result" variable. If you're an old fox like me, you know Ansible doesn't just save the HTTP response in this variable; there's plenty of meta data around. Best thing is to use the debug module to show what exactly Ansible stores in the "result" variable:

  - debug:
      msg: "{{ result }}"
    ignore_errors: True

After running these playbook tasks, the output shows there's quite some data saved into "result":

TASK [debug] ***************************************************************************************
ok: [target] => {
    "msg": {
        "accept_ranges": "bytes",
        "access_control_allow_origin": "*",
        "access_control_expose_headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
        "cache_control": "public, max-age=60, s-maxage=60",
        "changed": false,
        "connection": "close",
        "content": "[{\"name\":\"v2.9.0\",\"zipball_url\":\"https://api.github.com/repos/wp-cli/wp-cli/zipball/refs/tags/v2.9.0\",\"tarball_url\":\"https://api.github.com/repos/wp-cli/wp-cli/tarball/refs/tags/v2.9.0\",\"commit\":{\"sha\":\"8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7\",\"url\":\"https://api.github.com/repos/wp-cli/wp-cli/commits/8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7\"},\"node_id\":\"MDM6UmVmMjM2MDc1NTpyZWZzL3RhZ3MvdjIuOS4w\"},{\"name\":\"v2.8.1\",\"zipball_url\":\"https://api.github.com/repos/wp-cli/wp-cli/zipball/refs/tags/v2.8.1\", [...]
[...]
        "failed": false,
        "json": [
            {
                "commit": {
                    "sha": "3774696ae67e9160f4f5f5f8b429c26bda79bbec",
                    "url": "https://api.github.com/repos/Napsty/check_rancher2/commits/3774696ae67e9160f4f5f5f8b429c26bda79bbec"
                },
                "name": "1.12.0",
                "node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjEyLjA=",
                "tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.12.0",
                "zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.12.0"
            },
            {
                "commit": {
                    "sha": "f36626b1509fd2cd61a6c6395be130d0a9e730cf",
                    "url": "https://api.github.com/repos/Napsty/check_rancher2/commits/f36626b1509fd2cd61a6c6395be130d0a9e730cf"
                },
                "name": "1.11.0",
                "node_id": "MDM6UmVmMTM5MTU4MjgxOnJlZnMvdGFncy8xLjExLjA=",
                "tarball_url": "https://api.github.com/repos/Napsty/check_rancher2/tarball/refs/tags/1.11.0",
                "zipball_url": "https://api.github.com/repos/Napsty/check_rancher2/zipball/refs/tags/1.11.0"
            },
[...]

The JSON response is first saved into a "content" key and then translated by Ansible into a nested JSON structure (saved into the "json" key). This means we should be able to access the latest "name" key we need using "result.json[0].name",  similar to what we used with jq before.

  - debug:
      msg: "{{ result.json[0].name }}"
    ignore_errors: True

And the output of the playbook run shows the newest version/tag from the GitHub repo:

TASK [debug] ***************************************************************************************
ok: [target] => {
    "msg": "1.12.0"
}

This variable (result.json[0].name) can now be used inside the playbook to do a local version comparison.

All together, please

Here you go.

  # Check GitHub API for newest tag of check_rancher2
  - name: CHECK_RANCHER2 - Check for newest available check_rancher2 version
    uri:
      url: https://api.github.com/repos/Napsty/check_rancher2/tags
      method: GET
      return_content: yes
      status_code: 200
      body_format: json
    register: result

  # Debug output for the full result variable
  - debug:
      msg: "{{ result }}"
    ignore_errors: True

  # Debug output to see the latest tag. This should show an output like: 1.12.0
  - debug:
      msg: "{{ result.json[0].name }}"
    ignore_errors: True



Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Observability   Office   OpenSearch   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder