211 lines
5.8 KiB
Markdown
211 lines
5.8 KiB
Markdown
# 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.25 | Modules, toolchain directive |
|
|
| Logging | standard `log/slog` | Structured JSON/text logging + `WithField` extension |
|
|
| Config | standard `flag` | CLI flags with defaults, no config files |
|
|
| 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 |
|
|
| Tests | standard `testing` | Table-driven tests, manual fakes, no third-party test framework |
|
|
| Git hooks | custom pre-push | gofmt + go vet + golangci-lint + gosec on every push |
|
|
| 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.25+
|
|
- 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 https://gitea.djmil.dev/djmil/go-template 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 default flags
|
|
```
|
|
|
|
---
|
|
|
|
## Daily workflow
|
|
|
|
```bash
|
|
make test # run all tests
|
|
make test-race # … with race detector
|
|
make lint # go vet + golangci-lint
|
|
make lint-fix # go fix + golangci-lint auto-fix
|
|
make security # gosec + govulncheck
|
|
```
|
|
|
|
> **Keyboard shortcut (VSCode):** `Ctrl+Shift+B` → build, `Ctrl+Shift+T` → test.
|
|
|
|
---
|
|
|
|
## Project structure
|
|
|
|
```
|
|
.
|
|
├── cmd/
|
|
│ └── app/
|
|
│ └── main.go # Composition root (thin — just wiring)
|
|
├── internal/
|
|
│ ├── config/ # flag-based config (config.Load)
|
|
│ ├── logger/ # slog wrapper with WithField / WithFields
|
|
│ └── greeter/ # Example domain package (replace with yours)
|
|
├── .devcontainer/ # VSCode / Codespaces container definition
|
|
├── .githooks/ # pre-push hook (installed by `make setup`)
|
|
├── .vscode/ # launch, tasks, editor settings
|
|
├── tools.go # Tool version pinning (build tag: tools)
|
|
├── .golangci.yml # Linter configuration
|
|
└── Makefile # All development commands
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
All configuration is via CLI flags with sensible defaults:
|
|
|
|
```bash
|
|
./bin/app -help
|
|
-env string environment: dev | staging | prod (default "dev")
|
|
-log-level string log level: debug | info | warn | error (default "info")
|
|
-name string application name (default "go-template")
|
|
-port int listen port (default 8080)
|
|
```
|
|
|
|
Override at runtime:
|
|
|
|
```bash
|
|
./bin/app -port 9090 -env prod -log-level warn
|
|
```
|
|
|
|
---
|
|
|
|
## Logging
|
|
|
|
The logger wrapper adds `WithField` and `WithFields` for ergonomic context chaining
|
|
on top of the standard `log/slog` package:
|
|
|
|
```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 (`slog.NewJSONHandler`).
|
|
In development, `logger.NewDevelopment()` uses the human-friendly text handler.
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
Tests use the standard `testing` package. For dependencies on interfaces, write
|
|
a manual fake inline in the test file — no code generation required:
|
|
|
|
```go
|
|
type fakeGreeter struct {
|
|
greetFn func(name string) (string, error)
|
|
}
|
|
|
|
func (f *fakeGreeter) Greet(name string) (string, error) {
|
|
return f.greetFn(name)
|
|
}
|
|
|
|
func TestSomething(t *testing.T) {
|
|
fake := &fakeGreeter{
|
|
greetFn: func(name string) (string, error) {
|
|
return "Hello, " + name + "!", nil
|
|
},
|
|
}
|
|
// pass fake to the system under test
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Git hooks
|
|
|
|
The pre-push hook runs **gofmt + go vet + golangci-lint + gosec** against the
|
|
full codebase before every `git push`. Running on push (not commit) keeps the
|
|
inner commit loop fast while still blocking bad code from reaching the remote.
|
|
|
|
`govulncheck` is intentionally excluded (it makes network calls and can be
|
|
slow). Run it manually with `make security`.
|
|
|
|
To skip the hook in an emergency:
|
|
|
|
```bash
|
|
git push --no-verify
|
|
```
|
|
|
|
---
|
|
|
|
## 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/golangci/golangci-lint/cmd/golangci-lint@latest
|
|
go mod tidy
|
|
make tools # reinstall binaries
|
|
```
|
|
|
|
---
|
|
|
|
## Devcontainer
|
|
|
|
Open this repo in VSCode and choose **"Reopen in Container"** — the Go
|
|
toolchain and all tools (golangci-lint, gosec, govulncheck) are 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`
|