Extend Go Module
Prerequisites
Assumes we’re working with the module setup from the end of Hello Go Module.
It should look like this:
├── basic
│ ├── .gitignore
│ ├── Makefile
│ ├── bin
│ │ └── basic
│ ├── cmd
│ │ └── basic
│ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ └── pkg
│ └── hello
│ ├── hello.go
│ └── hello_test.go
Part 1: Use a Taskfile
Let’s use task
1 instead of make
for this project. Install task
2
if needed.
~/
$ wget https://github.com/go-task/task/releases/download/v3.22.0/task_linux_amd64.tar.gz
$ tar -C /usr/bin -xvf task_linux_amd64.tar.gz
$ rm task_linux_amd64.tar.gz
$ which task
/usr/bin/task
Here’s a more or less equivalent Taskfile
for the existing Makefile
.
~/example.com/basic/Taskfile.yml
version: "3"
tasks:
go:test:
cmds:
- go test example.com/basic/...
go:test:verbose:
cmds:
- go test -v example.com/basic/...
go:build:
generates:
- bin/basic
sources:
- ./**/*.go
cmds:
- go build -o bin/basic example.com/basic/cmd/basic
go:run:
deps:
- go:build
- go:test
cmds:
- bin/basic
~/example.com/basic
$ task go:run
task: [go:test] go test example.com/basic/...
task: [go:build] go build -o bin/basic example.com/basic/cmd/basic
? example.com/basic/cmd/basic [no test files]
ok example.com/basic/pkg/hello (cached)
task: [go:run] bin/basic
Hello M World
Woohoo! Looks like everything is still working and using the new Taskfile. Now we
can remove the old Makefile
: rm Makefile
.
Part 2: Bootstrap with an Ansible Playbook
Here we build an Ansible playbook and set of template files so we can quickly and easily spin up a new module with a default structure and contents. Each piece of the playbook is broken down below.
Check for a task
installation. This could probably be changed into a choice
between Taskfile or Makefile.
- name: Install `task`
ansible.builtin.unarchive:
src: https://github.com/go-task/task/releases/download/v3.22.0/task_linux_amd64.tar.gz
dest: /usr/local/bin
remote_src: true
creates: /usr/local/bin/task
Loop3 through a list of directories to create.
- name: Create module directories
ansible.builtin.file:
path: '{{ item }}'
state: directory
mode: '0755'
loop:
- '{{ module_dir }}'
- '{{ module_dir }}/{{ module_name }}'
- '{{ module_dir }}/{{ module_name }}/cmd/{{ module_name }}'
- '{{ module_dir }}/{{ module_name }}/pkg/hello'
Create general configuration files from templates4.
- name: Create taskfile
ansible.builtin.template:
src: files/Taskfile.yml.tpl
dest: '{{ module_dir }}/{{ module_name }}/Taskfile.yml'
mode: '0755'
- name: Create gitignore file
ansible.builtin.copy:
src: files/.gitignore_base
dest: '{{ module_dir }}/{{ module_name }}/.gitignore'
mode: '0755'
files/Taskfile.yml.tpl
files/.gitignore_base
bin/
.task/
.vscode/
Create go
files from templates. Loop through a list of filename
> path
mappings
and create the go
file at that path for the template.
- name: Create go files
ansible.builtin.template:
src: files/{{ item.key }}.go.tpl
dest: '{{ module_dir }}/{{ module_name }}/{{ item.value }}/{{ item.key }}.go'
mode: '0755'
loop: '{{ go_templates | dict2items }}'
vars:
go_templates:
hello: pkg/hello
hello_test: pkg/hello
main: cmd/{{ module_name }}
files/main.go.tpl
files/hello.go.tpl
files/hello_test.go.tpl
Initialize module and notify handler to update module requirements.
- name: Initialize go module
ansible.builtin.command:
chdir: '{{ module_dir }}/{{ module_name }}'
cmd: go mod init {{ module_import_path }}
creates: go.mod
notify: Update Module Reqs
handlers:
- name: Update Module Reqs
ansible.builtin.command:
chdir: "{{ module_dir }}/{{ module_name }}"
cmd: go mod tidy
creates: go.sum
Assuming a directory structure like this containing the playbook and templates.
├── playbook.yml
└── files
├── main.go.tpl
├── hello.go.tpl
├── hello_test.go.tpl
├── Taskpaper.tpl.tpl
└── .gitignore_base
Now we can set the playbook variables to values needed for a new module, and run
the playbook. In this case we’re creating a module named basic
initialized with the
path github.com/basic
located in the ~/github/examples/tmp
directory. The clean_module
variable controls whether or not the module_dir
is wiped before configuring the module,
this was mostly useful while I was testing the playbook.
vars:
module_dir: ~/github/examples/tmp
module_name: basic
module_import_path: github.com/basic
clean_module: false
$ ansible-playbook playbook.yml -v
As before we end up with a directory structure like this with the configured values:
.
├── .gitignore
├── Taskfile.yml
├── bin
│ └── basic
├── cmd
│ └── basic
│ └── main.go
├── go.mod
├── go.sum
└── pkg
└── hello
├── hello.go
└── hello_test.go