用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill golang-dev命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | golang-dev |
| description | > Use when this capability is needed. |
Go development best-practices guide. Covers library choices, build/test commands, and escape analysis.
When to use: Any Go project exposing a command-line interface.
Always use github.com/spf13/cobra. Structure:
cmd/
root.go # Root command + global flags
serve.go # Subcommand: serve
migrate.go # Subcommand: migrate
main.go # Only calls cmd.Execute()
// cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "Short description of myapp",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(InitConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default $HOME/.myapp.yaml)")
}
func InitConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, _ := os.UserHomeDir()
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.SetConfigName(".myapp")
viper.SetConfigType("yaml")
}
viper.AutomaticEnv()
_ = viper.ReadInConfig()
}
// cmd/serve.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the HTTP server",
RunE: func(cmd *cobra.Command, args []string) error {
port := viper.GetInt("port")
fmt.Printf("Listening on :%d\n", port)
// start server...
return nil
},
}
func init() {
rootCmd.AddCommand(serveCmd)
serveCmd.Flags().IntP("port", "p", 8080, "server port")
viper.BindPFlag("port", serveCmd.Flags().Lookup("port"))
}
RunE (not Run) so errors propagate instead of silently failing.PersistentFlags() for flags inherited by all subcommands; Flags() for command-specific.viper.BindPFlag() in init() to unify flag and config access.If github.com/bizshuk/gosdk is available, record every CLI invocation by wiring the metric hook into the root command before Execute():
import "github.com/bizshuk/gosdk/metric"
func init() {
metric.CobraCMDHook(rootCmd)
}
Each execution emits one metric via Prometheus remote-write to the backend configured by the METRIC_URL viper key (default: VictoriaMetrics http://localhost:8428/api/v1/write; override with viper.Set("METRIC_URL", ...), the APP_METRIC_URL env var when using config.Default(), or METRIC_URL env var with viper.AutomaticEnv()):
command_line_trigger{cmd="myapp sub leaf", flag="env-verbose"} = 1
cmd — full command chain, root → leaf (cmd.CommandPath()).flag — flags the user actually set, collected across the whole chain, alphabetically sorted, joined with -; empty string when no flag was set.PersistentPreRunE on root. If any subcommand defines its own PersistentPreRunE, set cobra.EnableTraverseRunHooks = true (once, in init()), otherwise cobra skips the root hook and no metric is emitted for that subcommand.monitor sub command is used to overall monitoring for the command, with --merge is one time fetch and merge into pre defined filesWhen to use: Any Go project that needs configuration management.
IMPORTANT: If github.com/bizshuk/gosdk is available, always use its config.Default() for configuration loading. Fall back to raw viper manual setup only if the SDK is not supported or available.
For database access, use the db package. Each storage type is a service with its own global singleton and a flat <TYPE>_<FIELD> viper key:
import "github.com/bizshuk/gosdk/db"
config.Default()
if viper.IsSet("SQLITE_PATH") {
if err := db.InitSQLite(); err != nil { /* handle */ }
}
if viper.IsSet("MYSQL_DSN") {
if err := db.InitMySQL(); err != nil { /* handle */ }
}
if viper.IsSet("POSTGRES_DSN") {
if err := db.InitPostgres(); err != nil { /* handle */ }
}
// Anywhere later in the process:
gormDB := db.DefaultSQLite.DB()
defer db.DefaultSQLite.Close()
YAML (flat keys, uppercase underscore):
SQLITE_PATH: ./app.db
Env var (with APP_ prefix from config.Default()): APP_SQLITE_PATH, APP_MYSQL_DSN, APP_POSTGRES_DSN.
Why a singleton per storage type: micro-service concept forbids two of the same storage type in one process. Init<Storage>() refuses double-init and returns an error if called twice. The *gorm.DB is cached in the singleton, so any code path that needs it just reads db.DefaultSQLite.DB() instead of opening its own connection.
Always use github.com/spf13/viper. Loading precedence (highest wins):
Environment variables (viper.AutomaticEnv())CLI flags (bound via viper.BindPFlag())Config file (YAML preferred)Defaults (viper.SetDefault())For non-DB settings, unmarshal nested config (e.g., server.* blocks) into a typed struct. Database access uses the db package above; do not include DB fields here.
type Config struct {
Server ServerConfig `mapstructure:"server"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
ReadTimeout int `mapstructure:"read_timeout"`
}
func LoadConfig() (*Config, error) {
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("unmarshal config: %w", err)
}
return &cfg, nil
}
# .myapp.yaml
server:
port: 8080
read_timeout: 30
Env prefix: Call viper.SetEnvPrefix("MYAPP") so env vars like MYAPP_SERVER_PORT map correctly.Nested keys in env: Use viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) so server.port maps to MYAPP_SERVER_PORT.Type mismatch: viper.Unmarshal uses mapstructure tags, not json or yaml tags.| Category | Library | When to use |
|---|---|---|
| Logging | go.uber.org/zap | Structured logging. zap.NewProduction() for JSON, zap.NewDevelopment() for console. |
| Testing | github.com/stretchr/testify | assert (continue on fail), require (stop on fail). |
| Linting | golangci-lint | Meta-linter. Install: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest. |
| HTTP | net/http (stdlib) | Prefer stdlib. Use github.com/gin-gonic/gin only when routing complexity justifies it. |
| Hot Reload | github.com/air-verse/air | Dev-time file watcher. Run air instead of go run. |
| Mocking | github.com/bytedance/mockey | Runtime monkey-patching for tests. |
| CLI | github.com/spf13/cobra | CLI scaffolding (see Section 1). |
| Config | github.com/spf13/viper | Config management (see Section 2). |
func NewLogger(isDev bool) (*zap.Logger, error) {
if isDev {
return zap.NewDevelopment()
}
return zap.NewProduction()
}
func TestGetUser(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
user, err := GetUser(ctx, "123")
require.NoError(err) // stops test if err != nil
assert.Equal("Alice", user.Name) // continues even if fails
}
Use mockey to mock functions and methods. Always execute mocking code within mockey.PatchConcurrently to ensure thread safety:
func TestGetUserDiscount(t *testing.T) {
mockey.PatchConcurrently(t, func() {
// Mock a package-level function
mockey.Mock(FetchUserFromDB).Return(&User{ID: "123", Age: 65}, nil).Build()
discount, err := GetUserDiscount("123")
assert.NoError(t, err)
assert.Equal(t, 0.2, discount)
})
}
| Task | Command |
|---|---|
| Standard build | go build ./... |
| Production binary | go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Static binary (no CGO) | CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Version injection | go build -ldflags="-X main.version=1.2.3 -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" |
| Cross compile (Linux) | GOOS=linux GOARCH=amd64 go build -o bin/app-linux ./cmd/app |
| Race detector | go build -race ./... |
-s strips symbol table, -w strips DWARF debug info — ~30-40% smaller binary.CGO_ENABLED=0 produces a fully static binary (no libc dependency) — ideal for scratch/distroless Docker images.-race enables the race detector — use in CI but NOT in production (10x slowdown).DO NOT create interface for pure test or coverage
| Task | Command |
|---|---|
| Run all tests | go test ./... |
| Verbose | go test -v ./... |
| Race detector | go test -race ./... |
| Coverage | go test -cover -coverprofile=coverage.out ./... |
| View coverage HTML | go tool cover -html=coverage.out |
| Specific test | go test -run TestFunctionName ./pkg/... |
| Benchmarks | go test -bench=. -benchmem -run=^$ ./... |
| Short mode | go test -short ./... |
| Disable caching | go test -count=1 ./... |
| Timeout | go test -timeout 30s ./... |
| Disable inlining | go test -gcflags="all=-N -l" ./... |
go test -gcflags="all=-N -l" ./...
-N disables all compiler optimizations.-l disables inlining (function calls remain as actual calls, not inlined).all= applies the flags to all packages being compiled, not just the test package. Without all=, only the direct test target gets the flags — dependencies may still be inlined.Use cases:
dlv (delve) — requires non-inlined frames for accurate breakpoints and variable inspection.mockey (or other monkey-patching libraries) rewrite function machine code at runtime. If a target function is inlined or optimized, the patch will fail because there is no independent function entry point to rewrite.When to use: Investigating heap allocations, optimizing memory-sensitive hot paths.
# Basic: shows escape decisions
go build -gcflags='-m' ./...
# Detailed: shows reasoning for each decision
go build -gcflags='-m=2' ./...
# Specific package
go build -gcflags='-m=2' ./pkg/handler/...
# Filter for escapes only
go build -gcflags='-m=2' ./... 2>&1 | grep "escapes to heap"
| Output | Cause | Fix |
|---|---|---|
leaking param: x | Param stored beyond function scope | Avoid storing pointer params in long-lived structs |
moved to heap: too large | Stack frame exceeds ~10MB | Break into smaller allocations or use sync.Pool |
moved to heap: captured by closure | Variable captured in closure | Pass value explicitly as parameter |
moved to heap: interface conversion | Concrete assigned to any/interface{} | Use concrete types in hot paths |
&x escapes to heap | Returning address of local | Return value instead of pointer when possible |
go build -gcflags='-m=2' ./... 2>&1 | grep "escapes to heap" to find escaping allocations.hot paths — handlers, loops, frequently called functions. Don't optimize cold code.go test -bench=. -benchmem to measure allocs/op before and after changes.Reference: Stack allocation ~0.26ns vs heap ~10.55ns — ~40x penalty per escaped allocation.
| Task | Command |
|---|---|
| Build (dev) | go build ./... |
| Build (prod) | go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Build (static) | CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Test | go test ./... |
| Test (race) | go test -race ./... |
| Test (cover) | go test -cover -coverprofile=coverage.out ./... |
| Test (no inline) | go test -gcflags="all=-N -l" ./... |
| Test (bench) | go test -bench=. -benchmem -run=^$ ./... |
| Escape analysis | go build -gcflags='-m=2' ./... |
| Lint | golangci-lint run ./... |
| Vet | go vet ./... |
| Format | gofmt -w . or goimports -w . |
| Hot reload | air |
| Cross compile | GOOS=linux GOARCH=amd64 go build -o bin/app ./cmd/app |
Source: BizShuk/gosdk — distributed by TomeVault.