This guide covers the development setup and workflows for the Compass project.
The fastest way to get started with a complete development environment:
-
Prerequisites:
-
Open in Container:
F1 β "Dev Containers: Reopen in Container" -
Wait for Setup (~5-10 min first time, <30s after)
-
Start Coding! Everything is pre-configured:
- Go 1.23
- All development tools
- GCP SDK
- Zsh with helpful aliases
π Full DevContainer Documentation
If you prefer to develop locally:
- Install Go: go.dev/doc/install
- 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
- Install GCP SDK: cloud.google.com/sdk/docs/install
- Open in VS Code: Configure with the included
.vscode/settings
π VSCode Configuration Guide
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
go test ./... # Basic
go test -v ./... # Verbose
go test -v -race ./... # With race detectiongo test ./internal/output
go test -v ./internal/outputgo test -run TestSpinnerStart ./internal/output# 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.outCurrent 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% |
See the VSCode README for details on:
- Visual coverage in editor (green/red gutters)
- Debugging tests with breakpoints
- Running specific 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 failurerequire.*- Stops on failure (use for setup)
go build -v -o compass .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 .# 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 .# Format all code
go fmt ./...
# Or use goimports (preferred - organizes imports too)
goimports -w .VS Code: Code is automatically formatted on save
# Run golangci-lint
golangci-lint run ./...
# Fix auto-fixable issues
golangci-lint run --fix ./...VS Code: Linting runs automatically on save
# Run this before committing
go fmt ./...
go vet ./...
golangci-lint run ./...
go test ./...- 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
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@latestdocker build -t compass:latest .docker run --rm compass:latest --helpmain- Stable release branchdevelop- Development branchfeature/*- Feature branchesbugfix/*- Bug fix branches
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
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# 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# List instances
./compass gcp ssh --list
# Test connectivity
./compass gcp connectivity-test list
# VPN inspection
./compass gcp vpn list# 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 TestSpinnerStartSee VSCode README for:
- Setting breakpoints
- Inspecting variables
- Step-through debugging
- Conditional breakpoints
# Run benchmarks
go test -bench=. ./...
# With memory stats
go test -bench=. -benchmem ./...
# Specific benchmark
go test -bench=BenchmarkMyFunction ./internal/output# CPU profile
go test -cpuprofile=cpu.prof ./...
go tool pprof cpu.prof
# Memory profile
go test -memprofile=mem.prof ./...
go tool pprof mem.prof# Check for known vulnerabilities
go list -json -m all | nancy sleuth# Check for updates
go list -u -m all
# Update dependencies
go get -u ./...
go mod tidy# Start doc server
godoc -http=:6060
# View at http://localhost:6060Follow 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
}- Keep packages focused and cohesive
- Use internal/ for non-public packages
- Avoid circular dependencies
- Use dependency injection
// 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
}- Test public API, not internal details
- Use table-driven tests for multiple cases
- Mock external dependencies
- Aim for >70% coverage on new code
- Use benchmarks to verify optimizations
- Profile before optimizing
- Consider memory allocations
- Use sync.Pool for frequently allocated objects
Solution: Install Go from go.dev
Solution:
go mod download
go mod tidySolution:
go install github.com/go-delve/delve/cmd/dlv@latestPossible causes:
- Race conditions (run with
-race) - File path assumptions
- Time zone differences
- Environment variables
- Documentation: Check
.vscode/README.mdand.devcontainer/README.md - Go Documentation: go.dev/doc
- VS Code Go Extension: github.com/golang/vscode-go
- GCP SDK: cloud.google.com/sdk/docs
Happy coding! π