197 lines
5.0 KiB
Markdown
197 lines
5.0 KiB
Markdown
# AGENTS.md
|
|
|
|
## Build and Test Commands
|
|
|
|
### Essential Setup
|
|
```bash
|
|
# Check Go version (requires 1.24.2+)
|
|
go version
|
|
|
|
# Check Helm dependency (required at runtime)
|
|
helm version # Must show version 3.x
|
|
|
|
# Install gci tool for import formatting
|
|
go install github.com/daixiang0/gci@latest
|
|
```
|
|
|
|
### Build Commands
|
|
```bash
|
|
# Standard build
|
|
make build
|
|
|
|
# Direct build
|
|
go build -o helmfile .
|
|
|
|
# Cross-platform builds
|
|
make cross
|
|
|
|
# Build test tools (required for integration tests)
|
|
make build-test-tools
|
|
```
|
|
|
|
### Linting and Formatting
|
|
```bash
|
|
# Run go vet (always available)
|
|
make check
|
|
|
|
# Format code with gci
|
|
make fmt
|
|
|
|
# Run golangci-lint
|
|
golangci-lint run
|
|
```
|
|
|
|
### Testing Commands
|
|
```bash
|
|
# Run all unit tests
|
|
make test
|
|
|
|
# Run specific test package
|
|
go test -v ./pkg/app/...
|
|
|
|
# Run single test function
|
|
go test -v ./pkg/app/... -run TestSpecificFunction
|
|
|
|
# Run tests with coverage
|
|
go test -v ./pkg/... -coverprofile cover.out -race -p=1
|
|
go tool cover -func cover.out
|
|
|
|
# Run integration tests (requires Kubernetes cluster)
|
|
make integration
|
|
```
|
|
|
|
## Code Style Guidelines
|
|
|
|
### Imports
|
|
Import ordering (enforced by gci):
|
|
1. Standard library (stdlib)
|
|
2. Default (third-party)
|
|
3. Local (github.com/helmfile/helmfile prefix)
|
|
|
|
Always use aliases for common stdlib packages to avoid conflicts:
|
|
```go
|
|
import (
|
|
goContext "context" // Alias to avoid naming conflicts
|
|
goruntime "runtime"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/helmfile/helmfile/pkg/app"
|
|
)
|
|
```
|
|
|
|
### Formatting
|
|
- Use `go fmt` for standard formatting
|
|
- Use `gci` for import organization: `gci write --skip-generated -s standard -s default -s 'prefix(github.com/helmfile/helmfile)' .`
|
|
- Run `make fmt` before committing (requires gci installation)
|
|
|
|
### Types
|
|
- Exported types use PascalCase: `type App struct`
|
|
- Private types use PascalCase: `type helmKey struct`
|
|
- Interface names should describe behavior: `type Interface interface`
|
|
- Use `any` instead of `interface{}`
|
|
|
|
### Naming Conventions
|
|
- Exported functions/variables: PascalCase
|
|
- Private functions/variables: camelCase
|
|
- Constants: PascalCase
|
|
- Test functions: TestXxx with descriptive names
|
|
- Package names: lowercase, single word when possible
|
|
- Error variables: ErrXxx
|
|
|
|
### Error Handling
|
|
- Use fmt.Errorf with %w for wrapping errors
|
|
- Check errors explicitly; never ignore
|
|
- Use custom error types in pkg/errors/ when appropriate
|
|
- Return error as last return value
|
|
```go
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse helm version '%s': %w", version, err)
|
|
}
|
|
```
|
|
|
|
### Testing
|
|
- Use testify/assert for assertions: `assert.Equal(t, expected, actual)`
|
|
- Use testify/require for critical assertions: `require.NoError(t, err)`
|
|
- Test files: *_test.go
|
|
- Use table-driven tests for multiple scenarios
|
|
- Run tests with -race flag: `-race -p=1`
|
|
- Use test helper packages: pkg/testutil, pkg/testhelper
|
|
```go
|
|
func TestFunctionName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid input",
|
|
input: "test",
|
|
want: "result",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := FunctionUnderTest(tt.input)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
### Logging
|
|
- Use zap.SugaredLogger: `logger.Infof("message")`
|
|
- Logger injected via dependency injection
|
|
- Don't use fmt.Print for production logging
|
|
|
|
### Comments
|
|
- Exported functions must have comments
|
|
- Comments should be complete sentences with capital first letter
|
|
- Use // for single-line comments
|
|
- Use /* */ for package documentation
|
|
|
|
### Linting Configuration (from .golangci.yaml)
|
|
Key enabled linters:
|
|
- errcheck: Check unhandled errors
|
|
- staticcheck: Advanced static analysis
|
|
- revive: Fast linter
|
|
- govet: Go vet checks
|
|
- ineffassign: Detect ineffectual assignments
|
|
- misspell: Spell checking
|
|
- unused: Detect unused code
|
|
|
|
Important thresholds:
|
|
- Max function length: 280 lines
|
|
- Max statements per function: 140
|
|
- Max cognitive complexity: 110
|
|
- Max naked return lines: 50
|
|
- Line length: 120 characters
|
|
|
|
### Structure
|
|
- Package main: Entry point only (main.go)
|
|
- cmd/: CLI commands using cobra
|
|
- pkg/: Core library code organized by domain
|
|
- test/: Integration and E2E tests
|
|
- Use dependency injection for testability
|
|
- Prefer composition over inheritance
|
|
|
|
### Critical Rules
|
|
1. Always handle errors
|
|
2. Run `make check` before committing
|
|
3. Run `golangci-lint run` and fix all issues
|
|
4. Write tests for new pkg/ functionality
|
|
5. Update docs/ for user-facing changes
|
|
6. Follow declarative design principles (desired state in config, operational via flags)
|
|
|
|
### Common Issues
|
|
- First build downloads 200+ packages (2-3 minutes)
|
|
- Integration tests require Kubernetes cluster (minikube/kind)
|
|
- Make fmt requires gci installation
|
|
- Use -p=1 for tests to avoid race conditions
|
|
- Always initialize Helm plugins with `helmfile init` after installation
|