Skip to content

Latest commit

Β 

History

History
478 lines (373 loc) Β· 10.3 KB

File metadata and controls

478 lines (373 loc) Β· 10.3 KB

Development Guide

This guide covers the development setup and workflows for the Compass project.

πŸš€ Quick Start

Option 1: DevContainer (Recommended)

The fastest way to get started with a complete development environment:

  1. Prerequisites:

  2. Open in Container:

    F1 β†’ "Dev Containers: Reopen in Container"
    
  3. Wait for Setup (~5-10 min first time, <30s after)

  4. Start Coding! Everything is pre-configured:

    • Go 1.23
    • All development tools
    • GCP SDK
    • Zsh with helpful aliases

πŸ“– Full DevContainer Documentation

Option 2: Local Setup

If you prefer to develop locally:

  1. Install Go: go.dev/doc/install
  2. Install Tools:
    go install golang.org/x/tools/cmd/goimports@latest
    go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
    go install github.com/go-delve/delve/cmd/dlv@latest
  3. Install GCP SDK: cloud.google.com/sdk/docs/install
  4. Open in VS Code: Configure with the included .vscode/ settings

πŸ“– VSCode Configuration Guide

πŸ“¦ Project Structure

compass/
β”œβ”€β”€ .devcontainer/       # Docker development environment
β”‚   β”œβ”€β”€ devcontainer.json
β”‚   β”œβ”€β”€ Dockerfile
β”‚   β”œβ”€β”€ docker-compose.yml
β”‚   β”œβ”€β”€ post-create.sh
β”‚   └── README.md
β”œβ”€β”€ .vscode/            # VS Code configuration
β”‚   β”œβ”€β”€ settings.json   # Workspace settings
β”‚   β”œβ”€β”€ launch.json     # Debug configurations
β”‚   β”œβ”€β”€ tasks.json      # Build tasks
β”‚   └── README.md       # VSCode usage guide
β”œβ”€β”€ cmd/                # Command implementations
β”œβ”€β”€ internal/           # Internal packages
β”‚   β”œβ”€β”€ cache/         # SQLite-based caching (instances, zones, projects, subnets)
β”‚   β”‚   └── migrations/ # Database schema migrations (v2-v4)
β”‚   β”œβ”€β”€ gcp/           # GCP API clients
β”‚   β”œβ”€β”€ logger/        # Logging infrastructure
β”‚   β”œβ”€β”€ output/        # Output formatting
β”‚   β”œβ”€β”€ ssh/           # SSH utilities
β”‚   β”œβ”€β”€ update/        # Self-update logic
β”‚   └── version/       # Version information
β”œβ”€β”€ main.go            # Application entry point
└── go.mod             # Go module definition

πŸ§ͺ Testing

Running Tests

All Tests

go test ./...                    # Basic
go test -v ./...                 # Verbose
go test -v -race ./...           # With race detection

Specific Package

go test ./internal/output
go test -v ./internal/output

Single Test

go test -run TestSpinnerStart ./internal/output

With Coverage

# Generate coverage report
go test -coverprofile=coverage.out ./...

# View in terminal
go tool cover -func=coverage.out

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

Coverage by Package

Current coverage status:

Package Coverage
internal/logger 90.2%
internal/ssh 74.4%
internal/output 72.0%
internal/cache 54.4%
internal/update 52.9%
cmd 21.4%
internal/gcp 20.9%
internal/version 100.0%

Testing in VS Code

See the VSCode README for details on:

  • Visual coverage in editor (green/red gutters)
  • Debugging tests with breakpoints
  • Running specific tests

Writing Tests

Follow these conventions:

// File: mypackage/myfile_test.go
package mypackage

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestMyFunction(t *testing.T) {
    t.Run("descriptive_test_case", func(t *testing.T) {
        // Arrange
        input := "test"

        // Act
        result := MyFunction(input)

        // Assert
        assert.Equal(t, "expected", result)
    })
}

Use testify assertions:

  • assert.* - Continues on failure
  • require.* - Stops on failure (use for setup)

πŸ”¨ Building

Build Binary

go build -v -o compass .

Build with Version Info

VERSION=$(git describe --tags)
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
go build -ldflags "-X main.Version=$VERSION -X main.BuildTime=$BUILD_TIME" -o compass .

Cross-Compilation

# Linux
GOOS=linux GOARCH=amd64 go build -o compass-linux-amd64 .

# Windows
GOOS=windows GOARCH=amd64 go build -o compass-windows-amd64.exe .

# macOS
GOOS=darwin GOARCH=amd64 go build -o compass-darwin-amd64 .
GOOS=darwin GOARCH=arm64 go build -o compass-darwin-arm64 .

🎨 Code Quality

Formatting

# Format all code
go fmt ./...

# Or use goimports (preferred - organizes imports too)
goimports -w .

VS Code: Code is automatically formatted on save

Linting

# Run golangci-lint
golangci-lint run ./...

# Fix auto-fixable issues
golangci-lint run --fix ./...

VS Code: Linting runs automatically on save

Pre-commit Checks

# Run this before committing
go fmt ./...
go vet ./...
golangci-lint run ./...
go test ./...

πŸ”§ Development Tools

Installed in DevContainer

  • goimports - Import management and formatting
  • golangci-lint - Comprehensive linter
  • delve (dlv) - Go debugger
  • air - Live reload for development
  • task - Task runner (Taskfile)
  • gotestsum - Enhanced test output
  • staticcheck - Static analysis
  • mockery - Mock generation

Installing Tools Locally

go install golang.org/x/tools/cmd/goimports@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/go-delve/delve/cmd/dlv@latest
go install github.com/cosmtrek/air@latest
go install github.com/go-task/task/v3/cmd/task@latest
go install gotest.tools/gotestsum@latest

🐳 Docker

Build Docker Image

docker build -t compass:latest .

Run in Container

docker run --rm compass:latest --help

πŸ“ Git Workflow

Branches

  • main - Stable release branch
  • develop - Development branch
  • feature/* - Feature branches
  • bugfix/* - Bug fix branches

Commit Messages

Follow Conventional Commits:

feat: add VPN tunnel status command
fix: correct coverage calculation in output
docs: update development guide
test: add tests for spinner component
refactor: simplify GCP client initialization

Pre-commit Hook

The devcontainer automatically sets up a pre-commit hook that runs tests:

#!/bin/bash
echo "Running tests before commit..."
go test ./...
if [ $? -ne 0 ]; then
    echo "Tests failed. Commit aborted."
    exit 1
fi

🌐 GCP Development

Authentication

# Login to GCP
gcloud auth login

# Set project
gcloud config set project YOUR_PROJECT_ID

# Application default credentials (for API calls)
gcloud auth application-default login

Testing GCP Features

# List instances
./compass gcp ssh --list

# Test connectivity
./compass gcp connectivity-test list

# VPN inspection
./compass gcp vpn list

πŸ› Debugging

Using Delve Directly

# Debug main package
dlv debug .

# Debug with arguments
dlv debug . -- gcp ssh my-instance

# Debug tests
dlv test ./internal/output

# Debug specific test
dlv test ./internal/output -- -test.run TestSpinnerStart

Debug in VS Code

See VSCode README for:

  • Setting breakpoints
  • Inspecting variables
  • Step-through debugging
  • Conditional breakpoints

πŸ“Š Performance

Benchmarking

# Run benchmarks
go test -bench=. ./...

# With memory stats
go test -bench=. -benchmem ./...

# Specific benchmark
go test -bench=BenchmarkMyFunction ./internal/output

Profiling

# CPU profile
go test -cpuprofile=cpu.prof ./...
go tool pprof cpu.prof

# Memory profile
go test -memprofile=mem.prof ./...
go tool pprof mem.prof

πŸ” Security

Vulnerability Scanning

# Check for known vulnerabilities
go list -json -m all | nancy sleuth

Dependency Updates

# Check for updates
go list -u -m all

# Update dependencies
go get -u ./...
go mod tidy

πŸ“š Documentation

Generate Go Docs

# Start doc server
godoc -http=:6060

# View at http://localhost:6060

Code Comments

Follow Go documentation conventions:

// MyFunction does something useful.
// It takes a string parameter and returns an error.
//
// Example:
//   result, err := MyFunction("input")
//   if err != nil {
//       return err
//   }
func MyFunction(input string) error {
    // Implementation
}

🎯 Best Practices

Code Organization

  • Keep packages focused and cohesive
  • Use internal/ for non-public packages
  • Avoid circular dependencies
  • Use dependency injection

Error Handling

// Good
if err != nil {
    return fmt.Errorf("failed to connect: %w", err)
}

// Use errors.Is for checking
if errors.Is(err, ErrNotFound) {
    // Handle not found
}

Testing

  • Test public API, not internal details
  • Use table-driven tests for multiple cases
  • Mock external dependencies
  • Aim for >70% coverage on new code

Performance

  • Use benchmarks to verify optimizations
  • Profile before optimizing
  • Consider memory allocations
  • Use sync.Pool for frequently allocated objects

πŸ†˜ Troubleshooting

Common Issues

"Command not found: go"

Solution: Install Go from go.dev

"Module not found"

Solution:

go mod download
go mod tidy

"Delve not working"

Solution:

go install github.com/go-delve/delve/cmd/dlv@latest

"Tests failing in CI but passing locally"

Possible causes:

  • Race conditions (run with -race)
  • File path assumptions
  • Time zone differences
  • Environment variables

πŸ“ž Getting Help


Happy coding! πŸš€