0.6.0
Features
Expose task name via Task
interface — #79, #87 ⇄ #80, #88 (⊶ bd15824, 8b30110)
↠ Most tasks provided a TaskName
package constant that contained the name of the task, but this was not an idiomatic and consistent way. To make sure that this information is part of the API, the new Name() string
method has been added to the Task
interface.
Task for Go toolchain env
command — #81 ⇄ #82 (⊶ 5e3764a)
↠ To support the go env
command of the Go toolchain, a new Task
has been implemented in the new env
package that can be used through a Go toolchain Runner
.
The task is customizable through the following functions:
WithEnv(env map[string]string) env.Option
— sets the task specific environment.WithEnvVars(envVars ...string) env.Option
— sets the names of the target environment variables.WithExtraArgs(extraArgs ...string) env.Option
— sets additional arguments to pass to the command.
RunOut
method for Runner
interface — #83 ⇄ #84 (⊶ d818065)
↠ The Run
method of the Runner
interface allows to run a command, but did not return its output. This was blocking when running commands like go env GOBIN
to get the path to the GOBIN
environment variable.
To support such uses cases, the new RunOut(Task) (string, error)
method has been added to the Runner
interface that runs a command and returns its output.
Replace deprecated gobin
with custom go install
based task runner — #89 ⇄ #90 (⊶ 9c510a7)
↠ This feature supersedes #78 which documents how the official deprecation of gobin
in favor of the new Go 1.16 go install pkg@version
syntax feature should have been handled for this project. The idea was to replace the gobin
task runner with a one that leverages bingo, a project similar to gobin
, that comes with many great features and also allows to manage development tools on a per-module basis. The problem is that bingo
uses some non-default and nontransparent mechanisms under the hood and automatically generates files in the repository without the option to disable this behavior. It does not make use of the go install
command but relies on custom dependency resolution mechanisms, making it prone to future changes in the Go toolchain and therefore not a good choice for the maintainability of projects.
go install
is still not perfect
Support for the new go install
features, which allow to install commands without affecting the main
module, have already been added in #71 as an alternative to gobin
, but one significant problem was still not addressed: install module/package executables globally without overriding already installed executables of different versions.
Since go install
will always place compiled binaries in the path defined by go env GOBIN
, any already existing executable with the same name will be replaced. It is not possible to install a module command with two different versions since go install
still messes up the local user environment.
The Workaround: Hybrid go install
task runner
The solution was to implement a custom Runner
that uses go install
under the hood, but places the compiled executable in a custom cache directory instead of go env GOBIN
. The runner checks if the executable already exists, installs it if not so, and executes it afterwards.
The concept of storing dependencies locally on a per-project basis is well-known from the node_modules
directory of the Node package manager npm. Storing executables in a cache directory within the repository (not tracked by Git) allows to use go install
mechanisms while not affect the global user environment and executables stored in go env GOBIN
. The runner achieves this by changing the GOBIN
environment variable to the custom cache directory during the execution of go install
. This way it bypasses the need for “dirty hacks“ while using a custom output path.
The only known disadvantage is the increased usage of storage disk space, but since most Go executables are small in size anyway, this is perfectly acceptable compared to the clearly outweighing advantages.
Note that the runner dynamically runs executables based on the given task so Validate() error
is a NOOP.
Upcoming Changes
The solution described above works totally fine, but is still not a clean solution that uses the Go toolchain without any special logic so as soon as the following changes are made to the Go toolchain (Go 1.17 or later), the custom runner will be removed again:
- golang/go/issues#42088 — tracks the process of adding support for the Go module syntax to the
go run
command. This will allow to let the Go toolchain handle the way how compiled executable are stored, located and executed. - golang/go#44469 — tracks the process of making
go install
aware of the-o
flag like thego build
command which is the only reason why the custom runner has been implemented.
Further Adjustments
Because the new custom task runner dynamically runs executables based on the given task, the Bootstrap
method of the Wand
reference implementation Elder
now additionally allows to pass Go module import paths, optionally including a version suffix (pkg@version
), to install executables from Go module-based main
packages into the local cache directory. This way the local development environment can be set up, for e.g. by running it as startup task in JetBrains IDEs.
The method also ensures that the local cache directory exists and will create a .gitignore
file that includes ignore pattern for the cache directory.
Task for go-mod-upgrade
Go module command — #95 ⇄ #96 (⊶ c944173)
↠ The github.com/oligot/go-mod-upgrade Go module provides the go-mod-upgrade
command, a tool that to update outdated Go module dependencies interactively.
To configure and run the go-mod-upgrade
command, a new task.GoModule
has been implemented in the new gomodupgrade
package. It can be be run using a command runner that handles tasks of kind KindGoModule
.
The task is customizable through the following functions:
WithEnv(map[string]string) gomodupgrade.Option
— sets the task specific environment.WithExtraArgs(...string) gomodupgrade.Option
— sets additional arguments to pass to the command.WithModulePath(string) gomodupgrade.Option
— sets the module import path.WithModuleVersion(*semver.Version) gomodupgrade.Option
— sets the module version.
The Elder
reference implementation will provide a new GoModUpgrade
method.
Improvements
Remove unnecessary Wand
parameter in Task
creation functions — #76 ⇄ #77 (⊶ 536556b)
↠ Most Task
creation functions 1 2 3 4 required a Wand
as parameter which was not used but blocked the internal usage for task runners. Therefore these parameters have been removed. When necessary, it can be added individually later on or can be reintroduced through a dedicated function with extended parameters to cover different use cases.
Remove unnecessary app.Config
parameter from Task
creation functions — #85 ⇄ #86 (⊶ 72dd6a1)
↠ Some functions that create a Task
required an app.Config
struct, but most tasks did not use the data in any way. To improve the code quality and simplify the internal usage of tasks these parameters have been removed as well as the field from the structs that implement the Task
interfaces.
Update to tmpl-go
template repository version 0.8.0
— #91 ⇄ #92 (⊶ 3e18917)
↠ Updated to tmpl-go
version 0.8.0
which updates golangci-lint
to version 1.39.0
and the tmpl
repository version 0.9.0
.
Dogfooding: Introduce Mage with wand toolkit — #93 ⇄ #94 (⊶ 85c466d)
↠ The project only used GitHub Action workflows for CI but not Mage to automate tasks for itself though.
Following the “dogfooding“ concept Mage has finally been added to the repository, using wand as toolkit through the Elder
wand reference implementation.
The full changelog is available in the repository
Copyright © 2019-present Sven Greb