Files
dnshelper/specs/001-safety-reliability-refactor/quickstart.md
T

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:

  1. Red: Write a failing test that specifies the desired behavior.
  2. Green: Write the minimum code to make the test pass.
  3. 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)