Jak używać
- Skopiuj polecenie instalacji i sprawdź źródła przed instalacją we własnym środowisku agenta.
- W oryginale sprawdź wymagane narzędzia, dane dostępowe i obsługiwane środowiska.
- Opisz dane wejściowe i oczekiwany rezultat; porównaj wynik ze źródłami.
npx skills add https://github.com/victorzhuk/skills --skill z-go-makefilePrzed rozpoczęciem
- Instalacja nie obejmuje subskrypcji modelu, limitu API ani uprawnień do plików.
- Gwiazdki i instalacje pokazują aktywność, a nie niezależny test jakości lub bezpieczeństwa.
Treść z repozytorium z zachowaniem nagłówków, przykładów, kodu, tabel, linków i obrazów.
Go Makefile (the inner loop)
Preserve the project's existing build tool and approved commands. When both Make and Task are already used, keep build/test/codegen recipes in Make and let [[z-go-taskfile]] delegate orchestration to them. Do not introduce a second tool solely to enforce this split.
Self-documenting help
Two conventions exist. Inline `target: ## desc` puts the comment on the target line itself:
.PHONY: help
help: ## show available targets
@grep -E '^[a-zA-Z0-9_-]+:.*## ' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*## "}; {printf " %-15s %s\n", $$1, $$2}'
build: ## compile the main binary
$(GO_BUILD) -o $(BUILD_DIR)/app ./cmd/appStandalone `## name: desc` as its own comment line above the target is an equally valid variant — pick whichever a project already has, don't mix both in one Makefile:
## build: compile the main binary
build:
$(GO_BUILD) -o $(BUILD_DIR)/app ./cmd/app.PHONY: help
help: ## help: show targets
@grep -E '^## ' $(MAKEFILE_LIST) | sed 's/^## / /' | column -t -s ':'Put help first (or set .DEFAULT_GOAL := help) so a bare make is discoverable.
DRY the build
Define the build incantation once and reuse it for every binary — flags can't drift apart:
BUILD_DIR ?= bin
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none)
BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -ldflags "-w -s -X main.Version=$(VERSION) -X main.CommitHash=$(GIT_COMMIT) -X main.BuildDate=$(BUILD_DATE)"
GO_BUILD := CGO_ENABLED=0 go build $(LDFLAGS) -trimpath
build: ## build: compile the main binary
@mkdir -p $(BUILD_DIR)
$(GO_BUILD) -o $(BUILD_DIR)/app ./cmd/app
build-cli: ## build-cli: compile the admin CLI
@mkdir -p $(BUILD_DIR)
$(GO_BUILD) -o $(BUILD_DIR)/cli ./cmd/cli- Inline
@mkdir -p $(BUILD_DIR)is simple and idempotent; use an order-only prerequisite when it improves the existing file. - Stamp the version through
-ldflags -Xwith all three symbols;-trimpath+CGO_ENABLED=0
for reproducible, static binaries.
- Stub contract: package
maindeclares threestringvars —Version,CommitHash,
BuildDate — printed by a --version flag or a /version log line. [[z-go-dockerfile]] build args ARG VERSION / ARG GIT_COMMIT / ARG BUILD_DATE MUST mirror these three names.
Optional: order-only dir prereq
A $(BUILD_DIR) target with an order-only prereq (| $(BUILD_DIR)) avoids repeating @mkdir -p across recipes — a DRY-er alternative worth it once a Makefile grows many build targets, at the cost of one more moving part:
$(BUILD_DIR):
@mkdir -p $@
build: | $(BUILD_DIR)
$(GO_BUILD) -o $(BUILD_DIR)/app ./cmd/appNever write binaries to the repo root
A bare go build ./cmd/app drops an app binary in the working dir and pollutes the tree. Always pass -o $(BUILD_DIR)/<name>.
Embedded assets
If a binary //go:embeds built assets (a frontend bundle, templates), make the Go build depend on the asset build, and offer a stub for asset-free local compiles:
build: assets
@mkdir -p $(BUILD_DIR)
$(GO_BUILD) -o $(BUILD_DIR)/app ./cmd/app
assets-stub: ## assets-stub: minimal placeholder for Go-only builds
@mkdir -p web/dist && echo '<!doctype html>' > web/dist/index.htmlCodegen via go tool
Pin generators in the go.mod tool block and invoke go tool <x> — versions stay reproducible, no @latest drift, no global installs:
generate: ## generate: regenerate code from schemas
go tool ogen --target internal/gen --clean api/v1/openapi.yamlPin source generators, including ogen and sqlc, to the project's selected versions. Preserve an existing installation convention when it is reproducible; do not silently move tools to @latest. The go.mod tool directive requires Go 1.24+.
Test and lint layering
GOTESTFLAGS ?=
test: ## test: all tests
timeout 5m go test $(GOTESTFLAGS) -timeout 2m -p 2 -parallel 4 ./...
test-unit: ## test-unit: fast tests, no external deps
timeout 5m go test -timeout 2m -p 2 -parallel 4 -short ./...
test-integration: ## test-integration: needs docker
timeout 15m go test -timeout 10m -p 2 -parallel 4 -count=1 -run Integration ./...
lint: ## lint: full lint
golangci-lint run
lint-new: ## lint-new: only changed code
@test -n "$(BASE_REF)" || { echo "BASE_REF is required" >&2; exit 1; }
golangci-lint run --new-from-rev=$(BASE_REF) ./...
fmt: ## fmt: format
go fmt ./... && golangci-lint fmt
vet: ## vet: go vet
go vet ./...Keep GOTESTFLAGS for optional selectors such as -run Foo; do not accept -args, package operands, shell metacharacters, or timeout/concurrency overrides through this seam. Fixed bounds follow the selectors so ordinary selection cannot replace them. timeout bounds the whole command, -timeout each test binary, -p package concurrency, and -parallel parallel tests/fuzz workers. Select bounds for the project and use an equivalent process deadline where GNU timeout is unavailable. Discover BASE_REF from the actual review target before lint-new; never assume master. Preserve caller-emitted commands rather than rewriting them.
dotenv and DSN handling
Optionally load a .env for local runs; strip surrounding quotes from any DSN you pass on a command line (a quoted DATABASE_URL="…" in .env otherwise reaches the tool with quotes):
-include .env
strip = $(patsubst "%",%,$(patsubst '%',%,$(1))).PHONY hygiene
List every target that is not a real file (build test lint fmt …). If you use the optional order-only dir-prereq pattern, keep the dir target (e.g. $(BUILD_DIR)) off .PHONY — that's what makes the order-only prereq work.
.gitignore for a Go service
bin/
coverage.out
coverage.html
.env
*.test
dist/.air.toml is ignored only for the working-copy variant; a committed .air.toml stays tracked.
dev target
dev: ## dev: watch-reload via air
go tool airPin air in the go.mod tool block (go get -tool github.com/air-verse/air@latest). A minimal .air.toml builds to bin/app and excludes vendor/. A Task dev task delegates to make dev — see [[z-go-taskfile]].
Do not
- Bare root
go build(drops a binary in the tree) — always-o $(BUILD_DIR)/…. - Repeat the same
go buildflags across recipes — hoist them intoGO_BUILD. - Mix the two help-comment conventions (inline and standalone) in the same Makefile.
- Add a new codegen tool via
go install @latestwithout consideringgo toolfirst —
pin new generators there; an existing @latest install for a dev-CLI tool elsewhere is an accepted coexisting convention, not something to migrate unprompted. The @latest convention applies only to tools that hold no credentials and have no production access; anything that receives credentials or touches production installs at a pinned version.
- Comments restating WHAT a recipe does — the help annotation is the doc.
Verify
make help # renders, lists every target
make -n build # dry-run: recipes expand with full flags, no tab errors
make vet test-unit lint
