I use Ansible to manage my entire OS setup, with a playbook that installs all necessary software, sets up my dotfiles, and configures my system. This playbook can be run on a fresh installation of my OS and have my system set up exactly how I like it. Recently, I shifted from the Linux Arch AUR to go install for Go binaries, as it felt simpler and ensured up-to-date dependencies - without having to depend on the authors creating Arch AUR packaging for it. However, I still couldn’t find a way to pin packages to specific GitHub tags/releases. To solve this, I created a simple Ansible playbook to install Go binaries directly from GitHub releases, allowing me to pin and auto update the version of the Go binaries I want to install.
First, we need to fetch the release versions from GitHub and store them in a JSON
file:
---
- name: Fetch latest Go release for packages
hosts: localhost
vars:
go_packages:
# jsonnet
- github.com/google/go-jsonnet/cmd/jsonnet
- github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
- github.com/google/go-jsonnet/cmd/jsonnet-lint
- github.com/google/go-jsonnet/cmd/jsonnetfmt
# configuration-management
- github.com/grafana/tanka/cmd/tk
- sigs.k8s.io/kustomize/kustomize/v5
- github.com/hashicorp/terraform
# ... Other packages
tasks:
- name: Fetch latest release from GitHub
uri:
url: "https://api.github.com/repos/{{ item.split('/')[1] }}/{{ item.split('/')[2] }}/releases/latest"
method: GET
return_content: true
headers:
Accept: "application/vnd.github.v3+json"
register: github_release
when: item.startswith('github.com')
loop: "{{ go_packages }}"
ignore_errors: true
loop_control:
label: "{{ item }}"
- name: Initialize release_versions variable
set_fact:
release_versions: {}
- name: Parse release versions and handle errors
set_fact:
release_versions: >-
{{
release_versions | combine({
item.item: (
item.content | from_json).tag_name if (
'json' in item and 'tag_name' in (item.content | from_json) and item.status == 200
) else 'latest'
})
}}
loop: "{{ github_release.results }}"
loop_control:
label: "{{ item.item }}"
- name: Write versions to JSON file
copy:
content: "{{ release_versions | to_nice_json }}"
dest: "~/dotfiles/ansible/deps/go-package-versions.json"
Note: if the Go dependency is not a GitHub repository, it will just set the version to latest
.
This will output a JSON
file with the latest release versions of the Go packages we want to install. Here's an example:
{
"github.com/go-delve/delve/cmd/dlv": "v1.23.1",
"github.com/google/go-jsonnet/cmd/jsonnet": "v0.20.0",
"github.com/google/go-jsonnet/cmd/jsonnet-lint": "v0.20.0",
"github.com/google/go-jsonnet/cmd/jsonnetfmt": "v0.20.0"
}
We can then use the JSON
file to install the Go binaries:
---
- name: Install and upgrade Go packages from JSON file
hosts: localhost
gather_facts: no
tasks:
- name: Read the Go packages JSON file
slurp:
src: ~/dotfiles/ansible/deps/go-package-versions.json
register: go_packages_file
- name: Set Go packages fact
set_fact:
go_packages: "{{ go_packages_file.content | b64decode | from_json }}"
- name: Install or upgrade Go packages
command: >
go install {{ item.key }}@{{ item.value }}
environment:
GOPATH: "{{ ansible_env.HOME }}/go"
with_dict: "{{ go_packages }}"
when: item.value is defined
loop_control:
label: "{{ item.key }}"
- name: Verify installations
command: "{{ ansible_env.HOME }}/go/bin/{{ item.key.split('/')[-1] }} --version"
register: go_pkg_versions
failed_when: go_pkg_versions.rc != 0
with_dict: "{{ go_packages }}"
loop_control:
label: "{{ item.key }}"
- name: Display installed Go package versions
debug:
msg: "Installed {{ item.key }} version: {{ item.stdout }}"
with_items: "{{ go_pkg_versions.results }}"
I hope this helps you manage your Go binaries with Ansible. I also manage all other pip
, npm
, ruby
etc. with Ansible. The full examples of fetching and installing Go deps can be found here: