trim fat, use Gi philosophy

This commit is contained in:
djmil 2026-03-29 19:24:55 +00:00
parent f4ce4599aa
commit 0f75b279c3
19 changed files with 320 additions and 469 deletions

View File

@ -1,37 +1,33 @@
#!/usr/bin/env bash #!/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) # Install with: make setup (sets core.hooksPath = .githooks)
set -euo pipefail set -euo pipefail
# Collect staged Go files only — avoids re-checking untouched code. echo "pre-push: running checks..."
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..."
# ── 1. gofmt ────────────────────────────────────────────────────────────────── # ── 1. gofmt ──────────────────────────────────────────────────────────────────
echo " → gofmt" echo " → gofmt"
UNFORMATTED=$(gofmt -l $STAGED) UNFORMATTED=$(gofmt -l $(git ls-files '*.go'))
if [ -n "$UNFORMATTED" ]; then if [ -n "$UNFORMATTED" ]; then
echo " FAIL: the following files are not gofmt-formatted:" echo " FAIL: the following files are not gofmt-formatted:"
echo "$UNFORMATTED" | sed 's/^/ /' echo "$UNFORMATTED" | sed 's/^/ /'
echo " Fix with: gofmt -w <file> or make lint-fix" echo " Fix with: make lint-fix"
exit 1 exit 1
fi fi
# ── 2. golangci-lint ────────────────────────────────────────────────────────── # ── 2. go vet ─────────────────────────────────────────────────────────────────
echo " → go vet"
go vet ./...
# ── 3. golangci-lint ──────────────────────────────────────────────────────────
echo " → golangci-lint" echo " → golangci-lint"
golangci-lint run ./... golangci-lint run ./...
# ── 3. gosec (security scan) ────────────────────────────────────────────────── # ── 4. gosec (security scan) ──────────────────────────────────────────────────
echo " → gosec" echo " → gosec"
gosec -quiet ./... gosec -quiet ./...
# govulncheck is intentionally omitted from pre-commit (network + slow). # govulncheck is intentionally omitted (network + slow).
# Run it manually with: make security # Run it manually with: make security
echo "pre-commit: all checks passed." echo "pre-push: all checks passed."

View File

@ -4,8 +4,7 @@
run: run:
timeout: 5m timeout: 5m
# Align with go.mod go: '1.25'
# go: '1.24'
# Enable specific linters on top of the default set. # Enable specific linters on top of the default set.
# Default set: errcheck, gosimple, govet, ineffassign, staticcheck, unused # Default set: errcheck, gosimple, govet, ineffassign, staticcheck, unused
@ -39,7 +38,7 @@ linters-settings:
goimports: goimports:
# Put local module imports in their own group (after stdlib and external). # 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: gocritic:
disabled-checks: disabled-checks:
@ -65,13 +64,5 @@ issues:
- unparam - unparam
- gocritic - gocritic
- gosec - gosec
# Don't fail on generated files.
- path: mock_.*\.go
linters:
- all
- path: mocks/
linters:
- all
max-issues-per-linter: 50 max-issues-per-linter: 50
max-same-issues: 5 max-same-issues: 5

View File

@ -1,18 +0,0 @@
# mockery v2 configuration
# Docs: https://vektra.github.io/mockery/latest/configuration/
#
# Mocks are placed in mocks/<package>/ 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:

9
.vscode/launch.json vendored
View File

@ -8,10 +8,7 @@
"request": "launch", "request": "launch",
"mode": "auto", "mode": "auto",
"program": "${workspaceFolder}/cmd/app", "program": "${workspaceFolder}/cmd/app",
"env": { "args": ["-env", "dev", "-log-level", "debug"]
"CONFIG_PATH": "${workspaceFolder}/config/dev.yaml"
},
"args": []
}, },
{ {
// Debug: same as above but with delve attached // Debug: same as above but with delve attached
@ -20,9 +17,7 @@
"request": "launch", "request": "launch",
"mode": "debug", "mode": "debug",
"program": "${workspaceFolder}/cmd/app", "program": "${workspaceFolder}/cmd/app",
"env": { "args": ["-env", "dev", "-log-level", "debug"],
"CONFIG_PATH": "${workspaceFolder}/config/dev.yaml"
},
"showLog": true, "showLog": true,
"trace": "verbose" "trace": "verbose"
}, },

View File

@ -33,5 +33,9 @@
// Test explorer // Test explorer
"go.testExplorer.enable": true, "go.testExplorer.enable": true,
"makefile.configureOnOpen": false "makefile.configureOnOpen": false,
"cSpell.words": [
"djmil",
"gitea"
]
} }

View File

@ -7,8 +7,8 @@ Keep it concise — the agent needs signal, not essays.
## Project overview ## Project overview
Go 1.24 template / PoC starter. Demonstrates: structured logging (zap), Go 1.25 template / PoC starter. Demonstrates: structured logging (slog),
config (viper), interfaces + mocks (mockery), linting (golangci-lint), config (flag), interfaces + manual fakes, linting (golangci-lint),
security scanning (gosec, govulncheck), git hooks, devcontainer, VSCode tasks. security scanning (gosec, govulncheck), git hooks, devcontainer, VSCode tasks.
Module: `gitea.djmil.dev/djmil/go-template` — update this when you fork. 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 cmd/app/main.go composition root — wires deps, no logic here
internal/config/ Viper config loader (config.Load) internal/config/ flag-based config loader (config.Load)
internal/logger/ Zap wrapper with WithField / WithFields internal/logger/ slog wrapper with WithField / WithFields
internal/greeter/ Example domain package (delete or repurpose) 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) tools.go Tool version pinning (build tag: tools)
.golangci.yml Linter rules .golangci.yml Linter rules
.mockery.yaml Mockery code-gen config .githooks/pre-push Runs gofmt + go vet + golangci-lint + gosec before push
.githooks/pre-commit Runs gofmt + golangci-lint + gosec before commit
``` ```
--- ---
@ -38,9 +35,8 @@ tools.go Tool version pinning (build tag: tools)
- **Packages** — keep `cmd/` thin (wiring only); business logic belongs in `internal/` - **Packages** — keep `cmd/` thin (wiring only); business logic belongs in `internal/`
- **Interfaces** — define interfaces where they are *used*, not where they are *implemented* - **Interfaces** — define interfaces where they are *used*, not where they are *implemented*
- **Errors** — wrap with `fmt.Errorf("context: %w", err)`; never swallow errors silently - **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 - **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`; no hard-coded values in logic packages - **Config** — all configuration through `internal/config` (flag-parsed); no hard-coded values in logic packages
- **Secrets** — never commit `.env` files or credentials; use env var overrides of config keys
--- ---
@ -58,13 +54,13 @@ tools.go Tool version pinning (build tag: tools)
## Testing rules ## 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 - 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 - Use `logger.NewNop()` when the test doesn't care about log output
- Table-driven tests with `t.Run("description", ...)` for multiple cases - 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 - 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 ```bash
make init # first-time setup: fetch deps, install tools, git hooks make init # first-time setup: fetch deps, install tools, git hooks
make build # compile to ./bin/app 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 # run all tests
make test-race # tests + race detector make test-race # tests + race detector
make lint # golangci-lint make lint # go vet + golangci-lint
make lint-fix # auto-fix lint issues make lint-fix # go fix + golangci-lint auto-fix
make security # gosec + govulncheck make security # gosec + govulncheck
make mocks # regenerate all mocks make clean # remove bin/
make generate # run all //go:generate directives
make clean # remove bin/ and mocks/
``` ```
VSCode: `Ctrl+Shift+B` = build, `Ctrl+Shift+T` = test. VSCode: `Ctrl+Shift+B` = build, `Ctrl+Shift+T` = test.
@ -92,19 +86,17 @@ Debug: use launch config "Debug: app" (F5).
## Adding new features (checklist) ## Adding new features (checklist)
1. Define the interface in `internal/<domain>/` 1. Define the interface in `internal/<domain>/`
2. Write the implementation and its unit tests 2. Write the implementation and its unit tests (using a manual fake for the interface)
3. Add the interface to `.mockery.yaml` and run `make mocks` 3. Wire it in `cmd/app/main.go`
4. Wire it in `cmd/app/main.go` 4. Run `make lint test` before committing
5. Run `make lint test` before committing
--- ---
## Known pitfalls ## Known pitfalls
- `mocks/` are committed intentionally — regenerate after interface changes - `govulncheck` makes network calls; excluded from pre-push hook (run manually)
- `govulncheck` makes network calls; excluded from pre-commit hook (run manually)
- `config/dev.yaml` is committed but never add real credentials here
- `tools.go` has `//go:build tools` — it won't compile into the binary - `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. Keep this section to ~10 entries; remove stale items.
Format: YYYY-MM-DD — what was done and why. --> Format: YYYY-MM-DD — what was done and why. -->
- 2026-03-05 — Initial template scaffolded: config, logger, greeter example, - 2026-03-29 — Stripped to stdlib-only: removed testify/mockery→manual fakes, zap→slog, viper→flag.
mocks, git hooks, devcontainer, VSCode tasks, golangci-lint, Makefile. - 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.

View File

@ -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 ────────────────────────────────────────────────────────────────── # ── Variables ──────────────────────────────────────────────────────────────────
BINARY_NAME := app BINARY_NAME := app
BINARY_PATH := ./bin/$(BINARY_NAME) BINARY_PATH := ./bin/$(BINARY_NAME)
CMD_PATH := ./cmd/app CMD_PATH := ./cmd/app
CONFIG_PATH := ./config/dev.yaml
GO_FILES := $(shell find . -type f -name '*.go' -not -path './vendor/*')
# ── Default target ───────────────────────────────────────────────────────────── # ── Default target ─────────────────────────────────────────────────────────────
help: ## Show this help message help: ## Show this help message
@ -14,13 +12,7 @@ help: ## Show this help message
# ── First-time setup ─────────────────────────────────────────────────────────── # ── First-time setup ───────────────────────────────────────────────────────────
init: ## First-time project init: fetch deps, install tools, configure git hooks 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) ---" @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/golangci/golangci-lint/cmd/golangci-lint@latest
go get github.com/securego/gosec/v2/cmd/gosec@latest go get github.com/securego/gosec/v2/cmd/gosec@latest
go get golang.org/x/vuln/cmd/govulncheck@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) tools: ## Install tool binaries to GOPATH/bin (versions from go.mod)
@echo "Installing tools..." @echo "Installing tools..."
go install github.com/go-delve/delve/cmd/dlv@latest 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/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest go install github.com/securego/gosec/v2/cmd/gosec@latest
go install golang.org/x/vuln/cmd/govulncheck@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) go build -o $(BINARY_PATH) $(CMD_PATH)
# ── Run ──────────────────────────────────────────────────────────────────────── # ── Run ────────────────────────────────────────────────────────────────────────
run: ## Run the application with dev config run: ## Run the application with default flags
CONFIG_PATH=$(CONFIG_PATH) go run $(CMD_PATH)/main.go go run $(CMD_PATH)/main.go
# ── Test ─────────────────────────────────────────────────────────────────────── # ── Test ───────────────────────────────────────────────────────────────────────
test: ## Run all tests test: ## Run all tests
@ -64,10 +55,12 @@ test-verbose: ## Run all tests with verbose output
go test ./... -race -cover -v go test ./... -race -cover -v
# ── Lint & Security ──────────────────────────────────────────────────────────── # ── Lint & Security ────────────────────────────────────────────────────────────
lint: ## Run golangci-lint lint: ## Run go vet + golangci-lint
go vet ./...
golangci-lint run ./... 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 ./... golangci-lint run --fix ./...
security: ## Run gosec + govulncheck security: ## Run gosec + govulncheck
@ -76,14 +69,6 @@ security: ## Run gosec + govulncheck
@echo "--- govulncheck ---" @echo "--- govulncheck ---"
govulncheck ./... govulncheck ./...
# ── Code generation ────────────────────────────────────────────────────────────
generate: ## Run go generate across all packages
go generate ./...
mocks: ## Regenerate all mocks via mockery (config: .mockery.yaml)
mockery
# ── Cleanup ──────────────────────────────────────────────────────────────────── # ── Cleanup ────────────────────────────────────────────────────────────────────
clean: ## Remove build artifacts and generated mocks clean: ## Remove build artifacts
rm -rf ./bin rm -rf ./bin
rm -rf ./mocks

109
README.md
View File

@ -9,14 +9,13 @@ Clone it, rename the module, run `make init`, and you're coding.
| Area | Tool | Purpose | | Area | Tool | Purpose |
|---|---|---| |---|---|---|
| Language | Go 1.24 | Modules, toolchain directive | | Language | Go 1.25 | Modules, toolchain directive |
| Logging | [zap](https://github.com/uber-go/zap) | Structured JSON logging + `WithField` extension | | Logging | standard `log/slog` | Structured JSON/text logging + `WithField` extension |
| Config | [viper](https://github.com/spf13/viper) | YAML file + env var overlay | | Config | standard `flag` | CLI flags with defaults, no config files |
| Linting | [golangci-lint](https://golangci-lint.run) | Aggregated linters, one config file | | 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 | | 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 | standard `testing` | Table-driven tests, manual fakes, no third-party test framework |
| Tests | [testify](https://github.com/stretchr/testify) | Assertions (`assert`/`require`) + mock framework | | Git hooks | custom pre-push | gofmt + go vet + golangci-lint + gosec on every push |
| 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 | | Tool versions | `tools.go` + `go.mod` | Single source of truth for tool versions |
| Dev environment | devcontainer | Reproducible VSCode / GitHub Codespaces setup | | Dev environment | devcontainer | Reproducible VSCode / GitHub Codespaces setup |
| IDE | VSCode | Launch configs, tasks, recommended settings | | IDE | VSCode | Launch configs, tasks, recommended settings |
@ -27,7 +26,7 @@ Clone it, rename the module, run `make init`, and you're coding.
### Prerequisites ### Prerequisites
- Go 1.24+ - Go 1.25+
- Git - Git
- (Optional) [VSCode](https://code.visualstudio.com/) + [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.Go) - (Optional) [VSCode](https://code.visualstudio.com/) + [Go extension](https://marketplace.visualstudio.com/items?itemName=golang.Go)
- (Optional) Docker for devcontainer - (Optional) Docker for devcontainer
@ -52,7 +51,7 @@ make init # fetches deps, installs tools, configures git hooks
```bash ```bash
make build # compiles to ./bin/app 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 ```bash
make test # run all tests make test # run all tests
make test-race # … with race detector make test-race # … with race detector
make lint # golangci-lint make lint # go vet + golangci-lint
make lint-fix # auto-fix what can be fixed make lint-fix # go fix + golangci-lint auto-fix
make security # gosec + govulncheck 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. > **Keyboard shortcut (VSCode):** `Ctrl+Shift+B` → build, `Ctrl+Shift+T` → test.
@ -80,20 +77,15 @@ make generate # run all //go:generate directives
├── cmd/ ├── cmd/
│ └── app/ │ └── app/
│ └── main.go # Composition root (thin — just wiring) │ └── main.go # Composition root (thin — just wiring)
├── config/
│ └── dev.yaml # Local dev config (safe to commit)
├── internal/ ├── internal/
│ ├── config/ # Viper config loader │ ├── config/ # flag-based config (config.Load)
│ ├── logger/ # Zap wrapper with WithField │ ├── logger/ # slog wrapper with WithField / WithFields
│ └── greeter/ # Example domain package (replace with yours) │ └── greeter/ # Example domain package (replace with yours)
├── mocks/
│ └── greeter/ # Generated by `make mocks` — commit these
├── .devcontainer/ # VSCode / Codespaces container definition ├── .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 ├── .vscode/ # launch, tasks, editor settings
├── tools.go # Tool version pinning (build tag: tools) ├── tools.go # Tool version pinning (build tag: tools)
├── .golangci.yml # Linter configuration ├── .golangci.yml # Linter configuration
├── .mockery.yaml # Mockery configuration
└── Makefile # All development commands └── Makefile # All development commands
``` ```
@ -101,35 +93,28 @@ make generate # run all //go:generate directives
## Configuration ## Configuration
Config is loaded from a YAML file (default `config/dev.yaml`) with env var overrides. All configuration is via CLI flags with sensible defaults:
```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 ```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 ```bash
CONFIG_PATH=config/staging.yaml ./bin/app ./bin/app -port 9090 -env prod -log-level warn
``` ```
--- ---
## Logging ## 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 ```go
log.WithField("request_id", rid). log.WithField("request_id", rid).
@ -142,29 +127,32 @@ log.WithFields(map[string]any{
}).Debug("generating greeting") }).Debug("generating greeting")
``` ```
In production (`app.env != dev`) the output is JSON. In development, use In production (`app.env != dev`) the output is JSON (`slog.NewJSONHandler`).
`logger.NewDevelopment()` for coloured console output. In development, `logger.NewDevelopment()` uses the human-friendly text handler.
--- ---
## Mocking ## Testing
1. Define your interface in an `internal/` package. Tests use the standard `testing` package. For dependencies on interfaces, write
2. Add a `//go:generate mockery --name=YourInterface` directive. a manual fake inline in the test file — no code generation required:
3. Register the interface in `.mockery.yaml` under `packages:`.
4. Run `make mocks`.
Generated mocks support type-safe EXPECT() call chains:
```go ```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) { func TestSomething(t *testing.T) {
g := mocks.NewMockGreeter(t) fake := &fakeGreeter{
g.EXPECT().Greet("Alice").Return("Hello, Alice!", nil) greetFn: func(name string) (string, error) {
return "Hello, " + name + "!", nil
result, err := myConsumer(g) },
require.NoError(t, err) }
// pass fake to the system under test
} }
``` ```
@ -172,16 +160,17 @@ func TestSomething(t *testing.T) {
## Git hooks ## Git hooks
The pre-commit hook runs **gofmt + golangci-lint + gosec** before every commit. The pre-push hook runs **gofmt + go vet + golangci-lint + gosec** against the
It checks only staged Go files to keep it fast. 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 `govulncheck` is intentionally excluded (it makes network calls and can be
and can be slow). Run it manually with `make security`. slow). Run it manually with `make security`.
To skip the hook in an emergency: To skip the hook in an emergency:
```bash ```bash
git commit --no-verify -m "wip: skip hooks" git push --no-verify
``` ```
--- ---
@ -195,7 +184,7 @@ same versions.
To update a tool: To update a tool:
```bash ```bash
go get github.com/vektra/mockery/v2@latest go get github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go mod tidy go mod tidy
make tools # reinstall binaries make tools # reinstall binaries
``` ```
@ -204,8 +193,8 @@ make tools # reinstall binaries
## Devcontainer ## Devcontainer
Open this repo in VSCode and choose **"Reopen in Container"** — everything Open this repo in VSCode and choose **"Reopen in Container"** — the Go
(Go toolchain, golangci-lint, mockery, gosec, govulncheck) is installed toolchain and all tools (golangci-lint, gosec, govulncheck) are installed
automatically via `make init`. automatically via `make init`.
Works with GitHub Codespaces out of the box. Works with GitHub Codespaces out of the box.

View File

@ -21,27 +21,21 @@ func main() {
func run() error { func run() error {
// ── Config ──────────────────────────────────────────────────────────────── // ── Config ────────────────────────────────────────────────────────────────
cfgPath := os.Getenv("CONFIG_PATH") cfg := config.Load()
if cfgPath == "" {
cfgPath = "config/dev.yaml"
}
cfg, err := config.Load(cfgPath)
if err != nil {
return fmt.Errorf("loading config: %w", err)
}
// ── Logger ──────────────────────────────────────────────────────────────── // ── Logger ────────────────────────────────────────────────────────────────
var log *logger.Logger var (
log *logger.Logger
err error
)
if cfg.App.Env == "dev" { if cfg.App.Env == "dev" {
log, err = logger.NewDevelopment() log = logger.NewDevelopment()
} else { } else {
log, err = logger.New(cfg.Logger.Level) log, err = logger.New(cfg.Logger.Level)
}
if err != nil { if err != nil {
return fmt.Errorf("creating logger: %w", err) return fmt.Errorf("creating logger: %w", err)
} }
defer log.Sync() }
log.WithFields(map[string]any{ log.WithFields(map[string]any{
"app": cfg.App.Name, "app": cfg.App.Name,

View File

@ -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"

31
go.mod
View File

@ -6,12 +6,9 @@ go 1.25.0
// After cloning, rename the module path above to match your project. // After cloning, rename the module path above to match your project.
require ( require (
github.com/go-delve/delve v1.26.1
github.com/golangci/golangci-lint v1.64.8 github.com/golangci/golangci-lint v1.64.8
github.com/securego/gosec/v2 v2.24.7 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 golang.org/x/vuln v1.1.4
) )
@ -53,12 +50,15 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect github.com/charithe/durationcheck v0.0.10 // indirect
github.com/chavacava/garif v0.1.0 // 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/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/curioswitch/go-reassign v0.3.0 // indirect
github.com/daixiang0/gci v0.13.5 // indirect github.com/daixiang0/gci v0.13.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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/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/ettle/strcase v0.2.0 // indirect
github.com/fatih/color v1.18.0 // indirect github.com/fatih/color v1.18.0 // indirect
github.com/fatih/structtag v1.2.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/fzipp/gocyclo v0.6.0 // indirect
github.com/ghostiam/protogetter v0.3.9 // indirect github.com/ghostiam/protogetter v0.3.9 // indirect
github.com/go-critic/go-critic v0.12.0 // 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/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-toolsmith/astcast v1.1.0 // 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/revgrep v0.8.0 // indirect
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
github.com/google/go-cmp v0.7.0 // 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/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // 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-immutable-radix/v2 v2.1.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // 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/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/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jgautheron/goconst v1.7.1 // indirect github.com/jgautheron/goconst v1.7.1 // indirect
github.com/jingyugao/rowserrcheck v1.1.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/jjti/go-spancheck v0.6.4 // indirect
github.com/julz/importas v0.2.0 // indirect github.com/julz/importas v0.2.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.1 // 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/ldez/usetesting v0.4.2 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect github.com/leonklingele/grouper v1.1.2 // indirect
github.com/macabu/inamedparam v0.1.3 // 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/testableexamples v1.0.0 // indirect
github.com/maratori/testpackage v1.1.1 // indirect github.com/maratori/testpackage v1.1.1 // indirect
github.com/matoous/godox v1.1.0 // 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/ll v0.0.8 // indirect
github.com/olekukonko/tablewriter v1.0.7 // indirect github.com/olekukonko/tablewriter v1.0.7 // indirect
github.com/openai/openai-go/v3 v3.23.0 // 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/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/polyfloyd/go-errorlint v1.7.1 // 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/raeperd/recvcheck v0.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // 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/ryancurrah/gomodguard v1.3.5 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // 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/sanposhiho/wastedassign/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // 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/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/cobra v1.10.2 // 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/pflag v1.0.10 // indirect
github.com/spf13/viper v1.12.0 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
github.com/stretchr/objx v0.5.3 // 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/subosito/gotenv v1.6.0 // indirect
github.com/tdakkota/asciicheck v0.4.1 // indirect github.com/tdakkota/asciicheck v0.4.1 // indirect
github.com/tetafro/godot v1.5.0 // 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 v1.38.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace 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/automaxprocs v1.6.0 // indirect
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/multierr v1.11.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 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/crypto v0.48.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // 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/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect golang.org/x/sys v0.41.0 // indirect
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // 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/text v0.34.0 // indirect
golang.org/x/tools v0.42.0 // indirect golang.org/x/tools v0.42.0 // indirect
golang.org/x/tools/go/expect v0.1.1-deprecated // 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/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.8 // 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.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/tools v0.6.1 // indirect honnef.co/go/tools v0.6.1 // indirect

69
go.sum
View File

@ -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/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 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU=
github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= 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/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 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= 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/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
github.com/chigopher/pathlib v0.19.1/go.mod h1:tzC1dZLW8o33UQpWkNkhvPwL5n4yyFRFm/jL1YGWFvY= 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 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY=
github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo= 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/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 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= 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/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 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= 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 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= 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/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 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w=
github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w= 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.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 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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/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 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= 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 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 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.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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= 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/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 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 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 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 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 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk=
github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= 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 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= 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 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc=
github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk= 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= 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/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 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk=
github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= 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 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= 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/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 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= 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 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= 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/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.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= 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 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 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/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.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 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/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 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
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/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU=
github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= 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 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= 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 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= 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= 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/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 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 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.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.9/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 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= 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 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= 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= 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.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= 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/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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= 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/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 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U=
github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= 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 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 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/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 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= 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 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= 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 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 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.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 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 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-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-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-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-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-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-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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.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= 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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 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.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.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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -1,61 +1,54 @@
// Package config loads application configuration from a YAML file + env vars. // Package config parses application configuration from command-line flags.
// Priority (highest → lowest): env vars → config file → defaults. // Defaults are defined here; override at runtime with flags:
//
// ./app -port 9090 -env prod -log-level warn
// //
// Usage: // Usage:
// //
// cfg, err := config.Load("config/dev.yaml") // cfg := config.Load()
// fmt.Println(cfg.App.Port) // fmt.Println(cfg.App.Port)
package config package config
import ( import (
"fmt" "flag"
"github.com/spf13/viper"
) )
// Config is the root configuration object. Add sub-structs as the app grows. // Config is the root configuration object. Add sub-structs as the app grows.
type Config struct { type Config struct {
App AppConfig `mapstructure:"app"` App AppConfig
Logger LoggerConfig `mapstructure:"logger"` Logger LoggerConfig
} }
// AppConfig holds generic application settings. // AppConfig holds generic application settings.
type AppConfig struct { type AppConfig struct {
Name string `mapstructure:"name"` Name string
Port int `mapstructure:"port"` Port int
Env string `mapstructure:"env"` // dev | staging | prod Env string // dev | staging | prod
} }
// LoggerConfig controls logging behavior. // LoggerConfig controls logging behavior.
type LoggerConfig struct { 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 // Load parses command-line flags and returns a Config.
// variables (e.g. APP_PORT overrides app.port). // Call this once at startup before any other flag parsing.
func Load(path string) (*Config, error) { func Load() *Config {
v := viper.New() 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. return &Config{
// e.g. APP_PORT=9090 overrides app.port App: AppConfig{
v.AutomaticEnv() Name: *name,
Port: *port,
// Sensible defaults so the binary works without a config file. Env: *env,
v.SetDefault("app.name", "go-template") },
v.SetDefault("app.port", 8080) Logger: LoggerConfig{
v.SetDefault("app.env", "dev") Level: *level,
v.SetDefault("logger.level", "info") },
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("config: reading %q: %w", path, err)
} }
var cfg Config
if err := v.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("config: unmarshalling: %w", err)
}
return &cfg, nil
} }

View File

@ -1,6 +1,6 @@
// Package greeter is a minimal example domain package. // Package greeter is a minimal example domain package.
// It demonstrates how to: // 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 // - inject dependencies (logger) through a constructor
// - use the logger.WithField pattern // - use the logger.WithField pattern
// //
@ -13,8 +13,6 @@ import (
"gitea.djmil.dev/djmil/go-template/internal/logger" "gitea.djmil.dev/djmil/go-template/internal/logger"
) )
//go:generate mockery --name=Greeter
// Greeter produces a greeting for a given name. // Greeter produces a greeting for a given name.
// The interface is what other packages should depend on — never the concrete type. // The interface is what other packages should depend on — never the concrete type.
type Greeter interface { type Greeter interface {

View File

@ -1,11 +1,9 @@
package greeter_test package greeter_test
import ( import (
"strings"
"testing" "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/greeter"
"gitea.djmil.dev/djmil/go-template/internal/logger" "gitea.djmil.dev/djmil/go-template/internal/logger"
) )
@ -15,37 +13,51 @@ import (
func TestGreet(t *testing.T) { func TestGreet(t *testing.T) {
svc := greeter.New(logger.NewNop()) 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") msg, err := svc.Greet("World")
require.NoError(t, err) if err != nil {
assert.Equal(t, "Hello, World!", msg) 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) { t.Run("rejects empty name", func(t *testing.T) {
_, err := svc.Greet("") _, err := svc.Greet("")
require.Error(t, err) if err == nil {
assert.Contains(t, err.Error(), "name must not be empty") 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 ──────────────────────────────────────────────────────── // ── Manual fake example ───────────────────────────────────────────────────────
// The mock lives in mocks/greeter/mock_greeter.go and is generated by: // For consumers that depend on the Greeter interface, write a manual fake.
// // No code generation required — just implement the interface directly.
// 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.
func TestMockUsageExample(t *testing.T) { type fakeGreeter struct {
// Import path when used in another package: greetFn func(name string) (string, error)
// mocks "gitea.djmil.dev/djmil/go-template/mocks/greeter" }
//
// mock := mocks.NewMockGreeter(t) func (f *fakeGreeter) Greet(name string) (string, error) {
// mock.EXPECT().Greet("Alice").Return("Hello, Alice!", nil) return f.greetFn(name)
// }
// result, err := someConsumer(mock)
// require.NoError(t, err) func TestFakeUsageExample(t *testing.T) {
// assert.Equal(t, "Hello, Alice!", result) fake := &fakeGreeter{
t.Skip("mock usage — see comment above for pattern") 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!")
}
} }

View File

@ -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 // The key addition over raw slog is the WithField / WithFields helpers that
// return a *Logger (not a *zap.Logger), so callers stay in the typed world and // return a *Logger (not a *slog.Logger), so callers stay in the typed world
// don't need to import zap just to attach context fields. // and can chain field attachments without importing slog directly.
// //
// Usage: // Usage:
// //
@ -15,18 +15,18 @@ package logger
import ( import (
"fmt" "fmt"
"io"
"go.uber.org/zap" "log/slog"
"go.uber.org/zap/zapcore" "os"
) )
// Logger is a thin wrapper around *zap.Logger. // Logger is a thin wrapper around *slog.Logger.
// All zap methods (Info, Error, Debug, …) are available via embedding. // All slog methods (Info, Error, Debug, Warn, …) are available via embedding.
type Logger struct { 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. // Valid levels: debug, info, warn, error.
func New(level string) (*Logger, error) { func New(level string) (*Logger, error) {
lvl, err := parseLevel(level) lvl, err := parseLevel(level)
@ -34,65 +34,45 @@ func New(level string) (*Logger, error) {
return nil, err return nil, err
} }
cfg := zap.NewProductionConfig() h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: lvl})
cfg.Level = zap.NewAtomicLevelAt(lvl)
// ISO8601 timestamps are human-readable and grep-friendly.
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
z, err := cfg.Build(zap.AddCallerSkip(0)) return &Logger{slog.New(h)}, nil
if err != nil {
return nil, fmt.Errorf("logger: build: %w", err)
}
return &Logger{z}, 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. // Use this in local dev; prefer New() in any deployed environment.
func NewDevelopment() (*Logger, error) { func NewDevelopment() *Logger {
z, err := zap.NewDevelopment() h := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})
if err != nil {
return nil, fmt.Errorf("logger: build dev: %w", err)
}
return &Logger{z}, nil return &Logger{slog.New(h)}
} }
// NewNop returns a no-op logger. Useful in tests that don't care about logs. // NewNop returns a no-op logger. Useful in tests that don't care about logs.
func NewNop() *Logger { 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 // WithField returns a child logger that always includes key=value in every log line.
// line. value can be any type; zap.Any is used internally.
func (l *Logger) WithField(key string, value any) *Logger { 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. // WithFields returns a child logger enriched with every key/value in fields.
// Prefer WithField for one or two fields; use WithFields for structured context // Prefer WithField for one or two fields; use WithFields for structured context
// objects (e.g. attaching a request span). // objects (e.g. attaching a request span).
func (l *Logger) WithFields(fields map[string]any) *Logger { 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 { for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v)) args = append(args, k, v)
} }
return &Logger{l.Logger.With(zapFields...)} return &Logger{l.Logger.With(args...)}
}
// 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()
} }
// ── helpers ─────────────────────────────────────────────────────────────────── // ── helpers ───────────────────────────────────────────────────────────────────
func parseLevel(level string) (zapcore.Level, error) { func parseLevel(level string) (slog.Level, error) {
var lvl zapcore.Level var lvl slog.Level
if err := lvl.UnmarshalText([]byte(level)); err != nil { if err := lvl.UnmarshalText([]byte(level)); err != nil {
return lvl, fmt.Errorf("logger: unknown level %q (use debug|info|warn|error)", level) return lvl, fmt.Errorf("logger: unknown level %q (use debug|info|warn|error)", level)
} }

View File

@ -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
}

View File

@ -8,11 +8,11 @@
# What it changes: # What it changes:
# go.mod module path # go.mod module path
# **/*.go import paths # **/*.go import paths
# .mockery.yaml package path
# config/dev.yaml app.name
# .devcontainer/devcontainer.json name field # .devcontainer/devcontainer.json name field
# README.md heading + module path references # README.md heading + module path references
# CLAUDE.md Module line # CLAUDE.md Module line
# .golangci.yml goimports local-prefixes
# git history squashed into one INIT commit
set -euo pipefail set -euo pipefail
@ -66,14 +66,19 @@ if [[ "$CURRENT_MODULE" != "$TEMPLATE_MODULE" ]]; then
echo echo
fi fi
# ── Capture template version before touching anything ─────────────────────────
TEMPLATE_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "untagged")
# ── Gather inputs ───────────────────────────────────────────────────────────── # ── Gather inputs ─────────────────────────────────────────────────────────────
heading "Go Template — Project Renamer" heading "Go Template — Project Renamer"
echo "This script rewrites the module path and project name throughout the codebase." echo "This script rewrites the module path and project name throughout the codebase."
echo echo
INTERACTIVE=true
if [[ $# -ge 2 ]]; then if [[ $# -ge 2 ]]; then
NEW_ORG="$1" NEW_ORG="$1"
NEW_PROJECT="$2" NEW_PROJECT="$2"
INTERACTIVE=false
else else
while true; do while true; do
read -rp "Org / username (e.g. djmil): " NEW_ORG read -rp "Org / username (e.g. djmil): " NEW_ORG
@ -99,9 +104,10 @@ heading "Changes to be applied"
printf " %-22s %s → %s\n" "Module path:" "$OLD_MODULE" "$NEW_MODULE" 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" "Project name:" "$OLD_PROJECT" "$NEW_PROJECT"
printf " %-22s %s → %s\n" "Display name:" "$OLD_DISPLAY" "$NEW_DISPLAY" printf " %-22s %s → %s\n" "Display name:" "$OLD_DISPLAY" "$NEW_DISPLAY"
printf " %-22s %s\n" "Template version:" "$TEMPLATE_TAG"
echo echo
if [[ $# -lt 2 ]]; then if $INTERACTIVE; then
read -rp "Apply these changes? [y/N] " CONFIRM read -rp "Apply these changes? [y/N] " CONFIRM
case "$CONFIRM" in case "$CONFIRM" in
[yY][eE][sS]|[yY]) ;; [yY][eE][sS]|[yY]) ;;
@ -113,10 +119,8 @@ fi
# macOS sed requires an extension argument for -i; GNU sed does not. # macOS sed requires an extension argument for -i; GNU sed does not.
sedi() { sedi() {
if sed --version &>/dev/null 2>&1; then if sed --version &>/dev/null 2>&1; then
# GNU sed
sed -i "$@" sed -i "$@"
else else
# BSD / macOS sed
sed -i '' "$@" sed -i '' "$@"
fi fi
} }
@ -144,43 +148,35 @@ for f in $GO_FILES; do
done done
info "${CHANGED_GO} Go source file(s)" info "${CHANGED_GO} Go source file(s)"
# 3. .mockery.yaml — package path # 3. .devcontainer/devcontainer.json — "name" field
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
if [[ -f .devcontainer/devcontainer.json ]]; then if [[ -f .devcontainer/devcontainer.json ]]; then
sedi "s|\"name\": \"${OLD_DISPLAY}\"|\"name\": \"${NEW_DISPLAY}\"|g" \ sedi "s|\"name\": \"${OLD_DISPLAY}\"|\"name\": \"${NEW_DISPLAY}\"|g" \
.devcontainer/devcontainer.json .devcontainer/devcontainer.json
info ".devcontainer/devcontainer.json" info ".devcontainer/devcontainer.json"
fi fi
# 6. README.md — heading + all module path occurrences # 4. README.md — heading + all module path occurrences
if [[ -f README.md ]]; then if [[ -f README.md ]]; then
sedi "s|^# ${OLD_PROJECT}$|# ${NEW_PROJECT}|g" README.md sedi "s|^# ${OLD_PROJECT}$|# ${NEW_PROJECT}|g" README.md
sedi "s|${OLD_MODULE}|${NEW_MODULE}|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" info "README.md"
fi fi
# 7. CLAUDE.md — Module line # 5. CLAUDE.md — Module line
if [[ -f CLAUDE.md ]]; then if [[ -f CLAUDE.md ]]; then
sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" CLAUDE.md sedi "s|${OLD_MODULE}|${NEW_MODULE}|g" CLAUDE.md
info "CLAUDE.md" info "CLAUDE.md"
fi 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 if git remote get-url origin &>/dev/null 2>&1; then
OLD_REMOTE=$(git remote get-url origin) OLD_REMOTE=$(git remote get-url origin)
# Detect scheme and build new URL accordingly
if [[ "$OLD_REMOTE" == https://* ]]; then if [[ "$OLD_REMOTE" == https://* ]]; then
NEW_REMOTE="https://${DEFAULT_HOST}/${NEW_ORG}/${NEW_PROJECT}.git" NEW_REMOTE="https://${DEFAULT_HOST}/${NEW_ORG}/${NEW_PROJECT}.git"
else else
@ -193,13 +189,37 @@ else
warn "No 'origin' remote found — skipping remote update." warn "No 'origin' remote found — skipping remote update."
fi 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 ─────────────────────────────────────────────────── # ── Post-rename suggestions ───────────────────────────────────────────────────
heading "Done" heading "Done"
echo "Module is now: ${BOLD}${NEW_MODULE}${RESET}" echo "Module is now: ${BOLD}${NEW_MODULE}${RESET}"
echo echo
echo "Recommended next steps:" echo "Recommended next steps:"
echo " go mod tidy # sync go.sum after path change" 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 build # verify it compiles"
echo " make test # verify tests pass" echo " make test # verify tests pass"
echo " git push -u origin main" echo " git push -u origin main --force-with-lease"

View File

@ -9,6 +9,5 @@ import (
_ "github.com/go-delve/delve/cmd/dlv" _ "github.com/go-delve/delve/cmd/dlv"
_ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "github.com/securego/gosec/v2/cmd/gosec" _ "github.com/securego/gosec/v2/cmd/gosec"
_ "github.com/vektra/mockery/v2"
_ "golang.org/x/vuln/cmd/govulncheck" _ "golang.org/x/vuln/cmd/govulncheck"
) )