- store build metadata (version, commit, date) - multi-binary build: `make build` builds all packages from ./cmd - devcontainer adds ./bin to the PATH by default
153 lines
5.9 KiB
Markdown
153 lines
5.9 KiB
Markdown
# template
|
|
|
|
A Go project template for **PoC, hobby projects, and small publishable packages**.
|
|
Clone it, rename the module, run `make init`, and you're coding.
|
|
|
|
Demonstrates idiomatic Go patterns: stdlib-only dependencies, consumer-defined interfaces,
|
|
manual fakes for testing, and a clean `go.mod` that stays free of dev tool noise —
|
|
so packages extracted from this template can be published without module graph pollution.
|
|
|
|
---
|
|
|
|
## Design forces
|
|
|
|
Two libraries in this template emerged from a recurring friction in Go:
|
|
**mechanism leaking into intent**.
|
|
|
|
**`pkg/result`** exists because `if err != nil { return nil, err }` repeated on every line
|
|
obscures what the code is actually doing. The happy path drowns in error-routing boilerplate.
|
|
`result.Expect` moves the exit logic to the edges — constructors and entry points — so the
|
|
body of a function reads as intent, not plumbing.
|
|
|
|
**`pkg/logger`** exists because `fmt.Fprintln(os.Stderr, "error: "+msg)` leaks *how* (write
|
|
to stderr, format a string) into code that should only express *what* (something went wrong).
|
|
The same friction appeared in format selection: choosing between human text and JSON based on
|
|
the execution environment is mechanism, not business logic. `logger.NewCLI` hides that choice —
|
|
the call site just says `log.Warn(...)` and gets the right output for the context.
|
|
|
|
The pattern: **constructors own the mechanism; call sites own the intent.**
|
|
|
|
---
|
|
|
|
## Features
|
|
|
|
| Area | Tool | Purpose |
|
|
|---|---|---|
|
|
| Language | Go 1.25 | Modules, toolchain directive |
|
|
| Logging | standard `log/slog` | Auto-detects terminal vs pipe: human text or JSON; optional debug file dump |
|
|
| 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.versions` + `go run` | Pinned versions without polluting `go.mod` |
|
|
| 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/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, configures git hooks
|
|
make tools # (optional) install tool binaries to GOPATH/bin for IDE integration
|
|
```
|
|
|
|
### 3. Build and run
|
|
|
|
```bash
|
|
make build # compiles every cmd/* binary to ./bin/, stamped with git commit + build time
|
|
```
|
|
|
|
Every subdirectory under `cmd/` becomes a named binary in `./bin/`. In the devcontainer,
|
|
`./bin/` is on `PATH` automatically, so after `make build` you can run `app` (or any other
|
|
binary) directly — with any flags — from any directory in the terminal.
|
|
|
|
---
|
|
|
|
## 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
|
|
make release # list tags
|
|
make release VERSION=v0.1.0 # run full checks then tag+push
|
|
```
|
|
|
|
> **Keyboard shortcut (VSCode):** `Ctrl+Shift+B` → build, `Ctrl+Shift+T` → test.
|
|
|
|
### Claude Code commands
|
|
|
|
If you use [Claude Code](https://claude.ai/code), the repo ships a custom slash command:
|
|
|
|
| Command | What it does |
|
|
|---|---|
|
|
| `/new-package <name>` | Scaffolds `internal/<name>/` with a concrete type, constructor, doc comments, and a black-box test file — all wired to project conventions |
|
|
|
|
Commands live in `.claude/commands/` and are available to anyone who clones the repo.
|
|
|
|
---
|
|
|
|
## Project structure
|
|
|
|
```
|
|
.
|
|
├── cmd/
|
|
│ └── app/
|
|
│ ├── config.go # Flag parsing and Config struct (all flags live here)
|
|
│ └── main.go # Composition root (thin — just wiring)
|
|
├── internal/
|
|
│ └── greeter/ # Example domain package (replace with yours)
|
|
├── pkg/ # Publishable packages — no app-specific assumptions
|
|
│ ├── logger/ # slog wrapper: NewCLI, New, NewWriter, WithField/WithFields
|
|
│ ├── result/ # Happy-path error handling (Expect[T])
|
|
│ └── testutil/ # Test helpers (ResultOk, Equal, …)
|
|
├── .devcontainer/ # VSCode / Codespaces container definition
|
|
├── .githooks/ # pre-push hook (gofmt + vet + lint + gosec)
|
|
├── .vscode/ # Launch configs, tasks, editor settings
|
|
├── tools.versions # Pinned tool versions (Makefile + hook source this)
|
|
├── .golangci.yml # Linter configuration
|
|
└── Makefile # All development commands
|
|
```
|
|
|
|
---
|
|
|
|
## Devcontainer
|
|
|
|
Open this repo in VSCode and choose **"Reopen in Container"**. Run `make init` to
|
|
configure git hooks, then `make tools` for IDE-integrated tool binaries.
|
|
|
|
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 a Gitea Actions / CI pipeline running `make lint test security`
|
|
- **Docker** — add a multi-stage `Dockerfile` to publish the binary as a container image
|
|
- **OpenTelemetry** — add tracing with `go.opentelemetry.io/otel`
|