From 0f75b279c368fd5e94d7549d9cd0276eb582d921 Mon Sep 17 00:00:00 2001 From: djmil Date: Sun, 29 Mar 2026 19:24:55 +0000 Subject: [PATCH] trim fat, use Gi philosophy --- .githooks/{pre-commit => pre-push} | 28 ++++---- .golangci.yml | 13 +--- .mockery.yaml | 18 ----- .vscode/launch.json | 9 +-- .vscode/settings.json | 6 +- CLAUDE.md | 51 ++++++-------- Makefile | 31 +++----- README.md | 109 +++++++++++++---------------- cmd/app/main.go | 24 +++---- config/dev.yaml | 12 ---- go.mod | 31 +++++--- go.sum | 69 +++++++++++------- internal/config/config.go | 65 ++++++++--------- internal/greeter/greeter.go | 4 +- internal/greeter/greeter_test.go | 66 ++++++++++------- internal/logger/logger.go | 70 +++++++----------- mocks/greeter/mock_greeter.go | 92 ------------------------ rename.sh | 90 +++++++++++++++--------- tools.go | 1 - 19 files changed, 320 insertions(+), 469 deletions(-) rename .githooks/{pre-commit => pre-push} (56%) delete mode 100644 .mockery.yaml delete mode 100644 config/dev.yaml delete mode 100644 mocks/greeter/mock_greeter.go diff --git a/.githooks/pre-commit b/.githooks/pre-push similarity index 56% rename from .githooks/pre-commit rename to .githooks/pre-push index b411295..4b01553 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-push @@ -1,37 +1,33 @@ #!/usr/bin/env bash -# pre-commit hook: runs on every `git commit` +# pre-push hook: runs quality checks before every `git push`. # Install with: make setup (sets core.hooksPath = .githooks) set -euo pipefail -# Collect staged Go files only — avoids re-checking untouched code. -STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$' || true) - -if [ -z "$STAGED" ]; then - echo "pre-commit: no Go files staged — skipping." - exit 0 -fi - -echo "pre-commit: running checks on staged Go files..." +echo "pre-push: running checks..." # ── 1. gofmt ────────────────────────────────────────────────────────────────── echo " → gofmt" -UNFORMATTED=$(gofmt -l $STAGED) +UNFORMATTED=$(gofmt -l $(git ls-files '*.go')) if [ -n "$UNFORMATTED" ]; then echo " FAIL: the following files are not gofmt-formatted:" echo "$UNFORMATTED" | sed 's/^/ /' - echo " Fix with: gofmt -w or make lint-fix" + echo " Fix with: make lint-fix" exit 1 fi -# ── 2. golangci-lint ────────────────────────────────────────────────────────── +# ── 2. go vet ───────────────────────────────────────────────────────────────── +echo " → go vet" +go vet ./... + +# ── 3. golangci-lint ────────────────────────────────────────────────────────── echo " → golangci-lint" golangci-lint run ./... -# ── 3. gosec (security scan) ────────────────────────────────────────────────── +# ── 4. gosec (security scan) ────────────────────────────────────────────────── echo " → gosec" gosec -quiet ./... -# govulncheck is intentionally omitted from pre-commit (network + slow). +# govulncheck is intentionally omitted (network + slow). # Run it manually with: make security -echo "pre-commit: all checks passed." +echo "pre-push: all checks passed." diff --git a/.golangci.yml b/.golangci.yml index 1ea4ceb..3783659 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,8 +4,7 @@ run: timeout: 5m - # Align with go.mod - # go: '1.24' + go: '1.25' # Enable specific linters on top of the default set. # Default set: errcheck, gosimple, govet, ineffassign, staticcheck, unused @@ -39,7 +38,7 @@ linters-settings: goimports: # Put local module imports in their own group (after stdlib and external). - local-prefixes: github.com/your-org/go-template + local-prefixes: gitea.djmil.dev/djmil/go-template gocritic: disabled-checks: @@ -65,13 +64,5 @@ issues: - unparam - gocritic - gosec - # Don't fail on generated files. - - path: mock_.*\.go - linters: - - all - - path: mocks/ - linters: - - all - max-issues-per-linter: 50 max-same-issues: 5 diff --git a/.mockery.yaml b/.mockery.yaml deleted file mode 100644 index c7873d1..0000000 --- a/.mockery.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# mockery v2 configuration -# Docs: https://vektra.github.io/mockery/latest/configuration/ -# -# Mocks are placed in mocks// next to their source package. -# Regenerate with: make mocks - -with-expecter: true # generate type-safe EXPECT() call chains -mockname: "Mock{{.InterfaceName}}" -filename: "mock_{{.InterfaceName | snakecase}}.go" -outpkg: "mocks" -dir: "mocks/{{.PackageName}}" - -# Packages whose interfaces should be mocked. -# Add entries here whenever you define an interface that other packages depend on. -packages: - gitea.djmil.dev/djmil/go-template/internal/greeter: - interfaces: - Greeter: diff --git a/.vscode/launch.json b/.vscode/launch.json index e8b6e76..306789c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,10 +8,7 @@ "request": "launch", "mode": "auto", "program": "${workspaceFolder}/cmd/app", - "env": { - "CONFIG_PATH": "${workspaceFolder}/config/dev.yaml" - }, - "args": [] + "args": ["-env", "dev", "-log-level", "debug"] }, { // ── Debug: same as above but with delve attached ─────────────────────── @@ -20,9 +17,7 @@ "request": "launch", "mode": "debug", "program": "${workspaceFolder}/cmd/app", - "env": { - "CONFIG_PATH": "${workspaceFolder}/config/dev.yaml" - }, + "args": ["-env", "dev", "-log-level", "debug"], "showLog": true, "trace": "verbose" }, diff --git a/.vscode/settings.json b/.vscode/settings.json index 9c3d71e..4f31e21 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,5 +33,9 @@ // ── Test explorer ────────────────────────────────────────────────────────── "go.testExplorer.enable": true, - "makefile.configureOnOpen": false + "makefile.configureOnOpen": false, + "cSpell.words": [ + "djmil", + "gitea" + ] } diff --git a/CLAUDE.md b/CLAUDE.md index ad0c3b1..90d513c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,8 +7,8 @@ Keep it concise — the agent needs signal, not essays. ## Project overview -Go 1.24 template / PoC starter. Demonstrates: structured logging (zap), -config (viper), interfaces + mocks (mockery), linting (golangci-lint), +Go 1.25 template / PoC starter. Demonstrates: structured logging (slog), +config (flag), interfaces + manual fakes, linting (golangci-lint), security scanning (gosec, govulncheck), git hooks, devcontainer, VSCode tasks. Module: `gitea.djmil.dev/djmil/go-template` — update this when you fork. @@ -19,15 +19,12 @@ Module: `gitea.djmil.dev/djmil/go-template` — update this when you fork. ``` cmd/app/main.go composition root — wires deps, no logic here -internal/config/ Viper config loader (config.Load) -internal/logger/ Zap wrapper with WithField / WithFields +internal/config/ flag-based config loader (config.Load) +internal/logger/ slog wrapper with WithField / WithFields internal/greeter/ Example domain package (delete or repurpose) -mocks/greeter/ Generated mocks — regenerate with `make mocks` -config/dev.yaml Local dev config (committed, no secrets) tools.go Tool version pinning (build tag: tools) .golangci.yml Linter rules -.mockery.yaml Mockery code-gen config -.githooks/pre-commit Runs gofmt + golangci-lint + gosec before commit +.githooks/pre-push Runs gofmt + go vet + golangci-lint + gosec before push ``` --- @@ -38,9 +35,8 @@ tools.go Tool version pinning (build tag: tools) - **Packages** — keep `cmd/` thin (wiring only); business logic belongs in `internal/` - **Interfaces** — define interfaces where they are *used*, not where they are *implemented* - **Errors** — wrap with `fmt.Errorf("context: %w", err)`; never swallow errors silently -- **Logging** — use `log.WithField("key", val)` for structured context, never `fmt.Sprintf` in log messages -- **Config** — all configuration through `internal/config`; no hard-coded values in logic packages -- **Secrets** — never commit `.env` files or credentials; use env var overrides of config keys +- **Logging** — use `log.WithField("key", val)` for structured context; never `fmt.Sprintf` in log messages; `log/slog` is the backend +- **Config** — all configuration through `internal/config` (flag-parsed); no hard-coded values in logic packages --- @@ -58,13 +54,13 @@ tools.go Tool version pinning (build tag: tools) ## Testing rules -- All tests use **testify** (`assert` for soft checks, `require` for stop-on-fail) +- Tests use only the standard `testing` package — no third-party assertion libraries - Test files: `package foo_test` (black-box) unless white-box access is needed -- Mock dependencies via **mockery**-generated mocks with `EXPECT()` chains +- Fake dependencies with **manual fakes** (implement the interface inline in `_test.go`) - Use `logger.NewNop()` when the test doesn't care about log output - Table-driven tests with `t.Run("description", ...)` for multiple cases - The race detector is enabled in CI (`make test-race`); don't introduce data races -- Never use `time.Sleep` in tests; use channels or `require.Eventually` +- Never use `time.Sleep` in tests; use channels or `t.Cleanup` --- @@ -73,15 +69,13 @@ tools.go Tool version pinning (build tag: tools) ```bash make init # first-time setup: fetch deps, install tools, git hooks make build # compile to ./bin/app -make run # go run with config/dev.yaml +make run # go run with default flags make test # run all tests make test-race # tests + race detector -make lint # golangci-lint -make lint-fix # auto-fix lint issues +make lint # go vet + golangci-lint +make lint-fix # go fix + golangci-lint auto-fix make security # gosec + govulncheck -make mocks # regenerate all mocks -make generate # run all //go:generate directives -make clean # remove bin/ and mocks/ +make clean # remove bin/ ``` VSCode: `Ctrl+Shift+B` = build, `Ctrl+Shift+T` = test. @@ -92,19 +86,17 @@ Debug: use launch config "Debug: app" (F5). ## Adding new features (checklist) 1. Define the interface in `internal//` -2. Write the implementation and its unit tests -3. Add the interface to `.mockery.yaml` and run `make mocks` -4. Wire it in `cmd/app/main.go` -5. Run `make lint test` before committing +2. Write the implementation and its unit tests (using a manual fake for the interface) +3. Wire it in `cmd/app/main.go` +4. Run `make lint test` before committing --- ## Known pitfalls -- `mocks/` are committed intentionally — regenerate after interface changes -- `govulncheck` makes network calls; excluded from pre-commit hook (run manually) -- `config/dev.yaml` is committed but never add real credentials here +- `govulncheck` makes network calls; excluded from pre-push hook (run manually) - `tools.go` has `//go:build tools` — it won't compile into the binary +- `go fix` rewrites source files; run `make lint-fix` before committing after a Go version bump --- @@ -113,5 +105,6 @@ Debug: use launch config "Debug: app" (F5). Keep this section to ~10 entries; remove stale items. Format: YYYY-MM-DD — what was done and why. --> -- 2026-03-05 — Initial template scaffolded: config, logger, greeter example, - mocks, git hooks, devcontainer, VSCode tasks, golangci-lint, Makefile. +- 2026-03-29 — Stripped to stdlib-only: removed testify/mockery→manual fakes, zap→slog, viper→flag. +- 2026-03-29 — Pre-commit hook moved to pre-push; go vet + go fix added to lint pipeline. +- 2026-03-29 — Fixed stale docs and .golangci.yml local-prefixes; .vscode launch configs use CLI flags. diff --git a/Makefile b/Makefile index 1b3281f..ec99a54 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,9 @@ -.PHONY: help init setup build run test test-race lint security generate mocks clean +.PHONY: help init setup build run test test-race lint lint-fix security clean # ── Variables ────────────────────────────────────────────────────────────────── BINARY_NAME := app BINARY_PATH := ./bin/$(BINARY_NAME) CMD_PATH := ./cmd/app -CONFIG_PATH := ./config/dev.yaml -GO_FILES := $(shell find . -type f -name '*.go' -not -path './vendor/*') # ── Default target ───────────────────────────────────────────────────────────── help: ## Show this help message @@ -14,13 +12,7 @@ help: ## Show this help message # ── First-time setup ─────────────────────────────────────────────────────────── init: ## First-time project init: fetch deps, install tools, configure git hooks - @echo "--- Fetching application dependencies ---" - go get go.uber.org/zap@latest - go get github.com/spf13/viper@latest - go get github.com/stretchr/testify@latest - go get github.com/stretchr/objx@latest @echo "--- Fetching tool dependencies (pinned in tools.go) ---" - go get github.com/vektra/mockery/v2@latest go get github.com/golangci/golangci-lint/cmd/golangci-lint@latest go get github.com/securego/gosec/v2/cmd/gosec@latest go get golang.org/x/vuln/cmd/govulncheck@latest @@ -40,7 +32,6 @@ setup: ## Configure git to use .githooks directory tools: ## Install tool binaries to GOPATH/bin (versions from go.mod) @echo "Installing tools..." go install github.com/go-delve/delve/cmd/dlv@latest - go install github.com/vektra/mockery/v2@latest go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest go install github.com/securego/gosec/v2/cmd/gosec@latest go install golang.org/x/vuln/cmd/govulncheck@latest @@ -50,8 +41,8 @@ build: ## Compile the binary to ./bin/ go build -o $(BINARY_PATH) $(CMD_PATH) # ── Run ──────────────────────────────────────────────────────────────────────── -run: ## Run the application with dev config - CONFIG_PATH=$(CONFIG_PATH) go run $(CMD_PATH)/main.go +run: ## Run the application with default flags + go run $(CMD_PATH)/main.go # ── Test ─────────────────────────────────────────────────────────────────────── test: ## Run all tests @@ -64,10 +55,12 @@ test-verbose: ## Run all tests with verbose output go test ./... -race -cover -v # ── Lint & Security ──────────────────────────────────────────────────────────── -lint: ## Run golangci-lint +lint: ## Run go vet + golangci-lint + go vet ./... golangci-lint run ./... -lint-fix: ## Run golangci-lint with auto-fix +lint-fix: ## Apply go fix + golangci-lint auto-fix + go fix ./... golangci-lint run --fix ./... security: ## Run gosec + govulncheck @@ -76,14 +69,6 @@ security: ## Run gosec + govulncheck @echo "--- govulncheck ---" govulncheck ./... -# ── Code generation ──────────────────────────────────────────────────────────── -generate: ## Run go generate across all packages - go generate ./... - -mocks: ## Regenerate all mocks via mockery (config: .mockery.yaml) - mockery - # ── Cleanup ──────────────────────────────────────────────────────────────────── -clean: ## Remove build artifacts and generated mocks +clean: ## Remove build artifacts rm -rf ./bin - rm -rf ./mocks diff --git a/README.md b/README.md index bd03702..b80312a 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,13 @@ Clone it, rename the module, run `make init`, and you're coding. | 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 | +| 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 | -| 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 | +| 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 | @@ -27,7 +26,7 @@ Clone it, rename the module, run `make init`, and you're coding. ### Prerequisites -- Go 1.24+ +- Go 1.25+ - Git - (Optional) [VSCode](https://code.visualstudio.com/) + [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.Go) - (Optional) Docker for devcontainer @@ -52,7 +51,7 @@ make init # fetches deps, installs tools, configures git hooks ```bash make build # compiles to ./bin/app -make run # go run with config/dev.yaml +make run # go run with default flags ``` --- @@ -62,11 +61,9 @@ make run # go run with config/dev.yaml ```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 lint # go vet + golangci-lint +make lint-fix # go fix + golangci-lint auto-fix 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. @@ -80,20 +77,15 @@ make generate # run all //go:generate directives ├── 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 +│ ├── config/ # flag-based config (config.Load) +│ ├── logger/ # slog wrapper with WithField / WithFields │ └── 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`) +├── .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 -├── .mockery.yaml # Mockery configuration └── Makefile # All development commands ``` @@ -101,35 +93,28 @@ make generate # run all //go:generate directives ## 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: +All configuration is via CLI flags with sensible defaults: ```bash -APP_PORT=9090 LOGGER_LEVEL=info make run +./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) ``` -The `CONFIG_PATH` env var points to the config file: +Override at runtime: ```bash -CONFIG_PATH=config/staging.yaml ./bin/app +./bin/app -port 9090 -env prod -log-level warn ``` --- ## Logging -The logger wrapper adds `WithField` and `WithFields` for ergonomic context chaining: +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). @@ -142,29 +127,32 @@ log.WithFields(map[string]any{ }).Debug("generating greeting") ``` -In production (`app.env != dev`) the output is JSON. In development, use -`logger.NewDevelopment()` for coloured console output. +In production (`app.env != dev`) the output is JSON (`slog.NewJSONHandler`). +In development, `logger.NewDevelopment()` uses the human-friendly text handler. --- -## Mocking +## Testing -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: +Tests use the standard `testing` package. For dependencies on interfaces, write +a manual fake inline in the test file — no code generation required: ```go -import mocks "gitea.djmil.dev/djmil/go-template/mocks/greeter" +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) { - g := mocks.NewMockGreeter(t) - g.EXPECT().Greet("Alice").Return("Hello, Alice!", nil) - - result, err := myConsumer(g) - require.NoError(t, err) + fake := &fakeGreeter{ + greetFn: func(name string) (string, error) { + return "Hello, " + name + "!", nil + }, + } + // pass fake to the system under test } ``` @@ -172,16 +160,17 @@ func TestSomething(t *testing.T) { ## Git hooks -The pre-commit hook runs **gofmt + golangci-lint + gosec** before every commit. -It checks only staged Go files to keep it fast. +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 from the hook (it makes network calls -and can be slow). Run it manually with `make security`. +`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 commit --no-verify -m "wip: skip hooks" +git push --no-verify ``` --- @@ -195,7 +184,7 @@ same versions. To update a tool: ```bash -go get github.com/vektra/mockery/v2@latest +go get github.com/golangci/golangci-lint/cmd/golangci-lint@latest go mod tidy make tools # reinstall binaries ``` @@ -204,8 +193,8 @@ 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 +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. diff --git a/cmd/app/main.go b/cmd/app/main.go index 5a32847..899ce41 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -21,27 +21,21 @@ func main() { func run() error { // ── Config ──────────────────────────────────────────────────────────────── - cfgPath := os.Getenv("CONFIG_PATH") - if cfgPath == "" { - cfgPath = "config/dev.yaml" - } - - cfg, err := config.Load(cfgPath) - if err != nil { - return fmt.Errorf("loading config: %w", err) - } + cfg := config.Load() // ── Logger ──────────────────────────────────────────────────────────────── - var log *logger.Logger + var ( + log *logger.Logger + err error + ) if cfg.App.Env == "dev" { - log, err = logger.NewDevelopment() + log = logger.NewDevelopment() } else { log, err = logger.New(cfg.Logger.Level) + if err != nil { + return fmt.Errorf("creating logger: %w", err) + } } - if err != nil { - return fmt.Errorf("creating logger: %w", err) - } - defer log.Sync() log.WithFields(map[string]any{ "app": cfg.App.Name, diff --git a/config/dev.yaml b/config/dev.yaml deleted file mode 100644 index 49193b1..0000000 --- a/config/dev.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# Development configuration. -# Values here are safe for local use only — never commit production secrets. -# Override any key with an environment variable: APP_PORT=9090, LOGGER_LEVEL=debug - -app: - name: "go-template" - port: 8080 - env: "dev" - -logger: - # debug | info | warn | error - level: "debug" diff --git a/go.mod b/go.mod index ea54105..cf319b0 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,9 @@ go 1.25.0 // After cloning, rename the module path above to match your project. require ( + github.com/go-delve/delve v1.26.1 github.com/golangci/golangci-lint v1.64.8 github.com/securego/gosec/v2 v2.24.7 - github.com/spf13/viper v1.21.0 - github.com/stretchr/testify v1.11.1 - github.com/vektra/mockery/v2 v2.53.6 - go.uber.org/zap v1.27.1 golang.org/x/vuln v1.1.4 ) @@ -53,12 +50,15 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect - github.com/chigopher/pathlib v0.19.1 // indirect + github.com/cilium/ebpf v0.11.0 // indirect github.com/ckaznocha/intrange v0.3.0 // indirect + github.com/cosiner/argv v0.1.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect github.com/daixiang0/gci v0.13.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/derekparker/trie/v3 v3.2.0 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect @@ -68,6 +68,7 @@ require ( github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v0.3.9 // indirect github.com/go-critic/go-critic v0.12.0 // indirect + github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect @@ -89,6 +90,7 @@ require ( github.com/golangci/revgrep v0.8.0 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/go-dap v0.12.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect @@ -103,13 +105,11 @@ require ( github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect - github.com/huandu/xstrings v1.5.0 // indirect - github.com/iancoleman/strcase v0.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jgautheron/goconst v1.7.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect - github.com/jinzhu/copier v0.4.0 // indirect github.com/jjti/go-spancheck v0.6.4 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect @@ -125,6 +125,7 @@ require ( github.com/ldez/usetesting v0.4.2 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/macabu/inamedparam v0.1.3 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v1.1.0 // indirect @@ -144,6 +145,7 @@ require ( github.com/olekukonko/ll v0.0.8 // indirect github.com/olekukonko/tablewriter v1.0.7 // indirect github.com/openai/openai-go/v3 v3.23.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.7.1 // indirect @@ -159,10 +161,9 @@ require ( github.com/raeperd/recvcheck v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/rs/zerolog v1.34.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryancurrah/gomodguard v1.3.5 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect - github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect @@ -175,10 +176,13 @@ require ( github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.12.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.3 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tdakkota/asciicheck v0.4.1 // indirect github.com/tetafro/godot v1.5.0 // indirect @@ -209,9 +213,14 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.starlark.net v0.0.0-20231101134539-556fd59b42f6 // indirect + go.uber.org/atomic v1.7.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect + go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect @@ -220,7 +229,6 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect - golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/tools/go/expect v0.1.1-deprecated // indirect @@ -229,6 +237,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect diff --git a/go.sum b/go.sum index 80e2fa4..a4101b2 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8ger github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= @@ -76,12 +78,16 @@ github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iy github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= -github.com/chigopher/pathlib v0.19.1 h1:RoLlUJc0CqBGwq239cilyhxPNLXTK+HXoASGyGznx5A= -github.com/chigopher/pathlib v0.19.1/go.mod h1:tzC1dZLW8o33UQpWkNkhvPwL5n4yyFRFm/jL1YGWFvY= +github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= +github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= github.com/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY= github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= +github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.20 h1:VIPb/a2s17qNeQgDnkfZC35RScx+blkKF8GV68n80J4= +github.com/creack/pty v1.1.20/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= @@ -92,6 +98,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= +github.com/derekparker/trie/v3 v3.2.0 h1:fET3Qbp9xSB7yc7tz6Y2GKMNl0SycYFo3cmiRI3Gpf0= +github.com/derekparker/trie/v3 v3.2.0/go.mod h1:P94lW0LPgiaMgKAEQD59IDZD2jMK9paKok8Nli/nQbE= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= @@ -116,6 +124,10 @@ github.com/ghostiam/protogetter v0.3.9 h1:j+zlLLWzqLay22Cz/aYwTHKQ88GE2DQ6GkWSYF github.com/ghostiam/protogetter v0.3.9/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w= github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w= +github.com/go-delve/delve v1.26.1 h1:V1F0hzAjXCpsBP+I/E6fVUTLC/ZBSs1YWUb8cTtIWFE= +github.com/go-delve/delve v1.26.1/go.mod h1:Ua/k2AAu4cLrUXGSRVH1b2Nzq2aCK188b9EYlAojlz4= +github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 h1:IGtvsNyIuRjl04XAOFGACozgUD7A82UffYxZt4DWbvA= +github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62/go.mod h1:biJCRbqp51wS+I92HMqn5H8/A0PAhxn2vyOT+JqhiGI= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -150,7 +162,6 @@ github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUW github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -180,6 +191,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-dap v0.12.0 h1:rVcjv3SyMIrpaOoTAdFDyHs99CwVOItIJGKLQFQhNeM= +github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= @@ -222,20 +235,16 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= -github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= -github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= -github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc= github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= @@ -270,6 +279,8 @@ github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84Yrj github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= @@ -278,13 +289,11 @@ github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= @@ -324,8 +333,11 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -359,16 +371,12 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= -github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= -github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= @@ -397,12 +405,14 @@ github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= +github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4= @@ -412,6 +422,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -457,8 +468,6 @@ github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYR github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= -github.com/vektra/mockery/v2 v2.53.6 h1:qfUB6saauu652ZlMF/mEdlj7B/A0fw2XR0XBACBrf7Y= -github.com/vektra/mockery/v2 v2.53.6/go.mod h1:fjxC+mskIZqf67+z34pHxRRyyZnPnWNA36Cirf01Pkg= github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -497,16 +506,22 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.starlark.net v0.0.0-20231101134539-556fd59b42f6 h1:+eC0F/k4aBLC4szgOcjd7bDTEnpxADJyWJE0yowgM3E= +go.starlark.net v0.0.0-20231101134539-556fd59b42f6/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -574,10 +589,10 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -596,8 +611,6 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -653,6 +666,8 @@ google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXn gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/internal/config/config.go b/internal/config/config.go index d131e9e..7b246b6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,61 +1,54 @@ -// Package config loads application configuration from a YAML file + env vars. -// Priority (highest → lowest): env vars → config file → defaults. +// Package config parses application configuration from command-line flags. +// Defaults are defined here; override at runtime with flags: +// +// ./app -port 9090 -env prod -log-level warn // // Usage: // -// cfg, err := config.Load("config/dev.yaml") +// cfg := config.Load() // fmt.Println(cfg.App.Port) package config import ( - "fmt" - - "github.com/spf13/viper" + "flag" ) // Config is the root configuration object. Add sub-structs as the app grows. type Config struct { - App AppConfig `mapstructure:"app"` - Logger LoggerConfig `mapstructure:"logger"` + App AppConfig + Logger LoggerConfig } // AppConfig holds generic application settings. type AppConfig struct { - Name string `mapstructure:"name"` - Port int `mapstructure:"port"` - Env string `mapstructure:"env"` // dev | staging | prod + Name string + Port int + Env string // dev | staging | prod } // LoggerConfig controls logging behavior. type LoggerConfig struct { - Level string `mapstructure:"level"` // debug | info | warn | error + Level string // debug | info | warn | error } -// Load reads the YAML file at path and overlays any matching environment -// variables (e.g. APP_PORT overrides app.port). -func Load(path string) (*Config, error) { - v := viper.New() +// Load parses command-line flags and returns a Config. +// Call this once at startup before any other flag parsing. +func Load() *Config { + name := flag.String("name", "go-template", "application name") + port := flag.Int("port", 8080, "listen port") + env := flag.String("env", "dev", "environment: dev | staging | prod") + level := flag.String("log-level", "info", "log level: debug | info | warn | error") - v.SetConfigFile(path) + flag.Parse() - // Allow every config key to be overridden by an env var. - // e.g. APP_PORT=9090 overrides app.port - v.AutomaticEnv() - - // Sensible defaults so the binary works without a config file. - v.SetDefault("app.name", "go-template") - v.SetDefault("app.port", 8080) - v.SetDefault("app.env", "dev") - v.SetDefault("logger.level", "info") - - if err := v.ReadInConfig(); err != nil { - return nil, fmt.Errorf("config: reading %q: %w", path, err) + return &Config{ + App: AppConfig{ + Name: *name, + Port: *port, + Env: *env, + }, + Logger: LoggerConfig{ + Level: *level, + }, } - - var cfg Config - if err := v.Unmarshal(&cfg); err != nil { - return nil, fmt.Errorf("config: unmarshalling: %w", err) - } - - return &cfg, nil } diff --git a/internal/greeter/greeter.go b/internal/greeter/greeter.go index e7a90bc..050eb44 100644 --- a/internal/greeter/greeter.go +++ b/internal/greeter/greeter.go @@ -1,6 +1,6 @@ // Package greeter is a minimal example domain package. // It demonstrates how to: -// - define an interface (mockable via mockery) +// - define an interface (satisfied by manual fakes in tests) // - inject dependencies (logger) through a constructor // - use the logger.WithField pattern // @@ -13,8 +13,6 @@ import ( "gitea.djmil.dev/djmil/go-template/internal/logger" ) -//go:generate mockery --name=Greeter - // Greeter produces a greeting for a given name. // The interface is what other packages should depend on — never the concrete type. type Greeter interface { diff --git a/internal/greeter/greeter_test.go b/internal/greeter/greeter_test.go index 7b62a72..0cdf5fd 100644 --- a/internal/greeter/greeter_test.go +++ b/internal/greeter/greeter_test.go @@ -1,11 +1,9 @@ package greeter_test import ( + "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gitea.djmil.dev/djmil/go-template/internal/greeter" "gitea.djmil.dev/djmil/go-template/internal/logger" ) @@ -15,37 +13,51 @@ import ( func TestGreet(t *testing.T) { svc := greeter.New(logger.NewNop()) - t.Run("returns personalised greeting", func(t *testing.T) { + t.Run("returns personalized greeting", func(t *testing.T) { msg, err := svc.Greet("World") - require.NoError(t, err) - assert.Equal(t, "Hello, World!", msg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if msg != "Hello, World!" { + t.Errorf("got %q, want %q", msg, "Hello, World!") + } }) t.Run("rejects empty name", func(t *testing.T) { _, err := svc.Greet("") - require.Error(t, err) - assert.Contains(t, err.Error(), "name must not be empty") + if err == nil { + t.Fatal("expected error, got nil") + } + if !strings.Contains(err.Error(), "name must not be empty") { + t.Errorf("error %q does not contain %q", err.Error(), "name must not be empty") + } }) } -// ── Mock usage example ──────────────────────────────────────────────────────── -// The mock lives in mocks/greeter/mock_greeter.go and is generated by: -// -// make mocks -// -// This test shows how a *consumer* of the Greeter interface would use the mock. -// It lives here only for proximity to the interface definition — in a real -// project, consumer packages write their own tests with the mock. +// ── Manual fake example ─────────────────────────────────────────────────────── +// For consumers that depend on the Greeter interface, write a manual fake. +// No code generation required — just implement the interface directly. -func TestMockUsageExample(t *testing.T) { - // Import path when used in another package: - // mocks "gitea.djmil.dev/djmil/go-template/mocks/greeter" - // - // mock := mocks.NewMockGreeter(t) - // mock.EXPECT().Greet("Alice").Return("Hello, Alice!", nil) - // - // result, err := someConsumer(mock) - // require.NoError(t, err) - // assert.Equal(t, "Hello, Alice!", result) - t.Skip("mock usage — see comment above for pattern") +type fakeGreeter struct { + greetFn func(name string) (string, error) +} + +func (f *fakeGreeter) Greet(name string) (string, error) { + return f.greetFn(name) +} + +func TestFakeUsageExample(t *testing.T) { + fake := &fakeGreeter{ + greetFn: func(name string) (string, error) { + return "Hello, " + name + "!", nil + }, + } + + msg, err := fake.Greet("Alice") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if msg != "Hello, Alice!" { + t.Errorf("got %q, want %q", msg, "Hello, Alice!") + } } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 6a82e7e..c6ec9ec 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -1,8 +1,8 @@ -// Package logger wraps go.uber.org/zap with a thin, ergonomic API. +// Package logger wraps log/slog with a thin, ergonomic API. // -// The key addition over raw zap is the WithField / WithFields helpers that -// return a *Logger (not a *zap.Logger), so callers stay in the typed world and -// don't need to import zap just to attach context fields. +// The key addition over raw slog is the WithField / WithFields helpers that +// return a *Logger (not a *slog.Logger), so callers stay in the typed world +// and can chain field attachments without importing slog directly. // // Usage: // @@ -15,18 +15,18 @@ package logger import ( "fmt" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" + "io" + "log/slog" + "os" ) -// Logger is a thin wrapper around *zap.Logger. -// All zap methods (Info, Error, Debug, …) are available via embedding. +// Logger is a thin wrapper around *slog.Logger. +// All slog methods (Info, Error, Debug, Warn, …) are available via embedding. type Logger struct { - *zap.Logger + *slog.Logger } -// New creates a production-style JSON logger for the given level string. +// New creates a JSON logger writing to stderr for the given level string. // Valid levels: debug, info, warn, error. func New(level string) (*Logger, error) { lvl, err := parseLevel(level) @@ -34,65 +34,45 @@ func New(level string) (*Logger, error) { return nil, err } - cfg := zap.NewProductionConfig() - cfg.Level = zap.NewAtomicLevelAt(lvl) - // ISO8601 timestamps are human-readable and grep-friendly. - cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: lvl}) - z, err := cfg.Build(zap.AddCallerSkip(0)) - if err != nil { - return nil, fmt.Errorf("logger: build: %w", err) - } - - return &Logger{z}, nil + return &Logger{slog.New(h)}, nil } -// NewDevelopment creates a colourised, human-friendly console logger. +// NewDevelopment creates a human-friendly text logger writing to stderr. // Use this in local dev; prefer New() in any deployed environment. -func NewDevelopment() (*Logger, error) { - z, err := zap.NewDevelopment() - if err != nil { - return nil, fmt.Errorf("logger: build dev: %w", err) - } +func NewDevelopment() *Logger { + h := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}) - return &Logger{z}, nil + return &Logger{slog.New(h)} } // NewNop returns a no-op logger. Useful in tests that don't care about logs. func NewNop() *Logger { - return &Logger{zap.NewNop()} + return &Logger{slog.New(slog.NewTextHandler(io.Discard, nil))} } -// WithField returns a child logger that always includes key=value in every log -// line. value can be any type; zap.Any is used internally. +// WithField returns a child logger that always includes key=value in every log line. func (l *Logger) WithField(key string, value any) *Logger { - return &Logger{l.Logger.With(zap.Any(key, value))} + return &Logger{l.Logger.With(key, value)} } // WithFields returns a child logger enriched with every key/value in fields. // Prefer WithField for one or two fields; use WithFields for structured context // objects (e.g. attaching a request span). func (l *Logger) WithFields(fields map[string]any) *Logger { - zapFields := make([]zap.Field, 0, len(fields)) + args := make([]any, 0, len(fields)*2) for k, v := range fields { - zapFields = append(zapFields, zap.Any(k, v)) + args = append(args, k, v) } - return &Logger{l.Logger.With(zapFields...)} -} - -// Sync flushes any buffered log entries. Call this on shutdown: -// -// defer log.Sync() -func (l *Logger) Sync() { - // Intentionally ignore the error — os.Stderr sync often fails on some OSes. - _ = l.Logger.Sync() + return &Logger{l.Logger.With(args...)} } // ── helpers ─────────────────────────────────────────────────────────────────── -func parseLevel(level string) (zapcore.Level, error) { - var lvl zapcore.Level +func parseLevel(level string) (slog.Level, error) { + var lvl slog.Level if err := lvl.UnmarshalText([]byte(level)); err != nil { return lvl, fmt.Errorf("logger: unknown level %q (use debug|info|warn|error)", level) } diff --git a/mocks/greeter/mock_greeter.go b/mocks/greeter/mock_greeter.go deleted file mode 100644 index 8dab3ec..0000000 --- a/mocks/greeter/mock_greeter.go +++ /dev/null @@ -1,92 +0,0 @@ -// Code generated by mockery v2.46.0. DO NOT EDIT. -// Regenerate with: make mocks - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// MockGreeter is an autogenerated mock type for the Greeter interface. -type MockGreeter struct { - mock.Mock -} - -// MockGreeter_Expecter provides a type-safe wrapper around mock.Call. -type MockGreeter_Expecter struct { - mock *mock.Mock -} - -// EXPECT returns a typed expecter for this mock. -func (_m *MockGreeter) EXPECT() *MockGreeter_Expecter { - return &MockGreeter_Expecter{mock: &_m.Mock} -} - -// Greet provides a mock function for the Greeter.Greet method. -func (_m *MockGreeter) Greet(name string) (string, error) { - ret := _m.Called(name) - - var r0 string - var r1 error - - if rf, ok := ret.Get(0).(func(string) (string, error)); ok { - return rf(name) - } - if rf, ok := ret.Get(0).(func(string) string); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockGreeter_Greet_Call is a *mock.Call with a type-safe Run method. -type MockGreeter_Greet_Call struct { - *mock.Call -} - -// Greet sets up an expectation on the Greet method. -// -// Example: -// -// mock.EXPECT().Greet("Alice").Return("Hello, Alice!", nil) -func (_e *MockGreeter_Expecter) Greet(name any) *MockGreeter_Greet_Call { - return &MockGreeter_Greet_Call{Call: _e.mock.On("Greet", name)} -} - -// Run sets a side-effect function that is called when Greet is invoked. -func (_c *MockGreeter_Greet_Call) Run(run func(name string)) *MockGreeter_Greet_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -// Return sets the return values for the Greet mock call. -func (_c *MockGreeter_Greet_Call) Return(greeting string, err error) *MockGreeter_Greet_Call { - _c.Call.Return(greeting, err) - return _c -} - -// RunAndReturn combines Run and Return into a single call. -func (_c *MockGreeter_Greet_Call) RunAndReturn(run func(string) (string, error)) *MockGreeter_Greet_Call { - _c.Call.Return(run) - return _c -} - -// NewMockGreeter creates a new MockGreeter and registers a cleanup function -// that asserts all expectations were met when the test ends. -func NewMockGreeter(t interface { - mock.TestingT - Cleanup(func()) -}) *MockGreeter { - m := &MockGreeter{} - m.Mock.Test(t) - t.Cleanup(func() { m.AssertExpectations(t) }) - return m -} diff --git a/rename.sh b/rename.sh index eb52a86..acb4006 100755 --- a/rename.sh +++ b/rename.sh @@ -6,13 +6,13 @@ # ./rename.sh acme-corp my-svc # non-interactive (org, project) # # What it changes: -# go.mod module path -# **/*.go import paths -# .mockery.yaml package path -# config/dev.yaml app.name +# go.mod module path +# **/*.go import paths # .devcontainer/devcontainer.json name field -# README.md heading + module path references -# CLAUDE.md Module line +# README.md heading + module path references +# CLAUDE.md Module line +# .golangci.yml goimports local-prefixes +# git history squashed into one INIT commit set -euo pipefail @@ -66,14 +66,19 @@ if [[ "$CURRENT_MODULE" != "$TEMPLATE_MODULE" ]]; then echo fi +# ── Capture template version before touching anything ───────────────────────── +TEMPLATE_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "untagged") + # ── Gather inputs ───────────────────────────────────────────────────────────── heading "Go Template — Project Renamer" echo "This script rewrites the module path and project name throughout the codebase." echo +INTERACTIVE=true if [[ $# -ge 2 ]]; then NEW_ORG="$1" NEW_PROJECT="$2" + INTERACTIVE=false else while true; do read -rp "Org / username (e.g. djmil): " NEW_ORG @@ -96,12 +101,13 @@ OLD_DISPLAY=$(to_title "$OLD_PROJECT") # e.g. Go Template # ── Preview ─────────────────────────────────────────────────────────────────── heading "Changes to be applied" -printf " %-22s %s → %s\n" "Module path:" "$OLD_MODULE" "$NEW_MODULE" -printf " %-22s %s → %s\n" "Project name:" "$OLD_PROJECT" "$NEW_PROJECT" -printf " %-22s %s → %s\n" "Display name:" "$OLD_DISPLAY" "$NEW_DISPLAY" +printf " %-22s %s → %s\n" "Module path:" "$OLD_MODULE" "$NEW_MODULE" +printf " %-22s %s → %s\n" "Project name:" "$OLD_PROJECT" "$NEW_PROJECT" +printf " %-22s %s → %s\n" "Display name:" "$OLD_DISPLAY" "$NEW_DISPLAY" +printf " %-22s %s\n" "Template version:" "$TEMPLATE_TAG" echo -if [[ $# -lt 2 ]]; then +if $INTERACTIVE; then read -rp "Apply these changes? [y/N] " CONFIRM case "$CONFIRM" in [yY][eE][sS]|[yY]) ;; @@ -113,10 +119,8 @@ fi # macOS sed requires an extension argument for -i; GNU sed does not. sedi() { if sed --version &>/dev/null 2>&1; then - # GNU sed sed -i "$@" else - # BSD / macOS sed sed -i '' "$@" fi } @@ -144,43 +148,35 @@ for f in $GO_FILES; do done info "${CHANGED_GO} Go source file(s)" -# 3. .mockery.yaml — package path -if [[ -f .mockery.yaml ]]; then - sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" .mockery.yaml - info ".mockery.yaml" -fi - -# 4. config/dev.yaml — app.name -if [[ -f config/dev.yaml ]]; then - sedi "s|name: \"${OLD_PROJECT}\"|name: \"${NEW_PROJECT}\"|g" config/dev.yaml - info "config/dev.yaml" -fi - -# 5. .devcontainer/devcontainer.json — "name" field +# 3. .devcontainer/devcontainer.json — "name" field if [[ -f .devcontainer/devcontainer.json ]]; then sedi "s|\"name\": \"${OLD_DISPLAY}\"|\"name\": \"${NEW_DISPLAY}\"|g" \ .devcontainer/devcontainer.json info ".devcontainer/devcontainer.json" fi -# 6. README.md — heading + all module path occurrences +# 4. README.md — heading + all module path occurrences if [[ -f README.md ]]; then sedi "s|^# ${OLD_PROJECT}$|# ${NEW_PROJECT}|g" README.md sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" README.md - sedi "s|name: \"${OLD_PROJECT}\"|name: \"${NEW_PROJECT}\"|g" README.md info "README.md" fi -# 7. CLAUDE.md — Module line +# 5. CLAUDE.md — Module line if [[ -f CLAUDE.md ]]; then sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" CLAUDE.md info "CLAUDE.md" fi -# 8. git remote origin — rewrite URL preserving scheme (https or ssh) +# 6. .golangci.yml — goimports local-prefixes +if [[ -f .golangci.yml ]]; then + sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" .golangci.yml + info ".golangci.yml" +fi + +# 6. git remote origin — rewrite URL preserving scheme (https or ssh) if git remote get-url origin &>/dev/null 2>&1; then OLD_REMOTE=$(git remote get-url origin) - # Detect scheme and build new URL accordingly if [[ "$OLD_REMOTE" == https://* ]]; then NEW_REMOTE="https://${DEFAULT_HOST}/${NEW_ORG}/${NEW_PROJECT}.git" else @@ -193,13 +189,37 @@ else warn "No 'origin' remote found — skipping remote update." fi +# ── Squash git history into a single INIT commit ────────────────────────────── +heading "Git history" + +DO_SQUASH=true +if $INTERACTIVE; then + read -rp "Squash all template history into one INIT commit? [Y/n] " SQUASH_CONFIRM + case "$SQUASH_CONFIRM" in + [nN][oO]|[nN]) DO_SQUASH=false ;; + esac +fi + +if $DO_SQUASH; then + # Stage rename changes, fold all commits back to root, rewrite as single commit. + git add -A + ROOT_COMMIT=$(git rev-list --max-parents=0 HEAD) + git reset --soft "$ROOT_COMMIT" + git commit --amend -m "init: bootstrap ${NEW_PROJECT} from go-template ${TEMPLATE_TAG} + +Initialized from ${OLD_MODULE} @ ${TEMPLATE_TAG}. +Module renamed to ${NEW_MODULE}." + info "History squashed → single INIT commit (template: ${TEMPLATE_TAG})" +else + warn "Skipping squash — template history preserved." +fi + # ── Post-rename suggestions ─────────────────────────────────────────────────── heading "Done" echo "Module is now: ${BOLD}${NEW_MODULE}${RESET}" echo echo "Recommended next steps:" -echo " go mod tidy # sync go.sum after path change" -echo " make mocks # regenerate mocks with new import paths" -echo " make build # verify it compiles" -echo " make test # verify tests pass" -echo " git push -u origin main" +echo " go mod tidy # sync go.sum after path change" +echo " make build # verify it compiles" +echo " make test # verify tests pass" +echo " git push -u origin main --force-with-lease" diff --git a/tools.go b/tools.go index 0692921..836b127 100644 --- a/tools.go +++ b/tools.go @@ -9,6 +9,5 @@ import ( _ "github.com/go-delve/delve/cmd/dlv" _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/securego/gosec/v2/cmd/gosec" - _ "github.com/vektra/mockery/v2" _ "golang.org/x/vuln/cmd/govulncheck" )