# AGENTS.md

## Build and Test Commands

### Essential Setup
```bash
# Check Go version (requires 1.26.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
