- Go 1.24+
- Use
gofmtandgoimports - Follow standard Go project layout
- Use
internal/for private packages - Structured logging with
log/slog
Create internal/cli/newcmd.go:
package cli
import (
"context"
"github.com/urfave/cli/v3"
)
func newCommand() *cli.Command {
return &cli.Command{
Name: "newcmd",
Usage: "Description of command",
Flags: []cli.Flag{
// flags here
},
Action: func(ctx context.Context, cmd *cli.Command) error {
// implementation
return nil
},
}
}Register in internal/app/app.go:
Commands: []*cli.Command{
cli.ValidateCommand(),
cli.VersionCommand(),
cli.CompletionCommand(),
cli.NewCommand(), // Add here
},Add to config struct in internal/config/config.go:
type Config struct {
DirStructure DirStructureConfig
FileNamingPattern FileNamingConfig
NewRule NewRuleConfig // Add new rule
Ignore []string
}
type NewRuleConfig struct {
SomeOption string `yaml:"someOption" json:"someOption"`
Patterns []string `yaml:"patterns" json:"patterns"`
}Add validation in internal/validator/validator.go:
func ValidateNewRule(cfg *config.Config, rootPath string) (int, int, []string) {
var successes, failures int
var errors []string
// validation logic
return successes, failures, errors
}Call from internal/cli/validate.go.
Add to command's Flags slice:
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "new-flag",
Usage: "Enable new feature",
Sources: cli.EnvVars("STRUCTLINT_NEW_FLAG"),
},
},Access in action:
if cmd.Bool("new-flag") {
// handle flag
}Use binary-first approach:
func TestNewFeature(t *testing.T) {
bin := buildBinary(t)
projectFiles := map[string]string{
"file.go": "package main",
}
configContent := `...`
projectDir := createTestProject(t, projectFiles, configContent)
out, err := runBinaryInDir(t, bin, projectDir,
"validate",
"--config", ".structlint.yaml",
)
if err != nil {
t.Errorf("Failed: %v\nOutput: %s", err, out)
}
}go test ./... -v # All tests
go test ./test/... -v # Integration tests only
go test -run TestName # Specific testmake build- Build for current platformmake build-all- Build for all platformsmake test- Run testsmake lint- Run lintermake clean- Remove build artifacts
Version info injected via LDFLAGS:
// internal/build/info.go
var (
Version = "dev"
Commit = "none"
Date = "unknown"
BuiltBy = "local"
)Set at build time:
go build -ldflags "-X .../build.Version=v1.0.0"- Return errors, don't panic
- Use structured logging for debug info
- Exit codes: 0=pass, 1=fail, 2=config error, 3=runtime error
- Tests pass:
go test ./... - Lint passes:
make lint - Self-validates:
make test-self - Documentation updated if needed
- Conventional commit message (feat/fix/docs/etc)