Skip to content

omarluq/og-template

Repository files navigation

og-template banner

Go Reference Go Version Go Report Card
License: MIT Tests Version DeepWiki Made with Love

What's Included

Category Tool
CLI Cobra + Fang v2 (styled help, manpages, completions)
Config Viper (YAML + env vars + defaults)
DI samber/do v2 (lazy dependency injection)
Functional samber/lo (map, filter, reduce)
Monads samber/mo (Option, Result, Either)
Errors samber/oops (structured errors with context)
Logging zerolog + slog-zerolog bridge
Testing testify (assert + require)
Linting golangci-lint v2 (50+ linters, strict config)
Tasks Task (build, test, lint, ci)
Tools mise (Go, Task, golangci-lint, lefthook versions)
Hooks Lefthook (pre-commit, pre-push, conventional commits)
Release GoReleaser v2 (cross-compile, checksums, changelog)
CI/CD GitHub Actions (lint + test + build matrix + release)
Deps Renovate (automated dependency updates)
AI Skills cc-skills-golang (opinionated agentic coding skills in .agents/)
Init huh + lipgloss (interactive project setup via task init)

Quick Start

Use this template

Click "Use this template" on GitHub, then:

git clone git@github.com:yourname/yourproject.git
cd yourproject

Initialize your project

Run the interactive init task to rename module, binary, and env prefix:

mise install          # Install Go, Task, golangci-lint, lefthook
task init             # Rename + deps + git hooks
task ci               # Verify everything works

task init uses huh to prompt for your module path, binary name, env prefix, and which AI coding assistant harnesses to enable (.adal, .augment, .claude, etc.). For each selected harness, it creates symlinks from .<harness>/skills/ into .agents/skills/. After setup, it rewrites all files, renames cmd/og-template/, runs go mod tidy, downloads deps, installs git hooks, and cleans up after itself (removes cmd/init/ and the init task from Taskfile.yml).

Project Structure

.
β”œβ”€β”€ .agents/skills/       # AI coding skills (cc-skills-golang, symlinked per harness)
β”œβ”€β”€ cmd/og-template/            # CLI entrypoint and commands
β”‚   β”œβ”€β”€ main.go           #   fang.Execute with signal handling
β”‚   β”œβ”€β”€ root.go           #   Root cobra command
β”‚   β”œβ”€β”€ config.go         #   config show/validate commands
β”‚   └── version.go        #   version command
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ config/           # Viper config loading + validation
β”‚   β”‚   β”œβ”€β”€ config.go     #   Config struct + Validate()
β”‚   β”‚   └── loader.go     #   Load() returns mo.Result[*Config]
β”‚   β”œβ”€β”€ di/               # samber/do dependency injection
β”‚   β”‚   β”œβ”€β”€ container.go  #   Root container with oops errors
β”‚   β”‚   β”œβ”€β”€ register.go   #   Service registration
β”‚   β”‚   β”œβ”€β”€ config_service.go
β”‚   β”‚   └── logger_service.go  # zerolog + slog bridge
β”‚   └── vinfo/            # Build version metadata (ldflags)
β”œβ”€β”€ .github/workflows/
β”‚   β”œβ”€β”€ ci.yml            # Lint + test + cross-platform build
β”‚   └── release.yml       # GoReleaser on tag push
β”œβ”€β”€ Taskfile.yml          # build, test, lint, fmt, ci, clean, init
β”œβ”€β”€ .golangci.yml         # 50+ linters, strict settings
β”œβ”€β”€ .goreleaser.yaml      # Cross-compile + changelog + archives
β”œβ”€β”€ .mise.toml            # Pinned tool versions
β”œβ”€β”€ lefthook.yml          # Pre-commit, pre-push, conventional commits
└── config.example.yaml   # Example configuration

Tasks

task              # List all tasks
task init         # Rename + deps + hooks (first-time only)
task build        # Build binary with ldflags
task run          # Build and run
task test         # Run tests with race detector
task test-short   # Run short tests only
task test-coverage # Tests + coverage HTML report
task lint         # golangci-lint
task fmt          # golangci-lint --fix
task ci           # fmt + lint + test + build
task deps         # Download dependencies
task tidy         # go mod tidy
task clean        # Remove all artifacts and caches

Opinions & How Things Work

CLI: Fang v2 + Cobra

The CLI uses Cobra for command structure and Fang v2 as a wrapper that adds styled help pages, automatic --version flag, manpage generation via a hidden man subcommand, and shell completions out of the box.

Entry point (cmd/og-template/main.go):

  • Sets up signal handling (SIGINT, SIGTERM)
  • Calls fang.Execute() which wraps the root Cobra command
  • Returns exit code 1 on error, 0 on success

Configuration: Viper + mo.Result

Configuration loads from multiple sources with this precedence:

  1. --config CLI flag (explicit path)
  2. Environment variables prefixed with OGTEMPLATE_ (e.g. OGTEMPLATE_APP_NAME, OGTEMPLATE_LOGGING_LEVEL)
  3. config.yaml in the current directory
  4. $HOME/.config/og-template/config.yaml
  5. Built-in defaults (development mode, info logging, pretty format)

The loader returns mo.Result[*Config] for monadic error handling. Config is validated after loading β€” invalid values produce clear error messages.

og-template config show       # Display all resolved config values
og-template config validate   # Check config is valid

Dependency Injection: samber/do v2

Services are registered lazily in internal/di/register.go and resolved on first use. The container pattern:

  1. NewContainer(configPath) creates the root injector
  2. RegisterServices() registers all providers (config, logger, etc.)
  3. Services resolve dependencies via do.MustInvoke[T](injector) in their constructors
  4. ShutdownWithContext() tears down all services gracefully

To add a new service: create it in internal/yourpkg/, register with do.Provide(injector, NewYourService) in register.go.

Error Handling: samber/oops

Errors use samber/oops for structured context throughout the DI and config layers:

return nil, oops.
    In("config").           // domain
    Code("invalid_config"). // machine-readable code
    Wrapf(err, "load configuration")

This gives you stack traces, domain context, and error codes without losing the original error chain.

Logging: zerolog + slog Bridge

The logger service creates a zerolog.Logger and bridges it to Go's slog via samber/slog-zerolog. This means:

  • Use slog in application code (stdlib, portable)
  • Get zerolog's performance and structured output under the hood
  • Pretty console output in development, JSON in production
  • Controlled via logging.level (debug/info/warn/error) and logging.format (pretty/json)

Functional Utilities: samber/lo + samber/mo

  • lo: Generic slice/map operations β€” lo.Map, lo.Filter, lo.SliceToMap, lo.MaxBy, lo.Uniq, etc.
  • mo: Monadic types β€” mo.Option[T], mo.Result[T], mo.Either[L, R]. Config loading returns mo.Result for composable error handling.

Git Hooks: Lefthook

Three hooks are installed via lefthook install:

pre-commit (runs in parallel):

  • golangci-lint run --fix on staged .go files (auto-stages fixes)
  • task test-short for fast feedback

pre-push:

  • task test (full test suite with race detector)
  • task build (ensures the binary compiles)

commit-msg:

  • Enforces Conventional Commits format
  • Valid types: feat, fix, docs, style, refactor, perf, test, chore, ci, build
  • Format: type(scope?): subject
  • Examples: feat: add user auth, fix(config): handle missing file, chore: bump deps

Hooks skip on merge and rebase to avoid friction.

Linting: golangci-lint v2

The .golangci.yml enables 50+ linters with strict settings and no exclusions on test files:

Setting Value Why
gocyclo.min-complexity 10 Keep functions simple
gocognit.min-complexity 15 Enforce readability
funlen.lines 80 Short functions
funlen.statements 50 Short functions
lll.line-length 120 Reasonable line width
dupl.threshold 100 Catch copy-paste
errcheck.check-type-assertions true No unchecked type casts
errcheck.check-blank true No _ = err
exhaustruct project packages only Catch missing struct fields

Only protobuf (.pb.go) and generated (_generated.go) files are excluded.

Build: ldflags Version Injection

task build injects version metadata via -ldflags:

  • Version β€” from git describe --tags --always --dirty
  • Commit β€” from git rev-parse --short HEAD
  • BuildDate β€” UTC timestamp

The internal/vinfo package exposes String() which formats these for --version output. Falls back to debug.ReadBuildInfo() for go install builds.

CI/CD: GitHub Actions

CI (.github/workflows/ci.yml):

  • Triggers on push/PR to main/master
  • Runs golangci-lint via official action
  • Runs tests with coverage (uploads to Codecov)
  • Cross-compiles build matrix: linux/darwin/windows Γ— amd64/arm64
  • Uses go-version-file: go.mod (always matches local Go version)
  • Concurrency groups cancel superseded runs

Release (.github/workflows/release.yml):

  • Triggers on v*.*.* tag push
  • Runs GoReleaser v2 to build, archive, generate changelog, and publish GitHub release

Release: GoReleaser v2

goreleaser builds for linux/darwin/windows (amd64 + arm64), creates tar.gz archives (zip for Windows), generates SHA-256 checksums, and publishes a GitHub release with a conventional-commit-based changelog grouped by type (features, fixes, performance).

git tag v0.1.0
git push origin v0.1.0  # Triggers release workflow

Tool Versions: mise

All tool versions are pinned in .mise.toml:

  • Go β€” pinned to specific patch version
  • Task β€” pinned
  • golangci-lint β€” pinned
  • lefthook β€” pinned

Run mise install to get the exact versions. No global installs needed.

Local Caches

Build caches are kept per-project (not in $HOME) for isolation:

  • .gocache/ β€” Go build cache
  • .gomodcache/ β€” Go module cache
  • .tmp/ β€” golangci-lint cache, temp files

All are gitignored. task clean removes everything.

Dependency Updates: Renovate

Renovate is pre-configured with config:recommended. Once enabled on your GitHub repo, it will automatically open PRs for dependency updates in go.mod.

AI Coding Skills

The .agents/skills/ directory contains cc-skills-golang β€” a curated set of agentic coding skills for AI assistants working in Go codebases. These provide opinionated guidance for code generation, testing patterns, and project conventions.

.agents/skills/ is the single source of truth. At task init, you pick which AI coding assistants you use and the init tool creates .<harness>/skills/ symlink directories pointing into .agents/skills/. Supported harnesses include .adal, .augment, .claude, .codebuddy, .continue, .cortex, .crush, .factory, .goose, .iflow, .junie, .kilocode, .kiro, .kode, .openhands, .qoder, .qwen, .roo, .trae, .windsurf, .zencoder, and more.

License

MIT

About

🐹 Omar's Opinionated Go project template with batteries included.

Resources

License

Stars

Watchers

Forks

Contributors

Languages