# go-template A batteries-included Go project template optimised for **PoC and hobby projects**. Clone it, rename the module, run `make init`, and you're coding. --- ## Features | Area | Tool | Purpose | |---|---|---| | Language | Go 1.24 | Modules, toolchain directive | | Logging | [zap](https://github.com/uber-go/zap) | Structured JSON logging + `WithField` extension | | Config | [viper](https://github.com/spf13/viper) | YAML file + env var overlay | | Linting | [golangci-lint](https://golangci-lint.run) | Aggregated linters, one config file | | Security | [gosec](https://github.com/securego/gosec) + [govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck) | SAST + dependency vulnerability scan | | Mocks | [mockery v2](https://vektra.github.io/mockery/latest/) | Type-safe, EXPECT()-style generated mocks | | Tests | [testify](https://github.com/stretchr/testify) | Assertions (`assert`/`require`) + mock framework | | Git hooks | custom pre-commit | gofmt + golangci-lint + gosec on every commit | | Tool versions | `tools.go` + `go.mod` | Single source of truth for tool versions | | Dev environment | devcontainer | Reproducible VSCode / GitHub Codespaces setup | | IDE | VSCode | Launch configs, tasks, recommended settings | --- ## Getting started ### Prerequisites - Go 1.24+ - Git - (Optional) [VSCode](https://code.visualstudio.com/) + [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.Go) - (Optional) Docker for devcontainer ### 1. Clone and rename ```bash git clone my-project cd my-project # Interactive rename — updates module path, config, devcontainer, and docs: ./rename.sh ``` ### 2. Init (run once) ```bash make init # fetches deps, installs tools, configures git hooks ``` ### 3. Build and run ```bash make build # compiles to ./bin/app make run # go run with config/dev.yaml ``` --- ## Daily workflow ```bash make test # run all tests make test-race # … with race detector make lint # golangci-lint make lint-fix # auto-fix what can be fixed make security # gosec + govulncheck make mocks # regenerate mocks after interface changes make generate # run all //go:generate directives ``` > **Keyboard shortcut (VSCode):** `Ctrl+Shift+B` → build, `Ctrl+Shift+T` → test. --- ## Project structure ``` . ├── cmd/ │ └── app/ │ └── main.go # Composition root (thin — just wiring) ├── config/ │ └── dev.yaml # Local dev config (safe to commit) ├── internal/ │ ├── config/ # Viper config loader │ ├── logger/ # Zap wrapper with WithField │ └── greeter/ # Example domain package (replace with yours) ├── mocks/ │ └── greeter/ # Generated by `make mocks` — commit these ├── .devcontainer/ # VSCode / Codespaces container definition ├── .githooks/ # pre-commit hook (installed by `make setup`) ├── .vscode/ # launch, tasks, editor settings ├── tools.go # Tool version pinning (build tag: tools) ├── .golangci.yml # Linter configuration ├── .mockery.yaml # Mockery configuration └── Makefile # All development commands ``` --- ## Configuration Config is loaded from a YAML file (default `config/dev.yaml`) with env var overrides. ```yaml app: name: "go-template" port: 8080 env: "dev" # dev | staging | prod logger: level: "debug" # debug | info | warn | error ``` Override any key at runtime: ```bash APP_PORT=9090 LOGGER_LEVEL=info make run ``` The `CONFIG_PATH` env var points to the config file: ```bash CONFIG_PATH=config/staging.yaml ./bin/app ``` --- ## Logging The logger wrapper adds `WithField` and `WithFields` for ergonomic context chaining: ```go log.WithField("request_id", rid). WithField("user", uid). Info("handling request") log.WithFields(map[string]any{ "component": "greeter", "name": name, }).Debug("generating greeting") ``` In production (`app.env != dev`) the output is JSON. In development, use `logger.NewDevelopment()` for coloured console output. --- ## Mocking 1. Define your interface in an `internal/` package. 2. Add a `//go:generate mockery --name=YourInterface` directive. 3. Register the interface in `.mockery.yaml` under `packages:`. 4. Run `make mocks`. Generated mocks support type-safe EXPECT() call chains: ```go import mocks "gitea.djmil.dev/djmil/go-template/mocks/greeter" func TestSomething(t *testing.T) { g := mocks.NewMockGreeter(t) g.EXPECT().Greet("Alice").Return("Hello, Alice!", nil) result, err := myConsumer(g) require.NoError(t, err) } ``` --- ## Git hooks The pre-commit hook runs **gofmt + golangci-lint + gosec** before every commit. It checks only staged Go files to keep it fast. `govulncheck` is intentionally excluded from the hook (it makes network calls and can be slow). Run it manually with `make security`. To skip the hook in an emergency: ```bash git commit --no-verify -m "wip: skip hooks" ``` --- ## Tool version management Tool versions are pinned via `tools.go` (a `//go:build tools` file) and tracked in `go.mod`/`go.sum`. This ensures every developer and CI run uses exactly the same versions. To update a tool: ```bash go get github.com/vektra/mockery/v2@latest go mod tidy make tools # reinstall binaries ``` --- ## Devcontainer Open this repo in VSCode and choose **"Reopen in Container"** — everything (Go toolchain, golangci-lint, mockery, gosec, govulncheck) is installed automatically via `make init`. Works with GitHub Codespaces out of the box. --- ## Next steps (not included) - **HTTP server** — add [chi](https://github.com/go-chi/chi) or [gin](https://github.com/gin-gonic/gin) - **Database** — add [sqlx](https://github.com/jmoiron/sqlx) or [ent](https://entgo.io/) - **CI** — add `.github/workflows/ci.yml` running `make lint test security` - **Docker** — add a multi-stage `Dockerfile` for the binary - **OpenTelemetry** — add tracing with `go.opentelemetry.io/otel`