[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# Application Configuration with Cobra + Viper
|
||||
|
||||
→ See `samber/cc-skills-golang@golang-cli` skill for complete Cobra+Viper setup, flag binding, precedence rules, and configuration layering.
|
||||
|
||||
## Where Config Lives
|
||||
|
||||
```
|
||||
myapp/
|
||||
├── cmd/myapp/
|
||||
│ ├── main.go # Entry point
|
||||
│ ├── root.go # Root command + Viper init
|
||||
│ ├── serve.go # Subcommand with flags
|
||||
│ └── config.go # Config struct + loader
|
||||
└── configs/
|
||||
└── config.yaml # Default config file
|
||||
```
|
||||
|
||||
## Config Struct
|
||||
|
||||
Define configuration as a struct with `mapstructure` tags matching your YAML keys:
|
||||
|
||||
```go
|
||||
// cmd/myapp/config.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Port int `mapstructure:"port"`
|
||||
Host string `mapstructure:"host"`
|
||||
LogLevel string `mapstructure:"log-level"`
|
||||
Database struct {
|
||||
DSN string `mapstructure:"dsn"`
|
||||
MaxConn int `mapstructure:"max-conn"`
|
||||
} `mapstructure:"database"`
|
||||
}
|
||||
|
||||
func loadConfig() (Config, error) {
|
||||
var cfg Config
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
return Config{}, fmt.Errorf("unmarshaling config: %w", err)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
```
|
||||
|
||||
Configuration MUST be loaded from env vars, files, or flags — NEVER hardcoded. Sensitive values MUST come from env vars or secret managers, NEVER config files.
|
||||
@@ -0,0 +1,151 @@
|
||||
# Directory Layouts
|
||||
|
||||
## Universal Layout (Most Projects)
|
||||
|
||||
```
|
||||
project/
|
||||
├── cmd/ # Entry points - ONE subdirectory per main package
|
||||
│ ├── server/ # Main application #1
|
||||
│ │ └── main.go
|
||||
│ ├── client/ # Main application #2
|
||||
│ │ └── main.go
|
||||
│ └── migrate/ # Main application #3
|
||||
│ └── main.go
|
||||
│ └── cli/ # Main application #4
|
||||
│ └── main.go
|
||||
│ └── worker/ # Main application #5
|
||||
│ └── main.go
|
||||
├── internal/ # Private application code (`internal/` MUST be used for non-exported packages)
|
||||
│ ├── app/ # Application initialization
|
||||
│ ├── config/ # Configuration loading
|
||||
│ ├── handler/ # HTTP/request handlers
|
||||
│ ├── model/ # Data models/domain
|
||||
│ └── service/ # Business logic
|
||||
├── pkg/ # Public libraries (optional - only if useful to others)
|
||||
│ └── logger/
|
||||
│ └── logger.go
|
||||
├── api/ # API definitions (optional)
|
||||
│ └── openapi.yaml
|
||||
├── configs/ # Configuration files (optional)
|
||||
│ └── config.yaml
|
||||
├── scripts/ # Build/deployment scripts (optional)
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── Makefile # Build automation
|
||||
├── .gitignore # Git ignore patterns
|
||||
├── .golangci.yml # Linter configuration
|
||||
├── LICENSE # License file
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Small Projects (Single Binary)
|
||||
|
||||
For simple tools, keep it minimal:
|
||||
|
||||
```
|
||||
my-tool/
|
||||
├── cmd/
|
||||
│ └── my-tool/
|
||||
│ └── main.go # Single main package
|
||||
├── internal/
|
||||
│ └── core.go # Application logic
|
||||
├── go.mod
|
||||
├── Makefile # Build automation (optional but recommended)
|
||||
├── .gitignore # Git ignore patterns
|
||||
├── .golangci.yml # Linter configuration (optional)
|
||||
├── LICENSE # License file (recommended)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Libraries (Reusable Code)
|
||||
|
||||
```
|
||||
my-library/
|
||||
├── example/ # Example
|
||||
├── logger/ # Public package
|
||||
│ ├── logger.go
|
||||
│ └── logger_test.go
|
||||
├── internal/
|
||||
│ └── impl/ # Private implementation details
|
||||
│ └── core.go
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── Makefile # Build automation
|
||||
├── .gitignore # Git ignore patterns
|
||||
├── .golangci.yml # Linter configuration
|
||||
├── LICENSE # License file
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**Key points for libraries:**
|
||||
|
||||
- Put public API in root-level directories (e.g., `logger/`)
|
||||
- Use `internal/` for private implementation
|
||||
- Don't use `cmd/` (unless you have example binaries)
|
||||
|
||||
## The cmd/ Directory Convention
|
||||
|
||||
**CRITICAL**: All `main` packages must reside in `cmd/`. `cmd/` MUST contain only `main.go` with minimal logic — parse flags, wire dependencies, call `Run()`. NEVER put business logic in `cmd/` — it belongs in `internal/` or `pkg/`.
|
||||
|
||||
### Single Application
|
||||
|
||||
```
|
||||
cmd/
|
||||
└── myapp/
|
||||
└── main.go // package main
|
||||
```
|
||||
|
||||
### Multiple Applications
|
||||
|
||||
When you need multiple binaries (e.g., server, CLI tool, migration utility):
|
||||
|
||||
```
|
||||
cmd/
|
||||
├── server/
|
||||
│ └── main.go // Runs the API server
|
||||
├── client/
|
||||
│ └── main.go // CLI client tool
|
||||
├── worker/
|
||||
│ └── main.go // Background worker
|
||||
└── migrate/
|
||||
└── main.go // Database migration utility
|
||||
```
|
||||
|
||||
Each `main.go`:
|
||||
|
||||
- Declares `package main`
|
||||
- Has its own `func main()`
|
||||
- Can be built independently: `go build ./cmd/...`
|
||||
|
||||
**Building all binaries:**
|
||||
|
||||
```bash
|
||||
go build ./cmd/... # Build all main packages
|
||||
go build ./cmd/server # Build specific binary
|
||||
```
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
### Don't Do This
|
||||
|
||||
```
|
||||
myproject/
|
||||
├── src/ # Go doesn't use /src (Java pattern)
|
||||
├── main.go # Don't put main at root
|
||||
├── utils/ # Generic package name
|
||||
├── helpers/ # Generic package name
|
||||
└── common/ # Generic package name
|
||||
```
|
||||
|
||||
### Do This Instead
|
||||
|
||||
```
|
||||
myproject/
|
||||
├── cmd/
|
||||
│ └── myapp/
|
||||
│ └── main.go # Main in cmd/
|
||||
├── internal/
|
||||
│ ├── util/ # Specific utility names
|
||||
│ └── format/ # Or domain-specific names
|
||||
└── pkg/ # Only if useful to others
|
||||
```
|
||||
@@ -0,0 +1,215 @@
|
||||
# Tests, Benchmarks, and Examples
|
||||
|
||||
## File Naming Conventions
|
||||
|
||||
Go uses suffix-based naming for test-related files:
|
||||
|
||||
| Suffix | Purpose | Build Tag |
|
||||
| --- | --- | --- |
|
||||
| `_test.go` | Tests | Not included in normal builds |
|
||||
| `_bench_test.go` | Benchmarks | Not included in normal builds |
|
||||
| `_example_test.go` | Examples that verify output | Not included in normal builds |
|
||||
| No suffix | Regular code | Included in all builds |
|
||||
|
||||
## Where to Place Tests
|
||||
|
||||
**Co-locate tests with the code they test:**
|
||||
|
||||
```
|
||||
internal/
|
||||
├── handler/
|
||||
│ ├── handler.go # Production code
|
||||
│ ├── handler_test.go # Tests for handler
|
||||
│ └── handler_bench_test.go # Benchmarks (optional)
|
||||
├── service/
|
||||
│ ├── service.go
|
||||
│ └── service_test.go
|
||||
└── model/
|
||||
├── user.go
|
||||
└── user_test.go
|
||||
|
||||
pkg/
|
||||
└── logger/
|
||||
├── logger.go
|
||||
└── logger_test.go
|
||||
```
|
||||
|
||||
**Key principles:**
|
||||
|
||||
- Tests live in the **same package** as the code (e.g., `package handler`)
|
||||
- Test files are in the **same directory** as the code they test
|
||||
- Use `_test.go` suffix for all test files
|
||||
|
||||
## Test Package Options
|
||||
|
||||
When writing tests, you have two options for the package declaration:
|
||||
|
||||
**Option 1: Same package (white-box testing)**
|
||||
|
||||
```go
|
||||
package handler // Same package, can access unexported
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
// Can access unexported functions and types
|
||||
internalFunction()
|
||||
}
|
||||
```
|
||||
|
||||
**Option 2: Package with `_test` suffix (black-box testing)**
|
||||
|
||||
```go
|
||||
package handler_test // Different package, only exported API
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
// Can only access exported functions and types
|
||||
handler.PublicMethod()
|
||||
}
|
||||
```
|
||||
|
||||
**When to use each:**
|
||||
|
||||
- Use **same package** for unit tests that need to test internals
|
||||
- Use **`_test` suffix** for integration/behavioral tests
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Benchmarks use the `_bench_test.go` suffix and contain functions with the `Benchmark` prefix.
|
||||
|
||||
## Examples
|
||||
|
||||
Examples serve two purposes: documentation and verification.
|
||||
|
||||
**In libraries** - use `*_example_test.go` files:
|
||||
|
||||
```
|
||||
pkg/
|
||||
└── logger/
|
||||
├── logger.go
|
||||
├── logger_test.go
|
||||
└── logger_example_test.go # Examples
|
||||
```
|
||||
|
||||
**Example function format:**
|
||||
|
||||
```go
|
||||
package logger
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleLogger_Info() {
|
||||
log := New()
|
||||
log.Info("processing started")
|
||||
log.Info("processing complete")
|
||||
// Output:
|
||||
// INFO: processing started
|
||||
// INFO: processing complete
|
||||
}
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
|
||||
- Example functions must start with `Example`
|
||||
- The `// Output:` comment verifies the output
|
||||
- Examples are runnable tests: `go test` will fail if output doesn't match
|
||||
- `godoc` displays examples as documentation
|
||||
- File name format: `{package}_example_test.go` (e.g., `logger_example_test.go`)
|
||||
|
||||
**For executable examples** (standalone demo programs):
|
||||
|
||||
```
|
||||
examples/
|
||||
└── basic-usage/
|
||||
└── main.go # Executable example
|
||||
```
|
||||
|
||||
## Test Utilities
|
||||
|
||||
When you have shared test helpers, use a dedicated package:
|
||||
|
||||
```
|
||||
test/
|
||||
└── testutils/
|
||||
├── mock.go
|
||||
└── fixtures.go
|
||||
```
|
||||
|
||||
Or use the `internal/testutil` pattern:
|
||||
|
||||
```
|
||||
internal/
|
||||
└── testutil/
|
||||
├── mock.go
|
||||
└── fixtures.go
|
||||
```
|
||||
|
||||
## Test Fixtures
|
||||
|
||||
Fixtures are test data files used across multiple tests. Use one of these patterns:
|
||||
|
||||
**Option 1: Local testdata directory** (package-specific fixtures)
|
||||
|
||||
```
|
||||
internal/
|
||||
└── handler/
|
||||
├── handler.go
|
||||
├── handler_test.go
|
||||
└── testdata/
|
||||
├── users.json
|
||||
├── request_valid.json
|
||||
└── request_invalid.json
|
||||
```
|
||||
|
||||
**Option 2: Global test directory** (shared across packages)
|
||||
|
||||
```
|
||||
test/
|
||||
└── fixtures/
|
||||
├── users.json
|
||||
├── products.json
|
||||
└── responses/
|
||||
├── success.json
|
||||
└── error.json
|
||||
```
|
||||
|
||||
**Option 3: Embedded fixtures** (Go 1.16+, use `//go:embed`)
|
||||
|
||||
```
|
||||
internal/
|
||||
└── handler/
|
||||
├── handler.go
|
||||
├── handler_test.go
|
||||
└── testdata/
|
||||
└── users.json
|
||||
```
|
||||
|
||||
**Important notes:**
|
||||
|
||||
- Go ignores the `testdata` directory when building regular packages
|
||||
- Use `testdata/` for package-specific test data
|
||||
- Use `test/fixtures/` for cross-package shared fixtures
|
||||
- Don't put `.go` files in `testdata/` - they will be ignored
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
go test ./... # Run all tests
|
||||
go test ./internal/handler # Test specific package
|
||||
go test -v ./... # Verbose output
|
||||
go test -race ./... # Race detection
|
||||
go test -cover ./... # Coverage report
|
||||
go test -short ./... # Skip long-running tests
|
||||
```
|
||||
|
||||
## Test File Summary
|
||||
|
||||
| File Type | Suffix | Package | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| Test | `*_test.go` | `package X` or `package X_test` | Unit/integration tests |
|
||||
| Benchmark | `*_bench_test.go` | Same as code | Performance tests |
|
||||
| Example (godoc) | `*_example_test.go` | Same as code | Documentation + verification |
|
||||
| Executable example | No suffix | `package main` | Standalone demo programs |
|
||||
| Test utilities | `*_test.go` | `package testutil` | Shared test helpers |
|
||||
@@ -0,0 +1,106 @@
|
||||
<!-- markdownlint-disable ol-prefix -->
|
||||
|
||||
# Go Workspaces for Multi-Package Repositories
|
||||
|
||||
## When to Use Workspaces
|
||||
|
||||
Use Go workspaces (`go.work`) when:
|
||||
|
||||
- Developing multiple related modules that import each other
|
||||
- Building a monorepo with separate Go modules
|
||||
- Testing local changes across module boundaries
|
||||
- Avoiding `replace` directives in every module
|
||||
|
||||
**Don't use workspaces for:**
|
||||
|
||||
- Single-module projects
|
||||
- Projects that only use external dependencies
|
||||
- Simple applications
|
||||
|
||||
## Workspace Structure
|
||||
|
||||
Example monorepo with multiple modules:
|
||||
|
||||
```
|
||||
my-monorepo/
|
||||
├── go.work # Workspace file (see below)
|
||||
├── pkg/
|
||||
│ ├── auth/ # Module 1: github.com/user/my-monorepo/pkg/auth
|
||||
│ │ ├── go.mod
|
||||
│ │ ├── cmd/
|
||||
│ │ │ └── auth-server/
|
||||
│ │ │ └── main.go
|
||||
│ │ └── internal/
|
||||
│ │ └── handler/
|
||||
│ │ └── auth.go
|
||||
│ └── user/ # Module 2: github.com/user/my-monorepo/pkg/user
|
||||
│ ├── go.mod
|
||||
│ ├── cmd/
|
||||
│ │ └── user-server/
|
||||
│ │ └── main.go
|
||||
│ └── internal/
|
||||
│ └── handler/
|
||||
│ └── user.go
|
||||
├── cmd/
|
||||
│ └── api/ # Module 3: github.com/user/my-monorepo/cmd/api
|
||||
│ ├── go.mod
|
||||
│ └── main.go
|
||||
└── tools/
|
||||
└── cli/ # Module 4: github.com/user/my-monorepo/tools/cli
|
||||
├── go.mod
|
||||
└── cmd/
|
||||
└── mycli/
|
||||
└── main.go
|
||||
```
|
||||
|
||||
## Creating a Workspace
|
||||
|
||||
1. **Initialize the workspace:**
|
||||
|
||||
```bash
|
||||
go work init
|
||||
```
|
||||
|
||||
This creates `go.work`:
|
||||
|
||||
```go
|
||||
go 1.21
|
||||
|
||||
use (
|
||||
./services/auth
|
||||
./services/user
|
||||
./shared/libs
|
||||
./tools/cli
|
||||
)
|
||||
```
|
||||
|
||||
2. **Add modules to workspace:**
|
||||
|
||||
```bash
|
||||
go work use ./services/auth
|
||||
go work use ./services/user
|
||||
go work use ./shared/libs
|
||||
```
|
||||
|
||||
3. **Use modules without replace directives:**
|
||||
|
||||
In `services/user/go.mod`:
|
||||
|
||||
```go
|
||||
module github.com/user/my-monorepo/services/user
|
||||
|
||||
go 1.21
|
||||
|
||||
require github.com/user/my-monorepo/shared/libs v0.0.0
|
||||
```
|
||||
|
||||
The workspace automatically resolves `shared/libs` to the local directory.
|
||||
|
||||
## Workspace Commands
|
||||
|
||||
```bash
|
||||
go work init # Initialize new workspace
|
||||
go work use ./path/to/mod # Add module to workspace
|
||||
go work use -rm ./path # Remove module from workspace
|
||||
go work sync # Sync workspace with module changes
|
||||
```
|
||||
Reference in New Issue
Block a user