Skip to main content

Development Guide

Complete guide for setting up your development environment and working on Kuve.

Prerequisites

Required Software

ToolVersionPurpose
Go1.25+Development language
GitAnyVersion control
MakeAnyBuild automation

Optional Tools

ToolPurpose
golangci-lintCode linting
goplsLanguage server (for IDE)
delveDebugging

Setup Development Environment

Install Go

  • Install Go for your OS from the official site
  • Or use a version manager like gvm or asdf

Fork and Clone

# Fork on GitHub first
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/kuve.git
cd kuve

# Add upstream remote
git remote add upstream https://github.com/germainlefebvre4/kuve.git

# Verify remotes
git remote -v

Install Dependencies

# Download Go modules
go mod download

# Verify
go mod verify

Build Project

# Build binary
make build

# Verify build
./kuve --version

Install Development Tools

# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# Install delve (debugger)
go install github.com/go-delve/delve/cmd/dlv@latest

# Verify
golangci-lint --version
dlv version

Development Workflow

Daily Workflow

# 1. Update your fork
git fetch upstream
git checkout main
git merge upstream/main

# 2. Create feature branch
git checkout -b feature/my-feature

# 3. Make changes
# Edit files...

# 4. Test locally
make test

# 5. Build and install
make install

# 6. Manual testing
kuve list installed
kuve install v1.28.0

# 7. Commit changes
git add .
git commit -m "feat: add my feature"

# 8. Push to fork
git push origin feature/my-feature

# 9. Create Pull Request on GitHub

Making Changes

Edit Code

# Use your favorite editor
code . # VS Code
vim internal/version/manager.go

Format Code

# Auto-format all Go files
make fmt

# Or manually
go fmt ./...

Run Tests

# All tests
make test

# Specific package
go test ./internal/version

# With verbose output
go test -v ./...

# With coverage
make test-coverage

Run Linter

# Using make
make lint

# Or directly
golangci-lint run

Project Structure

Key Directories

kuve/
├── cmd/ # CLI commands
├── internal/ # Internal packages
│ ├── kubectl/ # kubectl management
│ └── version/ # Version operations
├── pkg/ # Public packages
│ └── config/ # Configuration
├── docs/ # Documentation
├── .github/ # GitHub Actions
├── main.go # Entry point
├── Makefile # Build automation
└── go.mod # Go modules

Adding New Code

New Command

# Create new command file
touch cmd/newcommand.go
package cmd

import (
"fmt"
"github.com/spf13/cobra"
)

func init() {
rootCmd.AddCommand(newCmd)
}

var newCmd = &cobra.Command{
Use: "new [args]",
Short: "Short description",
Long: `Long description of what the command does.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// Implementation
fmt.Printf("New command with arg: %s\n", args[0])
},
}

New Internal Package

# Create package directory
mkdir -p internal/mypackage

# Create Go file
touch internal/mypackage/myfile.go
package mypackage

// MyFunction does something useful.
func MyFunction(input string) (string, error) {
// Implementation
return result, nil
}

Tests

# Create test file
touch internal/mypackage/myfile_test.go
package mypackage

import "testing"

func TestMyFunction(t *testing.T) {
tests := []struct {
name string
input string
expected string
wantErr bool
}{
{
name: "valid input",
input: "test",
expected: "result",
wantErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := MyFunction(tt.input)

if (err != nil) != tt.wantErr {
t.Errorf("MyFunction() error = %v, wantErr %v", err, tt.wantErr)
return
}

if result != tt.expected {
t.Errorf("MyFunction() = %v, want %v", result, tt.expected)
}
})
}
}

Build System

Makefile Targets

# Build binary
make build

# Install to system
make install

# Run tests
make test

# Test with coverage
make test-coverage

# Format code
make fmt

# Run linter
make lint

# Clean build artifacts
make clean

# Build for all platforms
make build-all

# Download dependencies
make deps

# Show help
make help

Manual Commands

# Build
go build -o kuve main.go

# Run tests
go test ./...

# Install
go install

# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o kuve-linux-amd64

# Cross-compile for macOS
GOOS=darwin GOARCH=amd64 go build -o kuve-darwin-amd64

Testing

Unit Tests

# Run all tests
go test ./...

# Run specific package
go test ./internal/version

# Run specific test
go test -run TestNormalizeVersion ./internal/version

# Verbose output
go test -v ./...

# With coverage
go test -cover ./...
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

Integration Tests

# Build and install
make install

# Test real usage
kuve install v1.28.0
kuve switch v1.28.0
kuve current
kuve list installed
kuve uninstall v1.28.0

Test Coverage

# Generate coverage
make test-coverage

# View in browser
go tool cover -html=coverage.out

Debugging

Using Delve

# Start debugger
dlv debug

# Debug specific test
dlv test ./internal/version -- -test.run TestNormalizeVersion

# Breakpoint commands
(dlv) break main.main
(dlv) continue
(dlv) next
(dlv) step
(dlv) print variable
(dlv) quit
import "fmt"

func MyFunction() {
fmt.Printf("Debug: value = %+v\n", value)
}

Logging

import "log"

func MyFunction() {
log.Printf("Processing version: %s", version)
}

Common Development Tasks

Add a New Flag

var myFlag string

func init() {
myCmd.Flags().StringVarP(&myFlag, "my-flag", "m", "", "Description")
}

Add Configuration Option

// In pkg/config/config.go
const NewOption = "value"

// In NewConfig()
config.NewOption = "default"

Modify Version Detection

// In internal/version/manager.go
func (vm *VersionManager) DetectVersion() (string, error) {
// Your implementation
}

Best Practices

Code Organization

  • Keep functions small (<50 lines)
  • Single responsibility per function
  • Clear, descriptive names
  • Add comments for exported functions

Error Handling

// Good: Wrap errors with context
if err != nil {
return fmt.Errorf("failed to download kubectl v%s: %w", version, err)
}

// Bad: Return raw error
if err != nil {
return err
}

Testing

  • Test happy path
  • Test error conditions
  • Test edge cases
  • Use table-driven tests

Documentation

  • Doc comments for exported items
  • Update README for new features
  • Update CLI help text
  • Add examples in docs/

Troubleshooting Development Issues

Build Fails

# Clean and rebuild
make clean
go mod tidy
make build

Tests Fail

# Check test output
go test -v ./...

# Run specific failing test
go test -v -run TestName ./package

Import Issues

# Update dependencies
go mod tidy
go mod download

IDE Issues

# Regenerate gopls cache
rm -rf ~/.cache/gopls

Release Process

(For maintainers)

# 1. Update version
git tag v0.2.0

# 2. Push tag
git push origin v0.2.0

# 3. GitHub Actions will:
# - Build binaries
# - Create release
# - Upload artifacts

Resources

Go Resources

Cobra Resources

Testing Resources

Getting Help

Next Steps