Contributing
Thank you for your interest in contributing to coverctl! This guide will help you get started.
Development Setup
Section titled “Development Setup”Prerequisites
Section titled “Prerequisites”- Go 1.25 or later
- Git
- Make (optional)
Clone and Build
Section titled “Clone and Build”git clone https://github.com/felixgeelhaar/coverctl.gitcd coverctlgo build ./...Run Tests
Section titled “Run Tests”go test ./... -coverRun Linter
Section titled “Run Linter”golangci-lint runDevelopment Workflow
Section titled “Development Workflow”1. Create a Branch
Section titled “1. Create a Branch”git checkout -b feature/your-feature2. Make Changes
Section titled “2. Make Changes”Follow the architecture guidelines.
3. Write Tests
Section titled “3. Write Tests”Practice TDD - write tests before implementation:
func TestNewFeature(t *testing.T) { // Arrange input := "test"
// Act result := NewFeature(input)
// Assert if result != expected { t.Errorf("got %v, want %v", result, expected) }}4. Verify Coverage
Section titled “4. Verify Coverage”# Run coverctl on itself./coverctl checkMaintain ≥80% coverage.
5. Commit Changes
Section titled “5. Commit Changes”Use Conventional Commits:
git commit -m "feat: add new validation option"git commit -m "fix: handle empty domain list"git commit -m "docs: update CLI reference"6. Create Pull Request
Section titled “6. Create Pull Request”- Fill out the PR template
- Link related issues
- Wait for CI to pass
- Request review
Code Style
Section titled “Code Style”Go Guidelines
Section titled “Go Guidelines”- Follow Effective Go
- Use
gofmtfor formatting - Run
golangci-lintbefore committing
Naming Conventions
Section titled “Naming Conventions”// Exported types: PascalCasetype CoverageResult struct {}
// Unexported types: camelCasetype internalState struct {}
// Methods: verb phrasesfunc (s *Service) CalculateCoverage() {}
// Interfaces: -er suffix when possibletype Runner interface {}type Parser interface {}Error Handling
Section titled “Error Handling”// Wrap errors with contextif err != nil { return fmt.Errorf("parse config: %w", err)}
// Use sentinel errors for expected conditionsvar ErrConfigNotFound = errors.New("config not found")Testing Guidelines
Section titled “Testing Guidelines”Table-Driven Tests
Section titled “Table-Driven Tests”func TestEvaluate(t *testing.T) { tests := []struct { name string policy Policy coverage map[string]CoverageStat want Result }{ { name: "all domains pass", policy: Policy{Default: DefaultPolicy{Min: 80}}, // ... }, { name: "one domain fails", // ... }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Evaluate(tt.policy, tt.coverage) if !reflect.DeepEqual(got, tt.want) { t.Errorf("got %+v, want %+v", got, tt.want) } }) }}Mock Interfaces
Section titled “Mock Interfaces”type mockRunner struct { runFunc func(ctx context.Context, opts RunOptions) (string, error)}
func (m *mockRunner) Run(ctx context.Context, opts RunOptions) (string, error) { return m.runFunc(ctx, opts)}Pull Request Process
Section titled “Pull Request Process”- CI Must Pass: All tests, linting, and coverage checks
- Review Required: At least one maintainer approval
- Squash Merge: PRs are squashed to keep history clean
- Conventional Commits: PR title follows conventional commit format
Commit Types
Section titled “Commit Types”| Type | Description |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation only |
style | Formatting, no code change |
refactor | Code restructuring |
test | Adding tests |
chore | Maintenance tasks |
Release Process
Section titled “Release Process”Releases are automated via Relicta:
- Commits to
maintrigger version analysis - Conventional commits determine version bump
- Tags are created automatically
- GitHub releases include binaries for all platforms
Getting Help
Section titled “Getting Help”- Issues: GitHub Issues
- Discussions: GitHub Discussions
License
Section titled “License”coverctl is MIT licensed. By contributing, you agree to license your contributions under the same license.