3.9 KiB
3.9 KiB
Quickstart: Safety, Reliability & Idiomatic Refactor
Feature Branch: 001-safety-reliability-refactor
Date: 2026-03-03
Prerequisites
- Go 1.20 toolchain installed (must be exactly 1.20.x, not newer)
- Administrator/root privileges (required to write the hosts file)
- Git
Setup
# Clone and switch to feature branch
git checkout 001-safety-reliability-refactor
# Verify Go version
go version
# Expected: go version go1.20.x ...
# Download dependencies
go mod download
# Run all tests
go test ./...
# Build
go build -o dns-helper .
Project Structure After Refactor
dns-helper/
├── main.go # Entry point, CLI parsing, os.Exit
├── resolver/
│ ├── resolver.go # DNS resolution via golang.org/x/net/dns/dnsmessage
│ └── resolver_test.go
├── hostfile/
│ ├── hostfile.go # Hosts file read/write (atomic pattern)
│ ├── hostfile_test.go
│ ├── managed.go # Managed block parsing, add, remove
│ └── managed_test.go
├── platform/
│ ├── platform.go # Interface definition
│ ├── platform_windows.go # Windows hosts file path
│ ├── platform_linux.go # Linux hosts file path
│ ├── platform_darwin.go # macOS hosts file path
│ └── platform_test.go
├── lockfile/
│ ├── lockfile.go # Lock file acquisition/release
│ └── lockfile_test.go
├── go.mod # go 1.20, golang.org/x/net v0.35.0
└── go.sum
Key Implementation Patterns
TDD Workflow (Constitution Principle VI)
Every change follows Red-Green-Refactor:
- Red: Write a failing test that specifies the desired behavior.
- Green: Write the minimum code to make the test pass.
- Refactor: Clean up while keeping tests green.
# Run tests for a specific package
go test ./resolver/
go test ./hostfile/
go test ./lockfile/
# Run all tests
go test ./...
Error Handling (Constitution Principle IV)
All functions return errors. Only main() calls os.Exit():
// WRONG — do not do this in any package
func doSomething() {
if err != nil {
fmt.Println(err)
os.Exit(1) // PROHIBITED outside main()
}
}
// CORRECT — return errors to caller
func doSomething() error {
if err != nil {
return fmt.Errorf("doing something: %w", err)
}
return nil
}
Testable Interfaces (Constitution Principle VI)
Functions that interact with filesystem or DNS accept interfaces:
// Production: uses real OS calls
manager := hostfile.NewManager(hostfile.NewOSFileSystem())
// Test: uses in-memory fake
manager := hostfile.NewManager(fakeFS)
Atomic Write Sequence
1. Backup: copy hosts → <exe-dir>/hosts.bak.<date>-<rand>
2. Write: assemble content → temp file (same dir as hosts)
3. Sync: temp.Sync()
4. Chmod: match original permissions
5. Close: temp.Close()
6. Rename: os.Rename(temp, hosts) [retry on Windows]
7. Clean: delete backup on success
Usage Examples
# Add DNS entries
dns-helper add -host myapp.example.com -server 10.0.0.53
# Add multiple hosts
dns-helper add -host host1.example.com,host2.example.com -server dns.internal
# Delete specific host entries
dns-helper delete -host myapp.example.com
# Delete all managed entries
dns-helper delete all
Build for All Platforms
# Windows
GOOS=windows GOARCH=amd64 go build -o bin/windows/dns-helper.exe .
# Linux
GOOS=linux GOARCH=amd64 go build -o bin/linux/dns-helper .
# macOS
GOOS=darwin GOARCH=amd64 go build -o bin/macos/dns-helper .
Dependency Management
# After removing miekg/dns and adding dnsmessage usage:
go mod tidy
# Verify no unapproved dependencies
go list -m all
# Should only show: golang.org/x/net, golang.org/x/sys (transitive)