Ansible - Download latest release binary from Github repo

I got inspired and extended the play. After downloading I add the version number to the binary and link to the program. so I can check on the next run if the version is still up to date. Otherwise it is downloaded and linked again.

- hosts: localhost
  become: false
  vars:
    org: watchexec
    repo: watchexec
    filename: x86_64-unknown-linux-gnu.tar.xz
    version: latest
    project_url: https://api.github.com/repos/{{ org }}/{{ repo }}/releases
  tasks:

  - name: check {{ repo }} version
    uri:
      url: "{{ project_url }}/{{ version }}"
      return_content: true
    register: latest_version

  - name: check if {{ repo }}-{{ version }} already there
    stat:
      path: "{{ ansible_env.HOME }}/.local/bin/{{ repo }}-{{ latest_version.json.tag_name }}"
    register: newestbinary

  - name: download and link to version {{ version }}
    block:
      - name: create tempfile
        tempfile:
          state: directory
          suffix: dwnld
        register: tempfolder_1

      - name: "installing {{ repo }} {{ latest_version.json.tag_name }}"
        # idea from: https://stackoverflow.com/a/62672308/886659
        loop: "{{ latest_version.json.assets }}"
        when: "filename|string in item.name"
        unarchive:
          remote_src: yes
          src: "{{ item.browser_download_url }}"
          dest: "{{ tempfolder_1.path }}"
          keep_newer: yes
          extra_opts:
          - --strip=1
          - --no-anchored
          - "{{ repo }}"

      - name: command because no mv available
        command: mv "{{ tempfolder_1.path }}/{{ repo }}" "{{ ansible_env.HOME }}/.local/bin/{{ repo }}-{{ latest_version.json.tag_name }}"
        args:
          creates: "{{ ansible_env.HOME }}/.local/bin/{{ repo }}-{{ latest_version.json.tag_name }}"

      - name: "link {{ repo }}-{{ latest_version.json.tag_name }} -> {{ repo }} "
        file:
          src: "{{ ansible_env.HOME }}/.local/bin/{{ repo }}-{{ latest_version.json.tag_name }}"
          dest: "{{ ansible_env.HOME }}/.local/bin/{{ repo }}"
          state: link
          force: yes
    when: not newestbinary.stat.exists
    always:
      - name: delete {{ tempfolder_1.path|default("tempfolder") }}
        file:
          path: "{{ tempfolder_1.path }}"
          state: absent
        when: tempfolder_1.path is defined
        ignore_errors: true

# vim:ft=yaml.ansible:

here is the file on github


I am using the following recipe to download and extract latest watchexec binary for Linux from GitHub releases.

- hosts: localhost                                                     
  tasks:                                                               

  - name: check latest watchexec
    uri:
      url: https://api.github.com/repos/watchexec/watchexec/releases/latest
      return_content: true
    register: watchexec_latest

  - name: "installing watchexec {{ watchexec_latest.json.tag_name }}"
    loop: "{{ watchexec_latest.json.assets }}"
    when: "'x86_64-unknown-linux-musl.tar.xz' in item.name"
    unarchive:
      remote_src: yes
      src: "{{ item.browser_download_url }}"
      dest: "{{ ansible_env.HOME }}/bin/"
      keep_newer: yes
      extra_opts:
      - --strip=1
      - --no-anchored
      - watchexec

tar extra_opts explained here.

This still downloads the binary every time a playbook is called. As an improvement, it might be possible to use set_fact for caching node_id attribute that corresponds to the unpacked file.


Github has an API to manipulate the release which is documented.

so imagine you want to get the latest release of ansible (which belong to the project ansible) you would

  • call the url https://api.github.com/repos/ansible/ansible/releases/latest
  • get an json structure like this
{
  "url": "https://api.github.com/repos/ansible/ansible/releases/5120666",
  "assets_url": "https://api.github.com/repos/ansible/ansible/releases/5120666/assets",
  "upload_url": "https://uploads.github.com/repos/ansible/ansible/releases/5120666/assets{?name,label}",
  "html_url": "https://github.com/ansible/ansible/releases/tag/v2.2.1.0-0.3.rc3",
  "id": 5120666,
  "node_id": "MDc6UmVsZWFzZTUxMjA2NjY=",
  "tag_name": "v2.2.1.0-0.3.rc3",
  "target_commitish": "devel",
  "name": "THESE ARE NOT OUR OFFICIAL RELEASES",
  ...
  },
  "prerelease": false,
  "created_at": "2017-01-09T16:49:01Z",
  "published_at": "2017-01-10T20:09:37Z",
  "assets": [

  ],
  "tarball_url": "https://api.github.com/repos/ansible/ansible/tarball/v2.2.1.0-0.3.rc3",
  "zipball_url": "https://api.github.com/repos/ansible/ansible/zipball/v2.2.1.0-0.3.rc3",
  "body": "For official tarballs go to https://releases.ansible.com\n"
}
  • get the value of the key tarball_url
  • download the value of the key retrieved just above

In ansible code that would do

- hosts: localhost                                                     
  tasks:                                                               

  - uri:                                                               
      url: https://api.github.com/repos/ansible/ansible/releases/latest
      return_content: true                                             
    register: json_reponse                                             

  - get_url:                                                           
      url: "{{ json_reponse.json.tarball_url }}"                       
      dest: ./ansible-latest.tar.gz       

I let you adapt the proper parameters to answer your question :)


Another approach is to use ansible github_release module to get the latest tag

example

- name: Get gogs latest tag 
  github_release:
    user: gogs
    repo: gogs
    action: latest_release
  register: gogs_latest 

- name: Grab gogs latest binaries 
  unarchive: 
    src: "https://github.com/gogs/gogs/releases/download/{{ gogs_latest['tag'] }}/gogs_{{ gogs_latest['tag'] | regex_replace('^v','') }}_linux_amd64.zip"
    dest: /usr/local/bin
    remote_src: true 

  • The regex part at the end is for replacing the v at the beginning of the tag since the format now on GitHub for gogs is gogs_0.12.3_linux_armv7.zip and latest tag includes a v