diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/.editorconfig b/packages/shared-skills/skills/programming/scripts/go/templates/.editorconfig new file mode 100644 index 000000000..7b4853508 --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{yml,yaml,json,md}] +indent_style = space +indent_size = 2 diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/.golangci.yml b/packages/shared-skills/skills/programming/scripts/go/templates/.golangci.yml new file mode 100644 index 000000000..4eee6949b --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/.golangci.yml @@ -0,0 +1,95 @@ +version: "2" + +run: + timeout: 5m + tests: true + modules-download-mode: readonly + +linters: + default: none + enable: + - govet + - staticcheck + - errcheck + - errorlint + - nilerr + - nilnil + - bodyclose + - rowserrcheck + - sqlclosecheck + - contextcheck + - fatcontext + - copyloopvar + - intrange + - usetesting + - testifylint + - gofumpt + - goimports + - whitespace + - misspell + - unconvert + - unparam + - ineffassign + - dupword + - gocognit + - gocyclo + - funlen + - lll + - nestif + - dupl + - revive + - unused + - exhaustive + - gosec + - sloglint + - perfsprint + - prealloc + - makezero + +linters-settings: + errcheck: + check-type-assertions: true + check-blank: true + errorlint: + errorf: true + asserts: true + comparison: true + gocognit: + min-complexity: 25 + gocyclo: + min-complexity: 15 + funlen: + lines: 90 + statements: 60 + lll: + line-length: 120 + tab-width: 4 + nestif: + min-complexity: 4 + exhaustive: + default-signifies-exhaustive: false + check: [switch, map] + sloglint: + no-mixed-args: true + attr-only: true + no-global: all + context: scope + static-msg: true + no-raw-keys: true + key-naming-case: snake + +formatters: + enable: + - gofumpt + - goimports + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + exclude-rules: + - path: _test\\.go + linters: [funlen, lll, dupl, gosec] + - path: \\.pb\\.go$ + linters: [all] + - path: \\.connect\\.go$ + linters: [all] diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/AGENTS.md.tmpl b/packages/shared-skills/skills/programming/scripts/go/templates/AGENTS.md.tmpl new file mode 100644 index 000000000..3f23b4181 --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/AGENTS.md.tmpl @@ -0,0 +1,24 @@ +# AGENTS.md + +Go 1.23+ $short_purpose service. + +## Commands +- `task` — fmt + lint + test +- `task build` — produce ./bin/server +- `task ci` — full CI pipeline locally + +## Architecture +- `cmd/server/main.go` — entrypoint, ≤50 LOC +- `internal/cmd/` — root command, signal wiring +- `internal/api/` — HTTP handlers + middleware (gin) +- `internal/domain/` — smart-constructor types, no I/O +- `internal/store/` — DB layer (sqlc-generated, never hand-edited) +- `internal/config/` — env-driven Config +- `internal/obs/` — slog setup, observability + +## Conventions +- `slog` for all logs; never `log.*`, never `fmt.Println` in libs +- `context.Context` first arg for every public function with I/O +- Errors wrapped with `%w`; check with `errors.Is/As` +- 250 pure LOC ceiling per file +- Tests follow Given/When/Then; less mock the better diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/README.md.tmpl b/packages/shared-skills/skills/programming/scripts/go/templates/README.md.tmpl new file mode 100644 index 000000000..fce8b6f65 --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/README.md.tmpl @@ -0,0 +1,12 @@ +# $name + +Bootstrapped with the `programming` skill's Go scaffold. + +## Run + +```bash +task # fmt + lint + test +task run # build + run server +``` + +See `AGENTS.md` for architecture conventions. diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/Taskfile.yml b/packages/shared-skills/skills/programming/scripts/go/templates/Taskfile.yml new file mode 100644 index 000000000..daa1d59b2 --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/Taskfile.yml @@ -0,0 +1,40 @@ +version: '3' + +vars: + BINARY: server + PKG: ./cmd/server + +tasks: + default: + deps: [fmt, lint, test] + + fmt: + cmds: + - gofumpt -w . + - goimports -w -local "$(go list -m)" . + + lint: + cmds: + - golangci-lint run --timeout 5m ./... + - nilaway ./... || true + + test: + cmds: + - go test -race -shuffle=on -count=1 ./... + + test-cover: + cmds: + - go test -race -shuffle=on -count=1 -coverprofile=coverage.out ./... + - go tool cover -html=coverage.out -o coverage.html + + build: + cmds: + - go build -trimpath -ldflags="-s -w" -o bin/{{.BINARY}} {{.PKG}} + + run: + deps: [build] + cmds: + - ./bin/{{.BINARY}} + + ci: + deps: [fmt, lint, test, build] diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/ci.yml b/packages/shared-skills/skills/programming/scripts/go/templates/ci.yml new file mode 100644 index 000000000..3cdf9011f --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/ci.yml @@ -0,0 +1,37 @@ +name: ci +on: + pull_request: + push: + branches: [main] + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.23' + cache: true + + - name: Install tools + run: | + go install mvdan.cc/gofumpt@latest + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v2.0.0 + go install go.uber.org/nilaway/cmd/nilaway@latest + go install github.com/go-task/task/v3/cmd/task@latest + + - name: Format check + run: gofumpt -l . | (! grep .) + + - name: Lint + run: golangci-lint run --timeout 5m ./... + + - name: Nilaway + run: nilaway ./... || true + + - name: Test + run: go test -race -shuffle=on -count=1 ./... + + - name: Build + run: go build -trimpath ./... diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/config.go b/packages/shared-skills/skills/programming/scripts/go/templates/config.go new file mode 100644 index 000000000..f104c9e7d --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/config.go @@ -0,0 +1,24 @@ +// Package config loads typed config from env. +package config + +import ( + "time" + + "github.com/caarlos0/env/v11" +) + +type Config struct { + Host string `env:"HOST" envDefault:"0.0.0.0"` + Port int `env:"PORT" envDefault:"8080"` + ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" envDefault:"20s"` + LogLevel string `env:"LOG_LEVEL" envDefault:"info"` + LogFormat string `env:"LOG_FORMAT" envDefault:"json"` +} + +func Load() (Config, error) { + var cfg Config + if err := env.Parse(&cfg); err != nil { + return Config{}, err + } + return cfg, nil +} diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/gitignore b/packages/shared-skills/skills/programming/scripts/go/templates/gitignore new file mode 100644 index 000000000..d009c99b2 --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/gitignore @@ -0,0 +1,15 @@ +bin/ +coverage.out +coverage.html +*.test +*.prof + +.idea/ +.vscode/ +*.swp + +.env +.env.local + +*.pem +*.key diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/main.go.tmpl b/packages/shared-skills/skills/programming/scripts/go/templates/main.go.tmpl new file mode 100644 index 000000000..460befb1f --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/main.go.tmpl @@ -0,0 +1,22 @@ +package main + +import ( + "context" + "log/slog" + "os" + "os/signal" + "syscall" + + "$module/internal/cmd" +) + +func main() { + ctx, stop := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM) + defer stop() + + if err := cmd.Execute(ctx); err != nil { + slog.Error("fatal", slog.Any("err", err)) + os.Exit(1) + } +} diff --git a/packages/shared-skills/skills/programming/scripts/go/templates/run.go b/packages/shared-skills/skills/programming/scripts/go/templates/run.go new file mode 100644 index 000000000..fe9d88dc5 --- /dev/null +++ b/packages/shared-skills/skills/programming/scripts/go/templates/run.go @@ -0,0 +1,15 @@ +// Package cmd wires the root command and subcommands. +package cmd + +import ( + "context" + "log/slog" + "os" +) + +// Execute runs the root command. Wire cobra/subcommands here. +func Execute(ctx context.Context) error { + slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo})) + slog.InfoContext(ctx, "starting") + return nil +}