mise
Use mise.toml as the project task runner and tool installer. Translate older
Taskfile/justfile commands into mise instead of preserving the old runner by
default.
Baseline Go mise.toml
Adapt names and paths, but keep the shape recognisable:
min_version = "2026.2.4"
[env]
GOAMD64 = "v3"
[vars]
test_args = "-v -race -count=5 -p=3 -timeout=5m ./..."
[tools]
go = "latest"
"go:golang.org/x/vuln/cmd/govulncheck" = "latest"
golangci-lint = "latest"
[tasks.fmt]
description = "Format Go code"
run = "golangci-lint fmt"
[tasks.tidy]
description = "Tidy go.mod and go.sum"
run = "go mod tidy"
[tasks.modernize]
description = "Apply Go modernization fixes"
run = "golangci-lint run --enable-only modernize --fix ./..."
[tasks.lint]
description = "Lint Go code and apply safe fixes"
run = "golangci-lint run --fix"
[tasks.fix]
description = "Format, modernize, and apply lint fixes"
run = [
"mise run fmt",
"mise run modernize",
"mise run lint",
]
[tasks.vet]
description = "Run go vet"
run = "go vet ./..."
[tasks.vuln]
description = "Run govulncheck"
run = "govulncheck ./..."
[tasks.build]
description = "Build all Go packages"
run = "go build ./..."
[tasks.test]
description = "Run Go tests repeatedly with the race detector"
env = { CGO_ENABLED = "1" }
run = "go test {{vars.test_args}}"
[tasks."test:quiet"]
description = "Run Go tests with quiet success output"
env = { CGO_ENABLED = "1" }
run = '''
if output=$(go test {{vars.test_args}} 2>&1); then
echo "✓"
else
echo "$output"
exit 1
fi
'''
[tasks.check]
description = "Format, tidy, lint, scan, build, and test"
run = [
"mise run fix",
"mise run tidy",
"mise run vet",
"mise run vuln",
"mise run build",
"mise run test:quiet",
]
GOAMD64=v3 is Amolith's personal/performance baseline. Lower or remove it for
public binaries that need broad x86-64 compatibility.
Do not add a non-mutating tidy check unless the user asks for it. tidy is the
task.
Binary projects
For a user-facing binary, make build/install tasks target the actual command:
[vars]
binary = "example"
main = "./cmd/example"
static_tags = "netgo,osusergo,static_build"
static_ldflags = "-d -s -w -buildid= -extldflags=-static"
[tasks.build]
description = "Build a static pure-Go binary"
env = { CGO_ENABLED = "0" }
run = '''
go build \
-trimpath \
-tags '{{vars.static_tags}}' \
-ldflags '{{vars.static_ldflags}}' \
-o {{vars.binary}} \
{{vars.main}}
'''
[tasks.install]
description = "Install a static pure-Go binary"
env = { CGO_ENABLED = "0" }
run = '''
go install \
-trimpath \
-tags '{{vars.static_tags}}' \
-ldflags '{{vars.static_ldflags}}' \
{{vars.main}}
'''
Do not settle version policy by copying the static template. The template leaves
-buildvcs unspecified, so Go uses its auto behavior. For version reporting,
those fields matter only if project code or Fang reads them; they can still
matter for reproducibility or privacy, so choose -buildvcs explicitly when
that matters. Choose the policy with the user:
- Best-effort local/dev metadata: leave
-buildvcsunset and readruntime/debug.BuildInfoor Fang's fallback whenunknownis acceptable. - Release/package/user-visible version: derive an exact semver or commit in
the task and inject it with
-ldflags -X .... - jj-derived versions or multiple version surfaces: keep a project-owned resolver and pass the values to Fang, server metadata, package metadata, and tests.
- Reproducible or privacy-sensitive builds: consider
-buildvcs=false, but document the tradeoff. If a release task relies on Go VCS metadata, consider-buildvcs=trueso missing metadata fails loudly.
The static template is for pure-Go binaries. It will not work for Wails, race tests, or projects that require CGO, native keychains, WebKitGTK, or C-backed database drivers.
netgo forces the pure-Go DNS resolver. osusergo forces pure-Go user/group
lookup. static_build is only a project convention unless files or dependencies
use that build tag. -extldflags=-static asks the external linker for static C
libraries when external linking is involved; it is not a magic fix for CGO.
Optional pprof tasks
Add these only when the project intentionally exposes net/http/pprof, usually
on localhost and behind an explicit debug flag:
[tasks."profile:cpu"]
run = "go tool pprof -http :6061 'http://127.0.0.1:6060/debug/pprof/profile?seconds=15'"
[tasks."profile:heap"]
run = "go tool pprof -http :6061 'http://127.0.0.1:6060/debug/pprof/heap'"
[tasks."profile:allocs"]
run = "go tool pprof -http :6061 'http://127.0.0.1:6060/debug/pprof/allocs'"
Frontend or Wails projects
Add only the tools the project needs. For Wails, see wails.md; for light web
apps, see web.md. Keep frontend package manager choice project-specific: use
the existing lockfile, or ask before introducing npm, Bun, pnpm, or yarn.