Why This Matters - Building Your Assembly Line
Think of a great development workflow like a well-organized assembly line. Every workstation has a specific purpose, quality checks happen at each stage, and the final product rolls out consistently, every time. Poor workflows are like chaotic workshops where tools are scattered, steps are forgotten, and quality varies wildly.
Real-world Impact: Companies with efficient Go workflows ship features 5-10x faster, have 70% fewer production bugs, and onboarding new developers takes weeks instead of months. Facebook's engineering teams, for example, attribute much of their productivity to sophisticated development workflows that catch issues before they reach production.
๐ก Key Insight: The best workflows are invisible when they work perfectly and painfully obvious when they break. Your goal is to build systems so frictionless that developers focus on solving problems, not fighting tools.
In this article, you'll learn:
- How to build quality gates with Git hooks and automation
- Task orchestration with Makefiles and modern task runners
- Live reload tools for instant development feedback
- Containerized development environments for team consistency
- CI/CD patterns that prevent production issues
- IDE configurations that accelerate development
Learning Objectives
By the end of this article, you'll be able to:
โ
Automate code quality checks with Git hooks and CI/CD pipelines
โ
Orchestrate complex development tasks with Makefiles and task runners
โ
Implement hot reload workflows for immediate development feedback
โ
Create containerized development environments for team consistency
โ
Configure IDEs and editors for maximum productivity
โ
Build end-to-end workflows from local development to production deployment
Core Concepts - The Modern Go Philosophy
Assembly Line Thinking
Modern Go development follows the assembly line principle: each stage adds value and includes quality checks. When something fails, the assembly line stops immediately to prevent downstream problems.
1// Assembly line pattern in action
2package main
3
4import (
5 "fmt"
6 "log"
7)
8
9// Stage 1: Input validation
10func validateInput(input string) error {
11 if len(input) == 0 {
12 return fmt.Errorf("input cannot be empty")
13 }
14 if len(input) > 1000 {
15 return fmt.Errorf("input too long")
16 }
17 return nil
18}
19
20// Stage 2: Processing with quality check
21func processInput(input string) {
22 // Simulate processing with error handling
23 if input == "crash" {
24 return "", fmt.Errorf("processing failed")
25 }
26
27 processed := fmt.Sprintf("processed_%s", input)
28 return processed, nil
29}
30
31// Stage 3: Output validation
32func validateOutput(output string) error {
33 if !strings.HasPrefix(output, "processed_") {
34 return fmt.Errorf("output format invalid")
35 }
36 return nil
37}
38
39// Assembly line coordinator
40func processAssemblyLine(input string) error {
41 fmt.Printf("๐ญ Starting assembly line for input: %s\n", input)
42
43 // Stage 1: Input validation
44 fmt.Println("๐ Stage 1: Input validation")
45 if err := validateInput(input); err != nil {
46 fmt.Printf("โ Input validation failed: %v\n", err)
47 return err
48 }
49 fmt.Println("โ
Input validation passed")
50
51 // Stage 2: Processing
52 fmt.Println("๐ง Stage 2: Processing")
53 processed, err := processInput(input)
54 if err != nil {
55 fmt.Printf("โ Processing failed: %v\n", err)
56 return err
57 }
58 fmt.Println("โ
Processing completed")
59
60 // Stage 3: Output validation
61 fmt.Println("๐ Stage 3: Output validation")
62 if err := validateOutput(processed); err != nil {
63 fmt.Printf("โ Output validation failed: %v\n", err)
64 return err
65 }
66 fmt.Println("โ
Output validation passed")
67
68 fmt.Printf("๐ Assembly line completed: %s\n", processed)
69 return nil
70}
Why this matters: This assembly line pattern demonstrates how each development stage should include built-in quality checks and fail fast when issues are detected.
Zero-Trust Development Philosophy
In modern workflows, nothing is trusted unless verified. Every code change, every commit, every deployment must pass through automated quality gates.
1// Zero-trust validation pipeline
2package main
3
4import (
5 "fmt"
6 "strings"
7 "time"
8)
9
10type QualityGate struct {
11 Name string
12 Check func() error
13 Critical bool // If true, failure blocks the pipeline
14 RetryCount int // Number of retries before giving up
15 Timeout time.Duration
16}
17
18type Pipeline struct {
19 Gates []QualityGate
20 Logger *log.Logger
21}
22
23func NewPipeline() *Pipeline {
24 return &Pipeline{
25 Gates: []QualityGate{
26 {
27 Name: "go-format",
28 Check: checkGoFormat,
29 Critical: true,
30 RetryCount: 1,
31 Timeout: 10 * time.Second,
32 },
33 {
34 Name: "go-vet",
35 Check: checkGoVet,
36 Critical: true,
37 RetryCount: 2,
38 Timeout: 30 * time.Second,
39 },
40 {
41 Name: "unit-tests",
42 Check: checkUnitTests,
43 Critical: true,
44 RetryCount: 3,
45 Timeout: 60 * time.Second,
46 },
47 {
48 Name: "security-scan",
49 Check: checkSecurity,
50 Critical: false, // Warning only
51 RetryCount: 1,
52 Timeout: 45 * time.Second,
53 },
54 },
55 }
56}
57
58// Individual quality gate implementations
59func checkGoFormat() error {
60 fmt.Println("๐ Checking Go code formatting...")
61 // Simulate format checking
62 time.Sleep(100 * time.Millisecond)
63 return nil // Would return error if formatting issues found
64}
65
66func checkGoVet() error {
67 fmt.Println("๐ Running go vet...")
68 // Simulate vet checking
69 time.Sleep(200 * time.Millisecond)
70 return nil // Would return error if issues found
71}
72
73func checkUnitTests() error {
74 fmt.Println("๐งช Running unit tests...")
75 // Simulate test running
76 time.Sleep(500 * time.Millisecond)
77 return nil // Would return error if tests fail
78}
79
80func checkSecurity() error {
81 fmt.Println("๐ Running security scan...")
82 // Simulate security scanning
83 time.Sleep(300 * time.Millisecond)
84 return nil // Would return error if vulnerabilities found
85}
86
87// Execute pipeline with retries and timeouts
88func Execute() error {
89 fmt.Println("๐ Starting quality pipeline...")
90
91 for _, gate := range p.Gates {
92 fmt.Printf("\n๐ Running gate: %s\n", gate.Name)
93
94 // Run with timeout and retry logic
95 err := p.runGateWithRetry(gate)
96 if err != nil {
97 if gate.Critical {
98 fmt.Printf("โ Critical gate %s failed: %v\n", gate.Name, err)
99 return fmt.Errorf("pipeline failed at %s: %w", gate.Name, err)
100 } else {
101 fmt.Printf("โ ๏ธ Non-critical gate %s failed: %v\n", gate.Name, err)
102 // Continue pipeline for non-critical failures
103 }
104 } else {
105 fmt.Printf("โ
Gate %s passed\n", gate.Name)
106 }
107 }
108
109 fmt.Println("\n๐ All quality gates passed!")
110 return nil
111}
112
113func runGateWithRetry(gate QualityGate) error {
114 var lastErr error
115
116 for attempt := 1; attempt <= gate.RetryCount; attempt++ {
117 if attempt > 1 {
118 fmt.Printf("๐ Retry %d for %s\n", attempt, gate.Name)
119 }
120
121 done := make(chan error, 1)
122 go func() {
123 done <- gate.Check()
124 }()
125
126 select {
127 case err := <-done:
128 if err == nil {
129 return nil
130 }
131 lastErr = err
132 fmt.Printf("โ Attempt %d failed for %s: %v\n", attempt, gate.Name, err)
133 case <-time.After(gate.Timeout):
134 lastErr = fmt.Errorf("timeout after %v", gate.Timeout)
135 fmt.Printf("โฐ Attempt %d timed out for %s\n", attempt, gate.Name)
136 }
137 }
138
139 return lastErr
140}
141
142func main() {
143 pipeline := NewPipeline()
144
145 if err := pipeline.Execute(); err != nil {
146 fmt.Printf("โ Pipeline failed: %v\n", err)
147 os.Exit(1)
148 }
149
150 fmt.Println("โ
Pipeline completed successfully!")
151}
Practical Examples - Git Hooks as Quality Gates
Git hooks are your first line of defense against poor code quality. They catch issues before they even leave your development machine.
Production-Ready Pre-commit Hook
Why this matters: Prevents poor quality code from being committed and requires developers to fix issues immediately.
1#!/bin/bash
2# .git/hooks/pre-commit - Production-ready quality gate
3
4set -e # Exit on any error
5
6echo "๐ Running pre-commit quality checks..."
7
8# Get list of staged Go files
9GO_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$' || true)
10
11if [ -z "$GO_FILES" ]; then
12 echo "โ
No Go files to check"
13 exit 0
14fi
15
16echo "๐ Checking ${GO_FILES}"
17
18# Stage 1: Code formatting
19echo ""
20echo "๐จ Stage 1: Code formatting checks"
21
22UNFORMATTED=$(gofmt -l $GO_FILES)
23if [ -n "$UNFORMATTED" ]; then
24 echo "โ Files need formatting:"
25 echo "$UNFORMATTED"
26 echo ""
27 echo "๐ก Fix with: gofmt -w ."
28 echo "๐ก Or run: make fmt"
29 exit 1
30fi
31echo "โ
Go formatting OK"
32
33# Stage 2: Import organization
34echo ""
35echo "๐ฆ Stage 2: Import organization"
36
37if command -v goimports &> /dev/null; then
38 UNFORMATTED=$(goimports -l $GO_FILES)
39 if [ -n "$UNFORMATTED" ]; then
40 echo "โ Imports need organization:"
41 echo "$UNFORMATTED"
42 echo ""
43 echo "๐ก Fix with: goimports -w ."
44 exit 1
45 fi
46 echo "โ
Import organization OK"
47else
48 echo "โ ๏ธ goimports not installed, skipping import checks"
49fi
50
51# Stage 3: Static analysis
52echo ""
53echo "๐ Stage 3: Static analysis"
54
55if ! go vet ./...; then
56 echo "โ go vet failed"
57 echo ""
58 echo "๐ก Fix the reported issues and try again"
59 exit 1
60fi
61echo "โ
go vet passed"
62
63# Stage 4: Linting
64echo ""
65echo "๐งน Stage 4: Linting"
66
67if command -v golangci-lint &> /dev/null; then
68 echo "Running golangci-lint on changed files..."
69
70 # Get the base commit for comparison
71 BASE_COMMIT=$(git merge-base origin/main HEAD)
72
73 if ! golangci-lint run --new-from-rev="$BASE_COMMIT"; then
74 echo "โ golangci-lint failed"
75 echo ""
76 echo "๐ก Fix the reported issues and try again"
77 echo "๐ก Or temporarily bypass with git commit --no-verify"
78 exit 1
79 fi
80 echo "โ
Linting passed"
81else
82 echo "โ ๏ธ golangci-lint not installed, skipping linting"
83 echo "๐ก Install with: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"
84fi
85
86# Stage 5: Fast tests
87echo ""
88echo "๐งช Stage 5: Fast tests"
89
90if ! go test -short -race ./...; then
91 echo "โ Tests failed"
92 echo ""
93 echo "๐ก Fix failing tests and try again"
94 exit 1
95fi
96echo "โ
Tests passed"
97
98# Stage 6: Build check
99echo ""
100echo "๐๏ธ Stage 6: Build check"
101
102if ! go build ./...; then
103 echo "โ Build failed"
104 echo ""
105 echo "๐ก Fix build errors and try again"
106 exit 1
107fi
108echo "โ
Build successful"
109
110echo ""
111echo "โจ All pre-commit checks passed!"
112echo ""
113echo "๐ Your code is ready to be committed!"
Advanced Pre-push Hook for Comprehensive Quality:
1#!/bin/bash
2# .git/hooks/pre-push - Comprehensive pre-push validation
3
4set -e
5
6echo "๐ Running pre-push comprehensive checks..."
7
8# Full test suite
9echo ""
10echo "๐งช Running full test suite..."
11
12if ! go test -v -race -coverprofile=coverage.out ./...; then
13 echo "โ Tests failed"
14 echo ""
15 echo "๐ก Fix failing tests before pushing"
16 exit 1
17fi
18
19echo "โ
All tests passed"
20
21# Coverage analysis
22echo ""
23echo "๐ Analyzing test coverage..."
24
25COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
26THRESHOLD=70
27
28if )); then
29 echo "โ Coverage ${COVERAGE}% is below threshold ${THRESHOLD}%"
30 echo ""
31 echo "๐ก Add more tests to reach minimum coverage"
32 echo "๐ก Or adjust threshold in pre-push hook"
33 exit 1
34fi
35
36echo "โ
Coverage ${COVERAGE}% exceeds threshold ${THRESHOLD}%"
37
38# Security scan
39echo ""
40echo "๐ Running security scan..."
41
42if command -v gosec &> /dev/null; then
43 if ! gosec -quiet ./...; then
44 echo "โ Security issues found"
45 echo ""
46 echo "๐ก Review and fix security issues"
47 echo "๐ก Or run gosec with specific exclusions if necessary"
48 exit 1
49 fi
50 echo "โ
Security scan passed"
51else
52 echo "โ ๏ธ gosec not installed, skipping security scan"
53 echo "๐ก Install with: go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest"
54fi
55
56# Benchmark comparison
57echo ""
58echo "โก Running benchmarks..."
59
60if [ -f "benchmark.baseline" ]; then
61 go test -bench=. -count=3 -benchmem ./... > benchmark.new 2>/dev/null || true
62
63 if [ -f "benchmark.new" ]; then
64 # Simple benchmark comparison
65 echo "๐ Benchmarks completed"
66 rm benchmark.new
67 else
68 echo "โ ๏ธ Benchmark execution failed"
69 fi
70else
71 echo "๐ก Create baseline benchmarks with: go test -bench=. -count=3 -benchmem ./... > benchmark.baseline"
72fi
73
74# Documentation check
75echo ""
76echo "๐ Checking documentation..."
77
78MISSING_DOCS=$(find . -name "*.go" -not -path "./vendor/*" -exec grep -L "^// Package" {} \;)
79if [ -n "$MISSING_DOCS" ]; then
80 echo "โ ๏ธ Files missing package documentation:"
81 echo "$MISSING_DOCS"
82 echo ""
83 echo "๐ก Consider adding package documentation"
84fi
85
86echo ""
87echo "โจ All pre-push checks passed!"
88echo ""
89echo "๐ Your code is ready to be pushed!"
Integration Patterns - Makefiles as Task Orchestrators
Makefiles serve as your development command center, providing memorable commands for complex workflows.
Production-Ready Makefile
1# Makefile - Complete development automation system
2
3.PHONY: help
4help: ## Show this help message
5 @echo "๐ Go Development Automation"
6 @echo ""
7 @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
8
9# ========================================
10# Development Environment
11# ========================================
12
13# Variables
14BINARY_NAME=myapp
15VERSION?=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
16BUILD_DIR=bin
17MAIN_PATH=./cmd/myapp
18GO_FILES=$(shell find . -name '*.go' -not -path './vendor/*')
19DOCKER_TAG=$(BINARY_NAME):$(VERSION)
20
21# Build information
22BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
23GIT_COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
24LDFLAGS=-ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME) -X main.GitCommit=$(GIT_COMMIT)"
25
26# ========================================
27# Development Commands
28# ========================================
29
30.PHONY: dev
31dev: ## Start development server with hot reload
32 @echo "๐ฅ Starting development server..."
33 @if command -v air >/dev/null 2>&1; then \
34 air -c .air.toml; \
35 else \
36 echo "โ air not installed. Install with: go install github.com/cosmtrek/air@latest"; \
37 exit 1; \
38 fi
39
40.PHONY: run
41run: build ## Build and run the application
42 @echo "๐ Running $(BINARY_NAME)..."
43 @$(BUILD_DIR)/$(BINARY_NAME)
44
45.PHONY: install-tools
46install-tools: ## Install development tools
47 @echo "๐ ๏ธ Installing development tools..."
48 @echo "Installing goimports..."
49 @go install golang.org/x/tools/cmd/goimports@latest
50 @echo "Installing golangci-lint..."
51 @go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
52 @echo "Installing air..."
53 @go install github.com/cosmtrek/air@latest
54 @echo "Installing gosec..."
55 @go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
56 @echo "Installing godot..."
57 @go install github.com/tcrampt/godot/cmd/godot@latest
58 @echo "โ
Tools installed successfully"
59
60# ========================================
61# Building and Packaging
62# ========================================
63
64.PHONY: build
65build: ## Build the application for current platform
66 @echo "๐๏ธ Building $(BINARY_NAME)..."
67 @mkdir -p $(BUILD_DIR)
68 @go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) $(MAIN_PATH)
69 @echo "โ
Built $(BUILD_DIR)/$(BINARY_NAME)"
70
71.PHONY: build-all
72build-all: ## Build for all supported platforms
73 @echo "๐๏ธ Building for multiple platforms..."
74 @mkdir -p $(BUILD_DIR)
75
76 # Linux AMD64
77 GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 $(MAIN_PATH)
78
79 # macOS AMD64
80 GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-amd64 $(MAIN_PATH)
81
82 # macOS ARM64
83 GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-arm64 $(MAIN_PATH)
84
85 # Windows AMD64
86 GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe $(MAIN_PATH)
87
88 @echo "โ
Built for all platforms"
89
90.PHONY: clean
91clean: ## Clean build artifacts
92 @echo "๐งน Cleaning build artifacts..."
93 @rm -rf $(BUILD_DIR)
94 @rm -f coverage.out coverage.html
95 @rm -f benchmark.out
96 @echo "โ
Cleaned"
97
98# ========================================
99# Code Quality
100# ========================================
101
102.PHONY: fmt
103fmt: ## Format code
104 @echo "๐จ Formatting code..."
105 @gofmt -w -s $(GO_FILES)
106 @if command -v goimports >/dev/null 2>&1; then \
107 goimports -w $(GO_FILES); \
108 fi
109 @echo "โ
Code formatted"
110
111.PHONY: vet
112vet: ## Run go vet
113 @echo "๐ Running go vet..."
114 @go vet ./...
115
116.PHONY: lint
117lint: ## Run linter
118 @echo "๐งน Running linter..."
119 @if command -v golangci-lint >/dev/null 2>&1; then \
120 golangci-lint run ./...; \
121 else \
122 echo "โ golangci-lint not installed. Run 'make install-tools'"; \
123 exit 1; \
124 fi
125
126.PHONY: security
127security: ## Run security scan
128 @echo "๐ Running security scan..."
129 @if command -v gosec >/dev/null 2>&1; then \
130 gosec ./...; \
131 else \
132 echo "โ gosec not installed. Run 'make install-tools'"; \
133 exit 1; \
134 fi
135
136.PHONY: check
137check: fmt vet lint test-security ## Run all code quality checks
138 @echo "โ
All quality checks passed"
139
140# ========================================
141# Testing
142# ========================================
143
144.PHONY: test
145test: ## Run tests
146 @echo "๐งช Running tests..."
147 @go test -v -race ./...
148
149.PHONY: test-short
150test-short: ## Run short tests only
151 @echo "๐งช Running short tests..."
152 @go test -short -race ./...
153
154.PHONY: test-coverage
155test-coverage: ## Run tests with coverage report
156 @echo "๐ Running tests with coverage..."
157 @go test -v -race -coverprofile=coverage.out ./...
158 @go tool cover -html=coverage.out -o coverage.html
159 @echo "โ
Coverage report: coverage.html"
160 @echo "๐ Total coverage: $$(go tool cover -func=coverage.out | grep total | awk '{print $$3}')"
161
162.PHONY: bench
163bench: ## Run benchmarks
164 @echo "โก Running benchmarks..."
165 @go test -bench=. -benchmem -run=^$$ ./...
166
167.PHONY: integration
168integration: ## Run integration tests
169 @echo "๐ Running integration tests..."
170 @go test -tags=integration -v ./test/integration/...
171
172# ========================================
173# Dependencies
174# ========================================
175
176.PHONY: deps
177deps: ## Download dependencies
178 @echo "๐ฆ Downloading dependencies..."
179 @go mod download
180 @go mod verify
181
182.PHONY: deps-update
183deps-update: ## Update dependencies
184 @echo "๐ฆ Updating dependencies..."
185 @go get -u ./...
186 @go mod tidy
187
188.PHONY: deps-check
189deps-check: ## Check for dependency issues
190 @echo "๐ Checking dependencies..."
191 @go list -u -m all | grep '\[' && echo "โ ๏ธ Dependencies need updates" || echo "โ
Dependencies up to date"
192
193# ========================================
194# Documentation
195# ========================================
196
197.PHONY: docs
198docs: ## Generate documentation
199 @echo "๐ Generating documentation..."
200 @if command -v godoc >/dev/null 2>&1; then \
201 echo "Starting godoc server on :6060"; \
202 godoc -http=:6060; \
203 else \
204 echo "โ godoc not installed. Install with: go install golang.org/x/tools/cmd/godoc@latest"; \
205 fi
206
207.PHONY: docs-check
208docs-check: ## Check documentation coverage
209 @echo "๐ Checking documentation coverage..."
210 @if command -v godot >/dev/null 2>&1; then \
211 find . -name "*.go" -not -path "./vendor/*" -exec godot -check {} \; || true; \
212 else \
213 echo "โ ๏ธ godot not installed. Run 'make install-tools'"; \
214 fi
215
216# ========================================
217# Docker and Deployment
218# ========================================
219
220.PHONY: docker-build
221docker-build: ## Build Docker image
222 @echo "๐ณ Building Docker image..."
223 @docker build -t $(DOCKER_TAG) .
224 @echo "โ
Built $(DOCKER_TAG)"
225
226.PHONY: docker-run
227docker-run: ## Run Docker container
228 @echo "๐ณ Running Docker container..."
229 @docker run --rm -p 8080:8080 $(DOCKER_TAG)
230
231.PHONY: docker-push
232docker-push: docker-build ## Push Docker image to registry
233 @echo "๐ณ Pushing Docker image..."
234 @docker push $(DOCKER_TAG)
235
236# ========================================
237# Development Environment Setup
238# ========================================
239
240.PHONY: setup
241setup: ## Set up development environment
242 @echo "๐ง Setting up development environment..."
243
244 @echo "Creating directories..."
245 @mkdir -p $(BUILD_DIR)
246 @mkdir -p config
247 @mkdir -p logs
248
249 @echo "Setting up pre-commit hooks..."
250 @if [ -f scripts/pre-commit.sh ]; then \
251 chmod +x scripts/pre-commit.sh; \
252 ln -sf ../../scripts/pre-commit.sh .git/hooks/pre-commit; \
253 echo "โ
Pre-commit hook installed"; \
254 fi
255
256 @echo "Installing tools..."
257 @$(MAKE) install-tools
258
259 @echo "Downloading dependencies..."
260 @$(MAKE) deps
261
262 @echo "โ
Development environment setup complete!"
263
264.PHONY: precommit-setup
265precommit-setup: ## Set up Git pre-commit hooks
266 @echo "๐ง Setting up pre-commit hooks..."
267 @mkdir -p scripts/hooks
268
269 # Create pre-commit hook
270 @echo '#!/bin/bash' > scripts/hooks/pre-commit
271 @echo 'set -e' >> scripts/hooks/pre-commit
272 @echo 'echo "๐ Running pre-commit checks..."' >> scripts/hooks/pre-commit
273 @echo 'make fmt && make vet && make test-short' >> scripts/hooks/pre-commit
274
275 @chmod +x scripts/hooks/pre-commit
276 @ln -sf ../../scripts/hooks/pre-commit .git/hooks/pre-commit
277 @echo "โ
Pre-commit hook installed"
278
279# ========================================
280# Database and Migrations
281# ========================================
282
283.PHONY: db-migrate
284db-migrate: ## Run database migrations
285 @echo "๐๏ธ Running database migrations..."
286 @if command -v migrate >/dev/null 2>&1; then \
287 migrate -path db/migrations -database "$(DB_URL)" up; \
288 else \
289 echo "โ migrate not installed. Install from https://github.com/golang-migrate/migrate"; \
290 exit 1; \
291 fi
292
293.PHONY: db-rollback
294db-rollback: ## Rollback database migration
295 @echo "๐๏ธ Rolling back database migration..."
296 @migrate -path db/migrations -database "$(DB_URL)" down 1
297
298.PHONY: db-reset
299db-reset: ## Reset database
300 @echo "๐๏ธ Resetting database..."
301 @migrate -path db/migrations -database "$(DB_URL)" down -all
302 @migrate -path db/migrations -database "$(DB_URL)" up
303
304# Default target
305.DEFAULT_GOAL := help
Air Configuration for Hot Reload
1# .air.toml - Air configuration for hot reload
2
3root = "."
4testdata_dir = "testdata"
5tmp_dir = "tmp"
6
7[build]
8 # Just plain old shell command. You could use 'make' too
9 cmd = "go build -o ./tmp/main ./cmd/myapp"
10
11 # Binary file yields from 'cmd'.
12 bin = "tmp/main"
13
14 # Customize binary.
15 full_bin = "./tmp/main"
16
17 # Add additional arguments when running binary.
18 args_bin = []
19
20 # Watch these filename extensions.
21 include_ext = ["go", "tpl", "tmpl", "html"]
22
23 # Ignore these filename extensions or directories.
24 exclude_dir = ["assets", "tmp", "vendor", "testdata"]
25
26 # Watch these directories if you specified.
27 include_dir = []
28
29 # Exclude files.
30 exclude_file = []
31
32 # Poll files for changes instead of using fsnotify.
33 poll = false
34
35 # This log file places in your tmp_dir.
36 log = "build-errors.log"
37
38 # Poll files every duration.
39 poll_interval = 1000 # ms
40
41[log]
42 # Show log time
43 time = true
44
45 # Only show main log
46 main_only = false
47
48[color]
49 # Customize each part's color.
50 main = "magenta"
51 watcher = "cyan"
52 build = "yellow"
53 runner = "green"
54
55[misc]
56 # Delete tmp directory on exit
57 clean_on_exit = false
58
59[screen]
60 clear_on_rebuild = true
61 keep_scroll = true
Integration and Mastery - Complete Workflow Implementation
Let's create a complete development workflow system that integrates all these concepts:
1// workflow_automation.go
2package main
3
4import (
5 "context"
6 "fmt"
7 "log"
8 "os"
9 "os/signal"
10 "sync"
11 "time"
12)
13
14type WorkflowEngine struct {
15 tasks map[string]*Task
16 logger *log.Logger
17 stopChan chan struct{}
18 wg sync.WaitGroup
19}
20
21type Task struct {
22 Name string
23 Description string
24 Command func(ctx context.Context) error
25 DependsOn []string
26 Timeout time.Duration
27 RetryCount int
28}
29
30func NewWorkflowEngine() *WorkflowEngine {
31 return &WorkflowEngine{
32 tasks: make(map[string]*Task),
33 logger: log.New(os.Stdout, "[WORKFLOW] ", log.LstdFlags),
34 stopChan: make(chan struct{}),
35 }
36}
37
38func AddTask(task *Task) {
39 we.tasks[task.Name] = task
40}
41
42func Execute(taskName string) error {
43 task, exists := we.tasks[taskName]
44 if !exists {
45 return fmt.Errorf("task %s not found", taskName)
46 }
47
48 return we.executeTaskWithDependencies(context.Background(), task)
49}
50
51func executeTaskWithDependencies(ctx context.Context, task *Task) error {
52 // Execute dependencies first
53 for _, depName := range task.DependsOn {
54 dep, exists := we.tasks[depName]
55 if !exists {
56 return fmt.Errorf("dependency %s not found for task %s", depName, task.Name)
57 }
58
59 if err := we.executeTaskWithDependencies(ctx, dep); err != nil {
60 return fmt.Errorf("dependency %s failed for task %s: %w", depName, task.Name, err)
61 }
62 }
63
64 // Execute the task with retry logic
65 return we.executeTaskWithRetry(ctx, task)
66}
67
68func executeTaskWithRetry(ctx context.Context, task *Task) error {
69 var lastErr error
70
71 for attempt := 1; attempt <= task.RetryCount; attempt++ {
72 if attempt > 1 {
73 we.logger.Printf("๐ Retry %d for task %s", attempt, task.Name)
74 }
75
76 // Create timeout context
77 taskCtx, cancel := context.WithTimeout(ctx, task.Timeout)
78 defer cancel()
79
80 done := make(chan error, 1)
81 go func() {
82 done <- task.Command(taskCtx)
83 }()
84
85 select {
86 case err := <-done:
87 if err == nil {
88 we.logger.Printf("โ
Task %s completed successfully", task.Name)
89 return nil
90 }
91 lastErr = err
92 we.logger.Printf("โ Task %s failed on attempt %d: %v", task.Name, attempt, err)
93
94 case <-taskCtx.Done():
95 lastErr = fmt.Errorf("task %s timed out after %v", task.Name, task.Timeout)
96 we.logger.Printf("โฐ Task %s timed out on attempt %d", task.Name, attempt)
97
98 case <-we.stopChan:
99 return fmt.Errorf("workflow stopped during task %s", task.Name)
100 }
101 }
102
103 return fmt.Errorf("task %s failed after %d attempts: %w", task.Name, task.RetryCount, lastErr)
104}
105
106func StartContinuousTasks() {
107 // Set up signal handling for graceful shutdown
108 sigChan := make(chan os.Signal, 1)
109 signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
110
111 go func() {
112 <-sigChan
113 we.logger.Println("๐ Stopping workflow engine...")
114 close(we.stopChan)
115 }()
116
117 we.logger.Println("๐ Workflow engine started")
118}
119
120func Stop() {
121 close(we.stopChan)
122 we.wg.Wait()
123 we.logger.Println("โ
Workflow engine stopped")
124}
125
126func main() {
127 workflow := NewWorkflowEngine()
128 workflow.StartContinuousTasks()
129
130 // Define development workflow tasks
131 workflow.AddTask(&Task{
132 Name: "format",
133 Description: "Format Go code",
134 Command: func(ctx context.Context) error {
135 // Simulate go fmt command
136 fmt.Println("๐จ Formatting code...")
137 time.Sleep(100 * time.Millisecond)
138 return nil
139 },
140 Timeout: 10 * time.Second,
141 RetryCount: 1,
142 })
143
144 workflow.AddTask(&Task{
145 Name: "vet",
146 Description: "Run go vet",
147 Command: func(ctx context.Context) error {
148 fmt.Println("๐ Running go vet...")
149 time.Sleep(200 * time.Millisecond)
150 return nil
151 },
152 DependsOn: []string{"format"},
153 Timeout: 30 * time.Second,
154 RetryCount: 2,
155 })
156
157 workflow.AddTask(&Task{
158 Name: "test",
159 Description: "Run tests",
160 Command: func(ctx context.Context) error {
161 fmt.Println("๐งช Running tests...")
162 time.Sleep(500 * time.Millisecond)
163 return nil
164 },
165 DependsOn: []string{"format", "vet"},
166 Timeout: 60 * time.Second,
167 RetryCount: 3,
168 })
169
170 workflow.AddTask(&Task{
171 Name: "build",
172 Description: "Build application",
173 Command: func(ctx context.Context) error {
174 fmt.Println("๐๏ธ Building application...")
175 time.Sleep(300 * time.Millisecond)
176 return nil
177 },
178 DependsOn: []string{"test"},
179 Timeout: 45 * time.Second,
180 RetryCount: 2,
181 })
182
183 workflow.AddTask(&Task{
184 Name: "deploy",
185 Description: "Deploy to staging",
186 Command: func(ctx context.Context) error {
187 fmt.Println("๐ Deploying to staging...")
188 time.Sleep(1 * time.Second)
189 return nil
190 },
191 DependsOn: []string{"build"},
192 Timeout: 5 * time.Minute,
193 RetryCount: 2,
194 })
195
196 // Execute example workflows
197 fmt.Println("\n๐๏ธ Executing build workflow...")
198 if err := workflow.Execute("build"); err != nil {
199 fmt.Printf("โ Build workflow failed: %v\n", err)
200 } else {
201 fmt.Println("โ
Build workflow completed successfully")
202 }
203
204 fmt.Println("\n๐ Executing deploy workflow...")
205 if err := workflow.Execute("deploy"); err != nil {
206 fmt.Printf("โ Deploy workflow failed: %v\n", err)
207 } else {
208 fmt.Println("โ
Deploy workflow completed successfully")
209 }
210
211 // Wait for shutdown signal
212 <-workflow.stopChan
213}
Advanced Workflow Patterns
Continuous Integration Best Practices
Modern Go development requires robust CI/CD pipelines that catch issues early and provide fast feedback.
1# .github/workflows/ci.yml - Comprehensive CI pipeline
2name: Continuous Integration
3
4on:
5 push:
6 branches: [ main, develop ]
7 pull_request:
8 branches: [ main ]
9
10env:
11 GO_VERSION: '1.22'
12
13jobs:
14 # Job 1: Code quality checks
15 quality:
16 name: Code Quality
17 runs-on: ubuntu-latest
18
19 steps:
20 - name: Checkout code
21 uses: actions/checkout@v4
22
23 - name: Set up Go
24 uses: actions/setup-go@v4
25 with:
26 go-version: ${{ env.GO_VERSION }}
27 cache: true
28
29 - name: Install tools
30 run: |
31 go install golang.org/x/tools/cmd/goimports@latest
32 go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
33
34 - name: Check formatting
35 run: |
36 if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
37 echo "Code not formatted:"
38 gofmt -s -d .
39 exit 1
40 fi
41
42 - name: Check imports
43 run: |
44 if [ "$(goimports -l . | wc -l)" -gt 0 ]; then
45 echo "Imports not organized:"
46 goimports -d .
47 exit 1
48 fi
49
50 - name: Run go vet
51 run: go vet ./...
52
53 - name: Run golangci-lint
54 run: golangci-lint run --timeout=5m
55
56 # Job 2: Tests
57 test:
58 name: Tests
59 runs-on: ${{ matrix.os }}
60 strategy:
61 matrix:
62 os: [ubuntu-latest, macos-latest, windows-latest]
63 go: ['1.21', '1.22']
64
65 steps:
66 - name: Checkout code
67 uses: actions/checkout@v4
68
69 - name: Set up Go
70 uses: actions/setup-go@v4
71 with:
72 go-version: ${{ matrix.go }}
73 cache: true
74
75 - name: Run tests
76 run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
77
78 - name: Upload coverage
79 uses: codecov/codecov-action@v3
80 with:
81 file: ./coverage.txt
82 flags: ${{ matrix.os }}
83
84 # Job 3: Build
85 build:
86 name: Build
87 runs-on: ubuntu-latest
88 needs: [quality, test]
89
90 strategy:
91 matrix:
92 goos: [linux, darwin, windows]
93 goarch: [amd64, arm64]
94
95 steps:
96 - name: Checkout code
97 uses: actions/checkout@v4
98
99 - name: Set up Go
100 uses: actions/setup-go@v4
101 with:
102 go-version: ${{ env.GO_VERSION }}
103 cache: true
104
105 - name: Build binary
106 env:
107 GOOS: ${{ matrix.goos }}
108 GOARCH: ${{ matrix.goarch }}
109 run: |
110 CGO_ENABLED=0 go build -v -ldflags="-s -w" -o dist/app-${{ matrix.goos }}-${{ matrix.goarch }} .
111
112 - name: Upload artifacts
113 uses: actions/upload-artifact@v3
114 with:
115 name: binaries
116 path: dist/
117
118 # Job 4: Security scanning
119 security:
120 name: Security Scan
121 runs-on: ubuntu-latest
122
123 steps:
124 - name: Checkout code
125 uses: actions/checkout@v4
126
127 - name: Set up Go
128 uses: actions/setup-go@v4
129 with:
130 go-version: ${{ env.GO_VERSION }}
131
132 - name: Run Gosec
133 uses: securecodewarrior/gosec-action@master
134 with:
135 args: '-no-fail -fmt json -out results.json ./...'
136
137 - name: Check for vulnerabilities
138 run: |
139 go install golang.org/x/vuln/cmd/govulncheck@latest
140 govulncheck ./...
141
142 # Job 5: Docker build
143 docker:
144 name: Docker Build
145 runs-on: ubuntu-latest
146 needs: [quality, test]
147
148 steps:
149 - name: Checkout code
150 uses: actions/checkout@v4
151
152 - name: Set up Docker Buildx
153 uses: docker/setup-buildx-action@v3
154
155 - name: Build Docker image
156 uses: docker/build-push-action@v5
157 with:
158 context: .
159 push: false
160 tags: myapp:${{ github.sha }}
161 cache-from: type=gha
162 cache-to: type=gha,mode=max
Advanced Makefile Techniques
Create powerful build automation with advanced Makefile patterns:
1# Advanced Makefile with comprehensive automation
2
3# Variables
4SHELL := /bin/bash
5.SHELLFLAGS := -eu -o pipefail -c
6.DEFAULT_GOAL := help
7.DELETE_ON_ERROR:
8.SUFFIXES:
9
10# Metadata
11BINARY_NAME := myapp
12VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
13BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
14GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
15GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
16
17# Directories
18BUILD_DIR := build
19BIN_DIR := bin
20DIST_DIR := dist
21DOCS_DIR := docs
22TOOLS_DIR := tools
23
24# Build flags
25LDFLAGS := -ldflags "\
26 -s -w \
27 -X main.Version=$(VERSION) \
28 -X main.BuildTime=$(BUILD_TIME) \
29 -X main.GitCommit=$(GIT_COMMIT) \
30 -X main.GitBranch=$(GIT_BRANCH)"
31
32# Go files
33GO_FILES := $(shell find . -type f -name '*.go' -not -path './vendor/*')
34GO_PACKAGES := $(shell go list ./... | grep -v /vendor/)
35
36# Colors for output
37COLOR_RESET := $(shell tput sgr0)
38COLOR_BOLD := $(shell tput bold)
39COLOR_GREEN := $(shell tput setaf 2)
40COLOR_BLUE := $(shell tput setaf 4)
41COLOR_YELLOW := $(shell tput setaf 3)
42
43# Helper functions
44define print_section
45 @echo ""
46 @echo "$(COLOR_BOLD)$(COLOR_BLUE)==> $1$(COLOR_RESET)"
47endef
48
49define print_success
50 @echo "$(COLOR_GREEN)โ $1$(COLOR_RESET)"
51endef
52
53define print_warning
54 @echo "$(COLOR_YELLOW)โ $1$(COLOR_RESET)"
55endef
56
57# ==================================================================================
58# Help
59# ==================================================================================
60
61.PHONY: help
62help: ## Show this help message
63 @echo "$(COLOR_BOLD)Available targets:$(COLOR_RESET)"
64 @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
65 awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_GREEN)%-20s$(COLOR_RESET) %s\n", $$1, $$2}' | \
66 sort
67
68# ==================================================================================
69# Development
70# ==================================================================================
71
72.PHONY: dev
73dev: ## Start development server with hot reload
74 $(call print_section,"Starting development server")
75 @if command -v air >/dev/null 2>&1; then \
76 air -c .air.toml; \
77 else \
78 $(call print_warning,"air not installed. Install with: go install github.com/cosmtrek/air@latest"); \
79 exit 1; \
80 fi
81
82.PHONY: run
83run: build ## Build and run the application
84 $(call print_section,"Running $(BINARY_NAME)")
85 @$(BIN_DIR)/$(BINARY_NAME)
86
87.PHONY: clean
88clean: ## Remove build artifacts
89 $(call print_section,"Cleaning build artifacts")
90 @rm -rf $(BUILD_DIR) $(BIN_DIR) $(DIST_DIR)
91 @rm -f coverage.* *.prof *.test
92 $(call print_success,"Cleaned")
93
94# ==================================================================================
95# Building
96# ==================================================================================
97
98.PHONY: build
99build: $(BIN_DIR)/$(BINARY_NAME) ## Build the application
100
101$(BIN_DIR)/$(BINARY_NAME): $(GO_FILES)
102 $(call print_section,"Building $(BINARY_NAME)")
103 @mkdir -p $(BIN_DIR)
104 @go build $(LDFLAGS) -o $(BIN_DIR)/$(BINARY_NAME) .
105 $(call print_success,"Built $(BIN_DIR)/$(BINARY_NAME)")
106
107.PHONY: build-all
108build-all: ## Build for all platforms
109 $(call print_section,"Building for multiple platforms")
110 @mkdir -p $(DIST_DIR)
111
112 @echo "Building for Linux AMD64..."
113 @GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-linux-amd64 .
114
115 @echo "Building for Linux ARM64..."
116 @GOOS=linux GOARCH=arm64 go build $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-linux-arm64 .
117
118 @echo "Building for macOS AMD64..."
119 @GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-darwin-amd64 .
120
121 @echo "Building for macOS ARM64..."
122 @GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-darwin-arm64 .
123
124 @echo "Building for Windows AMD64..."
125 @GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-windows-amd64.exe .
126
127 $(call print_success,"Built for all platforms")
128
129# ==================================================================================
130# Code Quality
131# ==================================================================================
132
133.PHONY: fmt
134fmt: ## Format code
135 $(call print_section,"Formatting code")
136 @gofmt -s -w $(GO_FILES)
137 @if command -v goimports >/dev/null 2>&1; then \
138 goimports -w $(GO_FILES); \
139 fi
140 $(call print_success,"Code formatted")
141
142.PHONY: vet
143vet: ## Run go vet
144 $(call print_section,"Running go vet")
145 @go vet ./...
146 $(call print_success,"go vet passed")
147
148.PHONY: lint
149lint: ## Run linter
150 $(call print_section,"Running linter")
151 @if ! command -v golangci-lint >/dev/null 2>&1; then \
152 $(call print_warning,"golangci-lint not installed. Run 'make install-tools'"); \
153 exit 1; \
154 fi
155 @golangci-lint run --timeout=5m ./...
156 $(call print_success,"Linting passed")
157
158.PHONY: security
159security: ## Run security scan
160 $(call print_section,"Running security scan")
161 @if ! command -v gosec >/dev/null 2>&1; then \
162 $(call print_warning,"gosec not installed. Run 'make install-tools'"); \
163 exit 1; \
164 fi
165 @gosec -quiet ./...
166 $(call print_success,"Security scan passed")
167
168.PHONY: check
169check: fmt vet lint test ## Run all quality checks
170 $(call print_success,"All checks passed")
171
172# ==================================================================================
173# Testing
174# ==================================================================================
175
176.PHONY: test
177test: ## Run tests
178 $(call print_section,"Running tests")
179 @go test -v -race -count=1 ./...
180
181.PHONY: test-short
182test-short: ## Run short tests only
183 $(call print_section,"Running short tests")
184 @go test -short -race ./...
185
186.PHONY: test-coverage
187test-coverage: ## Run tests with coverage
188 $(call print_section,"Running tests with coverage")
189 @go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
190 @go tool cover -html=coverage.out -o coverage.html
191 @go tool cover -func=coverage.out | tail -1
192 $(call print_success,"Coverage report: coverage.html")
193
194.PHONY: test-integration
195test-integration: ## Run integration tests
196 $(call print_section,"Running integration tests")
197 @go test -tags=integration -v ./test/integration/...
198
199.PHONY: bench
200bench: ## Run benchmarks
201 $(call print_section,"Running benchmarks")
202 @go test -bench=. -benchmem -run=^$$ ./...
203
204# ==================================================================================
205# Dependencies
206# ==================================================================================
207
208.PHONY: deps
209deps: ## Download dependencies
210 $(call print_section,"Downloading dependencies")
211 @go mod download
212 @go mod verify
213 $(call print_success,"Dependencies downloaded")
214
215.PHONY: deps-update
216deps-update: ## Update dependencies
217 $(call print_section,"Updating dependencies")
218 @go get -u ./...
219 @go mod tidy
220 $(call print_success,"Dependencies updated")
221
222.PHONY: deps-check
223deps-check: ## Check for outdated dependencies
224 $(call print_section,"Checking dependencies")
225 @go list -u -m all | grep '\['
226
227# ==================================================================================
228# Tools
229# ==================================================================================
230
231.PHONY: install-tools
232install-tools: ## Install development tools
233 $(call print_section,"Installing development tools")
234
235 @echo "Installing goimports..."
236 @go install golang.org/x/tools/cmd/goimports@latest
237
238 @echo "Installing golangci-lint..."
239 @go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
240
241 @echo "Installing air..."
242 @go install github.com/cosmtrek/air@latest
243
244 @echo "Installing gosec..."
245 @go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
246
247 @echo "Installing govulncheck..."
248 @go install golang.org/x/vuln/cmd/govulncheck@latest
249
250 $(call print_success,"Tools installed")
251
252# ==================================================================================
253# Docker
254# ==================================================================================
255
256.PHONY: docker-build
257docker-build: ## Build Docker image
258 $(call print_section,"Building Docker image")
259 @docker build -t $(BINARY_NAME):$(VERSION) -t $(BINARY_NAME):latest .
260 $(call print_success,"Built $(BINARY_NAME):$(VERSION)")
261
262.PHONY: docker-run
263docker-run: ## Run Docker container
264 $(call print_section,"Running Docker container")
265 @docker run --rm -p 8080:8080 $(BINARY_NAME):latest
266
267.PHONY: docker-push
268docker-push: docker-build ## Push Docker image
269 $(call print_section,"Pushing Docker image")
270 @docker push $(BINARY_NAME):$(VERSION)
271 @docker push $(BINARY_NAME):latest
272
273# ==================================================================================
274# Setup and Initialization
275# ==================================================================================
276
277.PHONY: setup
278setup: deps install-tools ## Set up development environment
279 $(call print_section,"Setting up development environment")
280
281 @mkdir -p $(BUILD_DIR) $(BIN_DIR) $(DIST_DIR) $(DOCS_DIR)
282
283 @if [ ! -f .env ]; then \
284 echo "Creating .env file..."; \
285 cp .env.example .env 2>/dev/null || echo "# Environment variables" > .env; \
286 fi
287
288 @if [ -d .git ] && [ ! -f .git/hooks/pre-commit ]; then \
289 echo "Installing Git hooks..."; \
290 make git-hooks; \
291 fi
292
293 $(call print_success,"Development environment ready")
294
295.PHONY: git-hooks
296git-hooks: ## Install Git hooks
297 $(call print_section,"Installing Git hooks")
298
299 @mkdir -p .git/hooks
300
301 @echo '#!/bin/bash' > .git/hooks/pre-commit
302 @echo 'set -e' >> .git/hooks/pre-commit
303 @echo 'make fmt vet test-short' >> .git/hooks/pre-commit
304 @chmod +x .git/hooks/pre-commit
305
306 @echo '#!/bin/bash' > .git/hooks/pre-push
307 @echo 'set -e' >> .git/hooks/pre-push
308 @echo 'make check' >> .git/hooks/pre-push
309 @chmod +x .git/hooks/pre-push
310
311 $(call print_success,"Git hooks installed")
312
313# ==================================================================================
314# Release
315# ==================================================================================
316
317.PHONY: release
318release: check build-all ## Create a new release
319 $(call print_section,"Creating release $(VERSION)")
320
321 @if [ -z "$(VERSION)" ]; then \
322 $(call print_warning,"VERSION not set"); \
323 exit 1; \
324 fi
325
326 @echo "Building release artifacts..."
327 @cd $(DIST_DIR) && for file in *; do \
328 sha256sum $${file} > $${file}.sha256; \
329 done
330
331 $(call print_success,"Release $(VERSION) ready")
332
333# ==================================================================================
334# CI/CD Helpers
335# ==================================================================================
336
337.PHONY: ci
338ci: check test-coverage ## Run CI pipeline locally
339 $(call print_section,"Running CI pipeline")
340 @make build
341 $(call print_success,"CI pipeline passed")
342
343# ==================================================================================
344# Monitoring and Profiling
345# ==================================================================================
346
347.PHONY: profile-cpu
348profile-cpu: ## Run CPU profiling
349 $(call print_section,"Running CPU profiling")
350 @go test -cpuprofile=cpu.prof -bench=. ./...
351 @go tool pprof -http=:8081 cpu.prof
352
353.PHONY: profile-mem
354profile-mem: ## Run memory profiling
355 $(call print_section,"Running memory profiling")
356 @go test -memprofile=mem.prof -bench=. ./...
357 @go tool pprof -http=:8081 mem.prof
358
359# ==================================================================================
360# Documentation
361# ==================================================================================
362
363.PHONY: docs
364docs: ## Generate documentation
365 $(call print_section,"Generating documentation")
366 @if ! command -v godoc >/dev/null 2>&1; then \
367 go install golang.org/x/tools/cmd/godoc@latest; \
368 fi
369 @echo "Documentation server: http://localhost:6060"
370 @godoc -http=:6060
371
372# ==================================================================================
373# Miscellaneous
374# ==================================================================================
375
376.PHONY: version
377version: ## Show version information
378 @echo "Version: $(VERSION)"
379 @echo "Build time: $(BUILD_TIME)"
380 @echo "Git commit: $(GIT_COMMIT)"
381 @echo "Git branch: $(GIT_BRANCH)"
382
383.PHONY: info
384info: ## Show project information
385 @echo "Binary name: $(BINARY_NAME)"
386 @echo "Go version: $(shell go version)"
387 @echo "Go packages: $(words $(GO_PACKAGES))"
388 @echo "Go files: $(words $(GO_FILES))"
Modern Development Environment Setup
Create reproducible development environments:
1# Dockerfile.dev - Development environment
2FROM golang:1.22-alpine AS dev
3
4# Install development tools
5RUN apk add --no-cache \
6 git \
7 make \
8 gcc \
9 musl-dev \
10 curl \
11 bash \
12 vim
13
14# Install Go tools
15RUN go install github.com/cosmtrek/air@latest && \
16 go install golang.org/x/tools/cmd/goimports@latest && \
17 go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
18
19WORKDIR /app
20
21# Copy dependency files
22COPY go.mod go.sum ./
23RUN go mod download
24
25# Copy source code
26COPY . .
27
28# Default command: start air for hot reload
29CMD ["air", "-c", ".air.toml"]
1# docker-compose.dev.yml - Development environment with dependencies
2version: '3.8'
3
4services:
5 app:
6 build:
7 context: .
8 dockerfile: Dockerfile.dev
9 ports:
10 - "8080:8080"
11 volumes:
12 - .:/app
13 - go-modules:/go/pkg/mod
14 environment:
15 - DATABASE_URL=postgresql://dev:dev@postgres:5432/myapp?sslmode=disable
16 - REDIS_URL=redis:6379
17 - LOG_LEVEL=debug
18 depends_on:
19 - postgres
20 - redis
21 command: air -c .air.toml
22
23 postgres:
24 image: postgres:16-alpine
25 ports:
26 - "5432:5432"
27 environment:
28 - POSTGRES_USER=dev
29 - POSTGRES_PASSWORD=dev
30 - POSTGRES_DB=myapp
31 volumes:
32 - postgres-data:/var/lib/postgresql/data
33
34 redis:
35 image: redis:7-alpine
36 ports:
37 - "6379:6379"
38 volumes:
39 - redis-data:/data
40
41volumes:
42 go-modules:
43 postgres-data:
44 redis-data:
Task Automation with Task Runner
Modern alternative to Make using Task:
1# Taskfile.yml - Modern task runner configuration
2version: '3'
3
4vars:
5 BINARY_NAME: myapp
6 VERSION:
7 sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
8 BUILD_DIR: build
9 DIST_DIR: dist
10
11env:
12 CGO_ENABLED: 0
13 GOOS: linux
14 GOARCH: amd64
15
16tasks:
17 default:
18 desc: Show available tasks
19 cmds:
20 - task --list
21
22 # Development tasks
23 dev:
24 desc: Start development server with hot reload
25 cmds:
26 - air -c .air.toml
27
28 run:
29 desc: Build and run the application
30 deps: [build]
31 cmds:
32 - ./{{.BUILD_DIR}}/{{.BINARY_NAME}}
33
34 # Building tasks
35 build:
36 desc: Build the application
37 sources:
38 - '**/*.go'
39 generates:
40 - '{{.BUILD_DIR}}/{{.BINARY_NAME}}'
41 cmds:
42 - mkdir -p {{.BUILD_DIR}}
43 - go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}} .
44
45 build:all:
46 desc: Build for all platforms
47 cmds:
48 - task: build:platform
49 vars: {GOOS: linux, GOARCH: amd64}
50 - task: build:platform
51 vars: {GOOS: linux, GOARCH: arm64}
52 - task: build:platform
53 vars: {GOOS: darwin, GOARCH: amd64}
54 - task: build:platform
55 vars: {GOOS: darwin, GOARCH: arm64}
56 - task: build:platform
57 vars: {GOOS: windows, GOARCH: amd64}
58
59 build:platform:
60 internal: true
61 cmds:
62 - GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -o {{.DIST_DIR}}/{{.BINARY_NAME}}-{{.GOOS}}-{{.GOARCH}} .
63
64 # Code quality tasks
65 fmt:
66 desc: Format code
67 cmds:
68 - gofmt -s -w .
69 - goimports -w .
70
71 vet:
72 desc: Run go vet
73 cmds:
74 - go vet ./...
75
76 lint:
77 desc: Run linter
78 cmds:
79 - golangci-lint run --timeout=5m ./...
80
81 # Testing tasks
82 test:
83 desc: Run tests
84 cmds:
85 - go test -v -race -count=1 ./...
86
87 test:coverage:
88 desc: Run tests with coverage
89 cmds:
90 - go test -v -race -coverprofile=coverage.out ./...
91 - go tool cover -html=coverage.out -o coverage.html
92 - go tool cover -func=coverage.out | tail -1
93
94 bench:
95 desc: Run benchmarks
96 cmds:
97 - go test -bench=. -benchmem ./...
98
99 # Quality check task
100 check:
101 desc: Run all quality checks
102 deps: [fmt, vet, lint, test]
103
104 # Docker tasks
105 docker:build:
106 desc: Build Docker image
107 cmds:
108 - docker build -t {{.BINARY_NAME}}:{{.VERSION}} .
109
110 docker:run:
111 desc: Run Docker container
112 cmds:
113 - docker run --rm -p 8080:8080 {{.BINARY_NAME}}:{{.VERSION}}
114
115 # Setup tasks
116 setup:
117 desc: Set up development environment
118 cmds:
119 - go mod download
120 - task: install:tools
121 - mkdir -p {{.BUILD_DIR}} {{.DIST_DIR}}
122
123 install:tools:
124 desc: Install development tools
125 cmds:
126 - go install github.com/cosmtrek/air@latest
127 - go install golang.org/x/tools/cmd/goimports@latest
128 - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
129
130 # Clean task
131 clean:
132 desc: Remove build artifacts
133 cmds:
134 - rm -rf {{.BUILD_DIR}} {{.DIST_DIR}}
135 - rm -f coverage.* *.prof
Practice Exercises
Exercise 1: Build Complete Development Workflow
Create a comprehensive development workflow system for a Go web application.
Requirements:
- Git hooks for pre-commit and pre-push validation
- Complete Makefile with development, testing, and deployment tasks
- Air configuration for hot reload development
- CI/CD pipeline configuration
- Docker containerization for consistent environments
Starter Code:
1// Your task: Complete this web application with comprehensive workflow
2package main
3
4import (
5 "net/http"
6 "log"
7)
8
9func main() {
10 // TODO: Set up HTTP server
11 // TODO: Add health check endpoint
12 // TODO: Add API endpoints for user management
13 // TODO: Integrate with workflow automation
14}
Tasks to Complete:
-
Git Hooks Setup
- Create pre-commit hook with formatting, linting, and testing
- Create pre-push hook with comprehensive testing and security scanning
- Add commit-msg hook for conventional commit validation
-
Makefile Creation
- Build targets for multiple platforms
- Development targets with hot reload
- Testing targets with coverage and benchmarks
- Quality targets for formatting, linting, and security scanning
-
CI/CD Pipeline
- GitHub Actions workflow for automated testing and deployment
- Multi-stage Docker builds for production
- Environment-specific configuration management
Solution
1. Git Hooks:
1#!/bin/bash
2set -e
3
4echo "๐ Running pre-commit checks..."
5
6# Format check
7echo "๐จ Checking formatting..."
8if ! gofmt -l . | grep -q .; then
9 echo "โ
Code is properly formatted"
10else
11 echo "โ Code needs formatting. Run 'make fmt'"
12 exit 1
13fi
14
15# Import organization
16echo "๐ฆ Checking imports..."
17if command -v goimports >/dev/null 2>&1; then
18 if ! goimports -l . | grep -q .; then
19 echo "โ
Imports are properly organized"
20 else
21 echo "โ Imports need organization. Run 'make fmt'"
22 exit 1
23 fi
24fi
25
26# Static analysis
27echo "๐ Running go vet..."
28if ! go vet ./...; then
29 echo "โ go vet failed"
30 exit 1
31fi
32
33# Linting
34echo "๐งน Running linter..."
35if command -v golangci-lint >/dev/null 2>&1; then
36 if ! golangci-lint run; then
37 echo "โ Linting failed"
38 exit 1
39 fi
40 echo "โ
Linting passed"
41fi
42
43# Tests
44echo "๐งช Running tests..."
45if ! go test -short -race ./...; then
46 echo "โ Tests failed"
47 exit 1
48fi
49
50echo "โ
All pre-commit checks passed!"
2. Complete Makefile:
1# Makefile for complete development workflow
2
3.PHONY: help
4help:
5 @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
6
7BINARY_NAME=webapp
8VERSION?=$(shell git describe --tags --always --dirty)
9BUILD_DIR=build
10
11.PHONY: build
12build: ## Build the application
13 @echo "๐๏ธ Building $(BINARY_NAME)..."
14 @mkdir -p $(BUILD_DIR)
15 @go build -o $(BUILD_DIR)/$(BINARY_NAME) .
16
17.PHONY: run
18run: build ## Build and run
19 @echo "๐ Starting $(BINARY_NAME)..."
20 @./$(BUILD_DIR)/$(BINARY_NAME)
21
22.PHONY: dev
23dev: ## Start development with hot reload
24 @echo "๐ฅ Starting development server..."
25 @air -c .air.toml
26
27.PHONY: test
28test: ## Run all tests
29 @echo "๐งช Running tests..."
30 @go test -v -race ./...
31
32.PHONY: test-coverage
33test-coverage: ## Run tests with coverage
34 @echo "๐ Running tests with coverage..."
35 @go test -v -race -coverprofile=coverage.out ./...
36 @go tool cover -html=coverage.out -o coverage.html
37 @echo "โ
Coverage report: coverage.html"
38
39.PHONY: fmt
40fmt: ## Format code
41 @echo "๐จ Formatting code..."
42 @gofmt -w -s .
43 @if command -v goimports >/dev/null 2>&1; then goimports -w .; fi
44 @echo "โ
Code formatted"
45
46.PHONY: lint
47lint: ## Run linter
48 @echo "๐งน Running linter..."
49 @if command -v golangci-lint >/dev/null 2>&1; then \
50 golangci-lint run; \
51 else \
52 echo "โ golangci-lint not installed"; \
53 exit 1; \
54 fi
55
56.PHONY: docker-build
57docker-build: ## Build Docker image
58 @echo "๐ณ Building Docker image..."
59 @docker build -t $(BINARY_NAME):$(VERSION) .
60
61.PHONY: docker-run
62docker-run: ## Run Docker container
63 @echo "๐ณ Running Docker container..."
64 @docker run --rm -p 8080:8080 $(BINARY_NAME):$(VERSION)
65
66.DEFAULT_GOAL := help
3. GitHub Actions Workflow:
1name: CI/CD Pipeline
2
3on:
4 push:
5 branches: [ main, develop ]
6 pull_request:
7 branches: [ main ]
8
9jobs:
10 test:
11 runs-on: ubuntu-latest
12
13 strategy:
14 matrix:
15 go-version: [1.21, 1.22]
16
17 steps:
18 - uses: actions/checkout@v3
19
20 - name: Set up Go
21 uses: actions/setup-go@v3
22 with:
23 go-version: ${{ matrix.go-version }}
24
25 - name: Cache Go modules
26 uses: actions/cache@v3
27 with:
28 path: ~/go/pkg/mod
29 key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
30
31 - name: Install dependencies
32 run: go mod download
33
34 - name: Run tests
35 run: make test-coverage
36
37 - name: Upload coverage to Codecov
38 uses: codecov/codecov-action@v3
39 with:
40 file: ./coverage.out
41
42 - name: Run linter
43 run: make lint
44
45 build:
46 needs: test
47 runs-on: ubuntu-latest
48
49 steps:
50 - uses: actions/checkout@v3
51
52 - name: Set up Go
53 uses: actions/setup-go@v3
54 with:
55 go-version: 1.22
56
57 - name: Build application
58 run: make build
59
60 - name: Build Docker image
61 run: make docker-build
62
63 - name: Push to registry
64 if: github.ref == 'refs/heads/main'
65 run: |
66 echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
67 docker push webapp:latest
4. Complete Web Application:
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "os"
9 "time"
10)
11
12type Application struct {
13 startTime time.Time
14}
15
16func NewApplication() *Application {
17 return &Application{
18 startTime: time.Now(),
19 }
20}
21
22func healthHandler(w http.ResponseWriter, r *http.Request) {
23 health := map[string]interface{}{
24 "status": "healthy",
25 "timestamp": time.Now(),
26 "uptime": time.Since(app.startTime).String(),
27 "version": os.Getenv("APP_VERSION"),
28 }
29
30 w.Header().Set("Content-Type", "application/json")
31 json.NewEncoder(w).Encode(health)
32}
33
34func userHandler(w http.ResponseWriter, r *http.Request) {
35 users := []map[string]interface{}{
36 {"id": 1, "name": "Alice", "email": "alice@example.com"},
37 {"id": 2, "name": "Bob", "email": "bob@example.com"},
38 {"id": 3, "name": "Charlie", "email": "charlie@example.com"},
39 }
40
41 w.Header().Set("Content-Type", "application/json")
42 json.NewEncoder(w).Encode(users)
43}
44
45func routes() http.Handler {
46 mux := http.NewServeMux()
47
48 // Health check endpoint
49 mux.HandleFunc("/health", app.healthHandler)
50
51 // API endpoints
52 mux.HandleFunc("/api/users", app.userHandler)
53
54 // Add middleware
55 return app.loggingMiddleware(mux)
56}
57
58func loggingMiddleware(next http.Handler) http.Handler {
59 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
60 start := time.Now()
61
62 // Log request
63 log.Printf("๐ %s %s", r.Method, r.URL.Path)
64
65 // Call next handler
66 next.ServeHTTP(w, r)
67
68 // Log response time
69 duration := time.Since(start)
70 log.Printf("โ
%s %s completed in %v", r.Method, r.URL.Path, duration)
71 })
72}
73
74func main() {
75 app := NewApplication()
76
77 port := os.Getenv("PORT")
78 if port == "" {
79 port = "8080"
80 }
81
82 fmt.Printf("๐ Starting web application on port %s\n", port)
83 fmt.Println("๐ Health check: http://localhost:8080/health")
84 fmt.Println("๐ฅ Users API: http://localhost:8080/api/users")
85
86 server := &http.Server{
87 Addr: ":" + port,
88 Handler: app.routes(),
89 ReadTimeout: 10 * time.Second,
90 WriteTimeout: 10 * time.Second,
91 IdleTimeout: 60 * time.Second,
92 }
93
94 log.Fatal(server.ListenAndServe())
95}
Exercise 2: CI/CD Pipeline Implementation
Create a complete CI/CD pipeline with GitHub Actions.
Requirements:
- Multi-platform testing (Linux, macOS, Windows)
- Code quality checks (formatting, linting, security)
- Automated releases with artifacts
- Docker image building and publishing
- Coverage reporting
Starter Code:
1# Your task: Complete this GitHub Actions workflow
2name: CI/CD Pipeline
3
4on:
5 push:
6 branches: [ main ]
7 pull_request:
8 branches: [ main ]
9
10jobs:
11 # TODO: Add quality check job
12 # TODO: Add testing job with matrix strategy
13 # TODO: Add build job
14 # TODO: Add release job
Solution
1name: Complete CI/CD Pipeline
2
3on:
4 push:
5 branches: [ main, develop ]
6 tags: [ 'v*' ]
7 pull_request:
8 branches: [ main ]
9
10env:
11 GO_VERSION: '1.22'
12 REGISTRY: ghcr.io
13 IMAGE_NAME: ${{ github.repository }}
14
15jobs:
16 quality:
17 name: Code Quality
18 runs-on: ubuntu-latest
19
20 steps:
21 - name: Checkout
22 uses: actions/checkout@v4
23
24 - name: Setup Go
25 uses: actions/setup-go@v4
26 with:
27 go-version: ${{ env.GO_VERSION }}
28 cache: true
29
30 - name: Install tools
31 run: |
32 go install golang.org/x/tools/cmd/goimports@latest
33 go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
34 go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
35
36 - name: Check formatting
37 run: |
38 if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
39 echo "Code is not formatted"
40 gofmt -s -d .
41 exit 1
42 fi
43
44 - name: Check imports
45 run: |
46 if [ "$(goimports -l . | wc -l)" -gt 0 ]; then
47 echo "Imports are not organized"
48 goimports -d .
49 exit 1
50 fi
51
52 - name: Run go vet
53 run: go vet ./...
54
55 - name: Run golangci-lint
56 run: golangci-lint run --timeout=5m ./...
57
58 - name: Security scan
59 run: gosec -quiet ./...
60
61 - name: Vulnerability check
62 run: |
63 go install golang.org/x/vuln/cmd/govulncheck@latest
64 govulncheck ./...
65
66 test:
67 name: Test
68 runs-on: ${{ matrix.os }}
69 strategy:
70 fail-fast: false
71 matrix:
72 os: [ubuntu-latest, macos-latest, windows-latest]
73 go: ['1.21', '1.22']
74
75 steps:
76 - name: Checkout
77 uses: actions/checkout@v4
78
79 - name: Setup Go
80 uses: actions/setup-go@v4
81 with:
82 go-version: ${{ matrix.go }}
83 cache: true
84
85 - name: Run tests
86 run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
87
88 - name: Upload coverage
89 uses: codecov/codecov-action@v3
90 with:
91 file: ./coverage.txt
92 flags: ${{ matrix.os }}-go${{ matrix.go }}
93
94 build:
95 name: Build
96 runs-on: ubuntu-latest
97 needs: [quality, test]
98 strategy:
99 matrix:
100 goos: [linux, darwin, windows]
101 goarch: [amd64, arm64]
102
103 steps:
104 - name: Checkout
105 uses: actions/checkout@v4
106
107 - name: Setup Go
108 uses: actions/setup-go@v4
109 with:
110 go-version: ${{ env.GO_VERSION }}
111 cache: true
112
113 - name: Build
114 env:
115 GOOS: ${{ matrix.goos }}
116 GOARCH: ${{ matrix.goarch }}
117 run: |
118 VERSION=$(git describe --tags --always --dirty)
119 BINARY=myapp-${{ matrix.goos }}-${{ matrix.goarch }}
120 if [ "${{ matrix.goos }}" == "windows" ]; then
121 BINARY="${BINARY}.exe"
122 fi
123 CGO_ENABLED=0 go build -v \
124 -ldflags="-s -w -X main.Version=${VERSION}" \
125 -o dist/${BINARY} .
126
127 - name: Upload artifacts
128 uses: actions/upload-artifact@v3
129 with:
130 name: binaries-${{ matrix.goos }}-${{ matrix.goarch }}
131 path: dist/*
132
133 docker:
134 name: Docker
135 runs-on: ubuntu-latest
136 needs: [quality, test]
137 permissions:
138 contents: read
139 packages: write
140
141 steps:
142 - name: Checkout
143 uses: actions/checkout@v4
144
145 - name: Setup Docker Buildx
146 uses: docker/setup-buildx-action@v3
147
148 - name: Log in to registry
149 uses: docker/login-action@v3
150 with:
151 registry: ${{ env.REGISTRY }}
152 username: ${{ github.actor }}
153 password: ${{ secrets.GITHUB_TOKEN }}
154
155 - name: Extract metadata
156 id: meta
157 uses: docker/metadata-action@v5
158 with:
159 images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
160 tags: |
161 type=ref,event=branch
162 type=ref,event=pr
163 type=semver,pattern={{version}}
164 type=semver,pattern={{major}}.{{minor}}
165 type=sha
166
167 - name: Build and push
168 uses: docker/build-push-action@v5
169 with:
170 context: .
171 push: ${{ github.event_name != 'pull_request' }}
172 tags: ${{ steps.meta.outputs.tags }}
173 labels: ${{ steps.meta.outputs.labels }}
174 cache-from: type=gha
175 cache-to: type=gha,mode=max
176
177 release:
178 name: Release
179 runs-on: ubuntu-latest
180 needs: [build, docker]
181 if: startsWith(github.ref, 'refs/tags/v')
182
183 steps:
184 - name: Checkout
185 uses: actions/checkout@v4
186
187 - name: Download artifacts
188 uses: actions/download-artifact@v3
189 with:
190 path: dist
191
192 - name: Create checksums
193 run: |
194 cd dist
195 find . -type f -exec sha256sum {} \; > checksums.txt
196
197 - name: Create release
198 uses: softprops/action-gh-release@v1
199 with:
200 files: dist/**/*
201 draft: false
202 prerelease: false
203 generate_release_notes: true
Exercise 3: Advanced Makefile with Task Orchestration
Build a production-ready Makefile with advanced features.
Requirements:
- Colored output and helper functions
- Dependency management
- Multi-platform builds
- Docker integration
- Git hooks setup
Starter Code:
1# Your task: Complete this advanced Makefile
2BINARY_NAME := myapp
3VERSION ?= dev
4
5.PHONY: help
6help:
7 @echo "Available targets:"
8 # TODO: Add help generation
9
10.PHONY: build
11build:
12 # TODO: Build with version info and optimization flags
13
14.PHONY: test
15test:
16 # TODO: Run tests with coverage
Solution
1# Complete advanced Makefile
2SHELL := /bin/bash
3.SHELLFLAGS := -eu -o pipefail -c
4.DEFAULT_GOAL := help
5.DELETE_ON_ERROR:
6.SUFFIXES:
7
8# Metadata
9BINARY_NAME := myapp
10VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
11BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
12GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
13MAIN_PACKAGE := ./cmd/$(BINARY_NAME)
14
15# Directories
16BUILD_DIR := build
17BIN_DIR := bin
18DIST_DIR := dist
19
20# Build flags
21LDFLAGS := -ldflags "\
22 -s -w \
23 -X main.Version=$(VERSION) \
24 -X main.BuildTime=$(BUILD_TIME) \
25 -X main.GitCommit=$(GIT_COMMIT)"
26
27# Go files and packages
28GO_FILES := $(shell find . -type f -name '*.go' -not -path './vendor/*' -not -path './dist/*')
29GO_PACKAGES := $(shell go list ./... 2>/dev/null | grep -v /vendor/)
30
31# Colors
32COLOR_RESET := \033[0m
33COLOR_BOLD := \033[1m
34COLOR_GREEN := \033[32m
35COLOR_BLUE := \033[34m
36COLOR_YELLOW := \033[33m
37COLOR_RED := \033[31m
38
39# Helper functions
40define log_info
41 @echo "$(COLOR_BLUE)โน $(1)$(COLOR_RESET)"
42endef
43
44define log_success
45 @echo "$(COLOR_GREEN)โ $(1)$(COLOR_RESET)"
46endef
47
48define log_warning
49 @echo "$(COLOR_YELLOW)โ $(1)$(COLOR_RESET)"
50endef
51
52define log_error
53 @echo "$(COLOR_RED)โ $(1)$(COLOR_RESET)"
54endef
55
56# =====================================================
57# Help
58# =====================================================
59
60.PHONY: help
61help: ## Show this help message
62 @echo "$(COLOR_BOLD)$(BINARY_NAME) - Available targets:$(COLOR_RESET)"
63 @echo ""
64 @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
65 awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_GREEN)%-20s$(COLOR_RESET) %s\n", $$1, $$2}' | \
66 sort
67
68# =====================================================
69# Development
70# =====================================================
71
72.PHONY: dev
73dev: ## Start development server
74 $(call log_info,"Starting development server")
75 @air -c .air.toml || \
76 ($(call log_error,"air not installed. Install with: go install github.com/cosmtrek/air@latest"); exit 1)
77
78.PHONY: run
79run: build ## Build and run
80 $(call log_info,"Running $(BINARY_NAME)")
81 @$(BIN_DIR)/$(BINARY_NAME)
82
83# =====================================================
84# Building
85# =====================================================
86
87.PHONY: build
88build: $(BIN_DIR)/$(BINARY_NAME) ## Build the application
89
90$(BIN_DIR)/$(BINARY_NAME): $(GO_FILES)
91 $(call log_info,"Building $(BINARY_NAME) $(VERSION)")
92 @mkdir -p $(BIN_DIR)
93 @go build $(LDFLAGS) -o $(BIN_DIR)/$(BINARY_NAME) $(MAIN_PACKAGE)
94 $(call log_success,"Built $(BIN_DIR)/$(BINARY_NAME)")
95
96.PHONY: build-all
97build-all: ## Build for all platforms
98 $(call log_info,"Building for all platforms")
99 @mkdir -p $(DIST_DIR)
100 @for os in linux darwin windows; do \
101 for arch in amd64 arm64; do \
102 output="$(DIST_DIR)/$(BINARY_NAME)-$${os}-$${arch}"; \
103 if [ "$${os}" = "windows" ]; then output="$${output}.exe"; fi; \
104 echo "Building for $${os}/$${arch}..."; \
105 GOOS=$${os} GOARCH=$${arch} CGO_ENABLED=0 \
106 go build $(LDFLAGS) -o $${output} $(MAIN_PACKAGE) || exit 1; \
107 done; \
108 done
109 $(call log_success,"Built for all platforms")
110
111# =====================================================
112# Code Quality
113# =====================================================
114
115.PHONY: fmt
116fmt: ## Format code
117 $(call log_info,"Formatting code")
118 @gofmt -s -w $(GO_FILES)
119 @goimports -w $(GO_FILES) 2>/dev/null || true
120 $(call log_success,"Code formatted")
121
122.PHONY: vet
123vet: ## Run go vet
124 $(call log_info,"Running go vet")
125 @go vet ./...
126 $(call log_success,"go vet passed")
127
128.PHONY: lint
129lint: ## Run linter
130 $(call log_info,"Running golangci-lint")
131 @golangci-lint run --timeout=5m ./... || \
132 ($(call log_error,"golangci-lint failed or not installed"); exit 1)
133 $(call log_success,"Linting passed")
134
135.PHONY: security
136security: ## Run security scan
137 $(call log_info,"Running security scan")
138 @gosec -quiet ./... || \
139 ($(call log_warning,"gosec not installed. Run: go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest"); exit 0)
140 $(call log_success,"Security scan passed")
141
142.PHONY: check
143check: fmt vet lint test ## Run all checks
144 $(call log_success,"All checks passed")
145
146# =====================================================
147# Testing
148# =====================================================
149
150.PHONY: test
151test: ## Run tests
152 $(call log_info,"Running tests")
153 @go test -v -race -count=1 ./...
154
155.PHONY: test-coverage
156test-coverage: ## Run tests with coverage
157 $(call log_info,"Running tests with coverage")
158 @go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
159 @go tool cover -html=coverage.out -o coverage.html
160 @COVERAGE=$$(go tool cover -func=coverage.out | grep total | awk '{print $$3}'); \
161 echo "Total coverage: $${COVERAGE}"; \
162 if (( $$(echo "$${COVERAGE%\%} < 70" | bc -l) )); then \
163 $(call log_warning,"Coverage $${COVERAGE} is below 70%"); \
164 else \
165 $(call log_success,"Coverage $${COVERAGE} meets threshold"); \
166 fi
167
168.PHONY: bench
169bench: ## Run benchmarks
170 $(call log_info,"Running benchmarks")
171 @go test -bench=. -benchmem -run=^$$ ./...
172
173# =====================================================
174# Dependencies
175# =====================================================
176
177.PHONY: deps
178deps: ## Download dependencies
179 $(call log_info,"Downloading dependencies")
180 @go mod download
181 @go mod verify
182 $(call log_success,"Dependencies downloaded")
183
184.PHONY: deps-update
185deps-update: ## Update dependencies
186 $(call log_info,"Updating dependencies")
187 @go get -u ./...
188 @go mod tidy
189 $(call log_success,"Dependencies updated")
190
191# =====================================================
192# Tools
193# =====================================================
194
195.PHONY: install-tools
196install-tools: ## Install development tools
197 $(call log_info,"Installing development tools")
198 @go install golang.org/x/tools/cmd/goimports@latest
199 @go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
200 @go install github.com/cosmtrek/air@latest
201 @go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
202 $(call log_success,"Tools installed")
203
204# =====================================================
205# Docker
206# =====================================================
207
208.PHONY: docker-build
209docker-build: ## Build Docker image
210 $(call log_info,"Building Docker image")
211 @docker build -t $(BINARY_NAME):$(VERSION) -t $(BINARY_NAME):latest .
212 $(call log_success,"Built $(BINARY_NAME):$(VERSION)")
213
214.PHONY: docker-run
215docker-run: ## Run Docker container
216 $(call log_info,"Running Docker container")
217 @docker run --rm -p 8080:8080 $(BINARY_NAME):latest
218
219# =====================================================
220# Setup
221# =====================================================
222
223.PHONY: setup
224setup: deps install-tools ## Setup development environment
225 $(call log_info,"Setting up development environment")
226 @mkdir -p $(BUILD_DIR) $(BIN_DIR) $(DIST_DIR)
227 @make git-hooks
228 $(call log_success,"Development environment ready")
229
230.PHONY: git-hooks
231git-hooks: ## Install Git hooks
232 $(call log_info,"Installing Git hooks")
233 @mkdir -p .git/hooks
234 @echo '#!/bin/bash' > .git/hooks/pre-commit
235 @echo 'set -e' >> .git/hooks/pre-commit
236 @echo 'make fmt vet test' >> .git/hooks/pre-commit
237 @chmod +x .git/hooks/pre-commit
238 $(call log_success,"Git hooks installed")
239
240# =====================================================
241# Clean
242# =====================================================
243
244.PHONY: clean
245clean: ## Remove build artifacts
246 $(call log_info,"Cleaning build artifacts")
247 @rm -rf $(BUILD_DIR) $(BIN_DIR) $(DIST_DIR)
248 @rm -f coverage.* *.prof *.test
249 $(call log_success,"Cleaned")
250
251# =====================================================
252# Information
253# =====================================================
254
255.PHONY: version
256version: ## Show version information
257 @echo "Version: $(VERSION)"
258 @echo "Build time: $(BUILD_TIME)"
259 @echo "Git commit: $(GIT_COMMIT)"
260
261.PHONY: info
262info: ## Show project information
263 @echo "Binary: $(BINARY_NAME)"
264 @echo "Go version: $(shell go version)"
265 @echo "Packages: $(words $(GO_PACKAGES))"
266 @echo "Go files: $(words $(GO_FILES))"
Exercise 4: Development Environment with Docker Compose
Create a complete development environment using Docker Compose.
Requirements:
- Hot reload development container
- Database and cache services
- Volume mounts for code
- Network configuration
- Health checks
Starter Code:
1# Your task: Complete this docker-compose.yml
2version: '3.8'
3
4services:
5 app:
6 build:
7 context: .
8 dockerfile: Dockerfile.dev
9 # TODO: Add ports, volumes, environment
10 # TODO: Add dependencies
11
12 # TODO: Add database service
13 # TODO: Add cache service
Solution
1# docker-compose.dev.yml - Complete development environment
2version: '3.8'
3
4services:
5 app:
6 build:
7 context: .
8 dockerfile: Dockerfile.dev
9 args:
10 GO_VERSION: 1.22
11 container_name: myapp-dev
12 ports:
13 - "8080:8080"
14 - "2345:2345" # Delve debugger port
15 volumes:
16 - .:/app
17 - go-modules:/go/pkg/mod
18 - go-build-cache:/root/.cache/go-build
19 environment:
20 - GO_ENV=development
21 - DATABASE_URL=postgresql://dev:dev@postgres:5432/myapp?sslmode=disable
22 - REDIS_URL=redis://redis:6379/0
23 - LOG_LEVEL=debug
24 - LOG_FORMAT=text
25 depends_on:
26 postgres:
27 condition: service_healthy
28 redis:
29 condition: service_healthy
30 command: air -c .air.toml
31 networks:
32 - app-network
33 restart: unless-stopped
34
35 postgres:
36 image: postgres:16-alpine
37 container_name: myapp-postgres
38 ports:
39 - "5432:5432"
40 environment:
41 - POSTGRES_USER=dev
42 - POSTGRES_PASSWORD=dev
43 - POSTGRES_DB=myapp
44 - POSTGRES_INITDB_ARGS=--encoding=UTF-8
45 volumes:
46 - postgres-data:/var/lib/postgresql/data
47 - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro
48 healthcheck:
49 test: ["CMD-SHELL", "pg_isready -U dev -d myapp"]
50 interval: 10s
51 timeout: 5s
52 retries: 5
53 networks:
54 - app-network
55 restart: unless-stopped
56
57 redis:
58 image: redis:7-alpine
59 container_name: myapp-redis
60 ports:
61 - "6379:6379"
62 volumes:
63 - redis-data:/data
64 - ./configs/redis.conf:/usr/local/etc/redis/redis.conf:ro
65 command: redis-server /usr/local/etc/redis/redis.conf
66 healthcheck:
67 test: ["CMD", "redis-cli", "ping"]
68 interval: 10s
69 timeout: 3s
70 retries: 5
71 networks:
72 - app-network
73 restart: unless-stopped
74
75 adminer:
76 image: adminer:latest
77 container_name: myapp-adminer
78 ports:
79 - "8081:8080"
80 environment:
81 - ADMINER_DEFAULT_SERVER=postgres
82 depends_on:
83 - postgres
84 networks:
85 - app-network
86 restart: unless-stopped
87
88volumes:
89 go-modules:
90 driver: local
91 go-build-cache:
92 driver: local
93 postgres-data:
94 driver: local
95 redis-data:
96 driver: local
97
98networks:
99 app-network:
100 driver: bridge
1# Dockerfile.dev - Development container
2FROM golang:1.22-alpine
3
4# Install development dependencies
5RUN apk add --no-cache \
6 git \
7 make \
8 gcc \
9 musl-dev \
10 curl \
11 bash \
12 vim \
13 postgresql-client \
14 redis
15
16# Install Go tools
17RUN go install github.com/cosmtrek/air@latest && \
18 go install golang.org/x/tools/cmd/goimports@latest && \
19 go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest && \
20 go install github.com/go-delve/delve/cmd/dlv@latest
21
22WORKDIR /app
23
24# Copy dependency files
25COPY go.mod go.sum ./
26RUN go mod download
27
28# Copy source code
29COPY . .
30
31# Expose application and debugger ports
32EXPOSE 8080 2345
33
34# Default command
35CMD ["air", "-c", ".air.toml"]
1# .air.toml - Hot reload configuration
2root = "."
3testdata_dir = "testdata"
4tmp_dir = "tmp"
5
6[build]
7 args_bin = []
8 bin = "./tmp/main"
9 cmd = "go build -gcflags='all=-N -l' -o ./tmp/main ./cmd/myapp"
10 delay = 1000
11 exclude_dir = ["assets", "tmp", "vendor", "testdata"]
12 exclude_file = []
13 exclude_regex = ["_test.go"]
14 exclude_unchanged = false
15 follow_symlink = false
16 full_bin = "./tmp/main"
17 include_dir = []
18 include_ext = ["go", "tpl", "tmpl", "html"]
19 include_file = []
20 kill_delay = "0s"
21 log = "build-errors.log"
22 poll = false
23 poll_interval = 0
24 rerun = false
25 rerun_delay = 500
26 send_interrupt = false
27 stop_on_error = false
28
29[color]
30 app = ""
31 build = "yellow"
32 main = "magenta"
33 runner = "green"
34 watcher = "cyan"
35
36[log]
37 main_only = false
38 time = false
39
40[misc]
41 clean_on_exit = false
42
43[screen]
44 clear_on_rebuild = false
45 keep_scroll = true
1# scripts/dev.sh - Development helper script
2#!/bin/bash
3set -e
4
5# Colors
6RED='\033[0;31m'
7GREEN='\033[0;32m'
8YELLOW='\033[1;33m'
9NC='\033[0m' # No Color
10
11echo -e "${GREEN}Starting development environment...${NC}"
12
13# Check Docker
14if ! command -v docker &> /dev/null; then
15 echo -e "${RED}Docker is not installed${NC}"
16 exit 1
17fi
18
19# Check Docker Compose
20if ! docker compose version &> /dev/null; then
21 echo -e "${RED}Docker Compose is not installed${NC}"
22 exit 1
23fi
24
25# Start services
26echo -e "${YELLOW}Starting services...${NC}"
27docker compose -f docker-compose.dev.yml up -d
28
29# Wait for services to be healthy
30echo -e "${YELLOW}Waiting for services to be ready...${NC}"
31until docker compose -f docker-compose.dev.yml ps | grep -q "healthy"; do
32 sleep 2
33 echo -n "."
34done
35echo ""
36
37echo -e "${GREEN}Development environment is ready!${NC}"
38echo ""
39echo "Services:"
40echo " - Application: http://localhost:8080"
41echo " - Adminer (DB UI): http://localhost:8081"
42echo " - PostgreSQL: localhost:5432"
43echo " - Redis: localhost:6379"
44echo ""
45echo "Logs: docker compose -f docker-compose.dev.yml logs -f app"
46echo "Stop: docker compose -f docker-compose.dev.yml down"
Exercise 5: Automated Release System
Build an automated release system with changelog generation.
Requirements:
- Semantic versioning
- Changelog generation from commits
- Multi-platform binary builds
- GitHub Release creation
- Docker image publishing
Starter Code:
1#!/bin/bash
2# Your task: Complete this release script
3
4VERSION=$1
5
6if [ -z "$VERSION" ]; then
7 echo "Usage: $0 <version>"
8 exit 1
9fi
10
11# TODO: Validate version format
12# TODO: Check for uncommitted changes
13# TODO: Run tests
14# TODO: Build artifacts
15# TODO: Create release
Solution
1#!/bin/bash
2# scripts/release.sh - Automated release system
3set -euo pipefail
4
5# Colors
6RED='\033[0;31m'
7GREEN='\033[0;32m'
8YELLOW='\033[1;33m'
9BLUE='\033[0;34m'
10NC='\033[0m' # No Color
11
12# Functions
13log_info() {
14 echo -e "${BLUE}โน $1${NC}"
15}
16
17log_success() {
18 echo -e "${GREEN}โ $1${NC}"
19}
20
21log_warning() {
22 echo -e "${YELLOW}โ $1${NC}"
23}
24
25log_error() {
26 echo -e "${RED}โ $1${NC}"
27 exit 1
28}
29
30# Parse arguments
31VERSION=$1
32SKIP_TESTS=${SKIP_TESTS:-false}
33DRY_RUN=${DRY_RUN:-false}
34
35if [ -z "$VERSION" ]; then
36 log_error "Usage: $0 <version>"
37fi
38
39# Validate version format (semver)
40if ! [[ $VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then
41 log_error "Invalid version format. Use semantic versioning (e.g., v1.2.3)"
42fi
43
44log_info "Preparing release ${VERSION}"
45
46# Check for uncommitted changes
47if [[ -n $(git status -s) ]]; then
48 log_warning "You have uncommitted changes"
49 read -p "Continue anyway? (y/N) " -n 1 -r
50 echo
51 if [[ ! $REPLY =~ ^[Yy]$ ]]; then
52 exit 1
53 fi
54fi
55
56# Verify we're on main branch
57CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
58if [ "$CURRENT_BRANCH" != "main" ]; then
59 log_warning "You are not on main branch (currently on: $CURRENT_BRANCH)"
60 read -p "Continue anyway? (y/N) " -n 1 -r
61 echo
62 if [[ ! $REPLY =~ ^[Yy]$ ]]; then
63 exit 1
64 fi
65fi
66
67# Check if tag already exists
68if git rev-parse "$VERSION" >/dev/null 2>&1; then
69 log_error "Tag $VERSION already exists"
70fi
71
72# Pull latest changes
73log_info "Pulling latest changes"
74git pull origin main
75
76# Run tests
77if [ "$SKIP_TESTS" != "true" ]; then
78 log_info "Running tests"
79 make test || log_error "Tests failed"
80 log_success "Tests passed"
81fi
82
83# Run linting
84log_info "Running linting"
85make lint || log_error "Linting failed"
86log_success "Linting passed"
87
88# Create distribution directory
89DIST_DIR="dist/$VERSION"
90mkdir -p "$DIST_DIR"
91
92# Build for all platforms
93log_info "Building for all platforms"
94make build-all || log_error "Build failed"
95log_success "Build completed"
96
97# Generate checksums
98log_info "Generating checksums"
99cd dist
100for file in myapp-*; do
101 if [ -f "$file" ]; then
102 sha256sum "$file" > "$file.sha256"
103 fi
104done
105cd ..
106
107# Generate changelog
108log_info "Generating changelog"
109PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
110
111if [ -n "$PREVIOUS_TAG" ]; then
112 CHANGELOG=$(git log --pretty=format:"- %s (%h)" "$PREVIOUS_TAG..HEAD")
113else
114 CHANGELOG=$(git log --pretty=format:"- %s (%h)")
115fi
116
117cat > "$DIST_DIR/CHANGELOG.md" <<EOF
118# Release $VERSION
119
120## Changes since $PREVIOUS_TAG
121
122$CHANGELOG
123
124## Installation
125
126Download the appropriate binary for your platform from the assets below.
127
128## Docker
129
130\`\`\`bash
131docker pull ghcr.io/your-org/myapp:$VERSION
132\`\`\`
133
134## Checksums
135
136SHA256 checksums are provided for all binaries.
137EOF
138
139# Create git tag
140if [ "$DRY_RUN" != "true" ]; then
141 log_info "Creating git tag"
142 git tag -a "$VERSION" -m "Release $VERSION"
143
144 log_info "Pushing tag to origin"
145 git push origin "$VERSION"
146
147 log_success "Tag created and pushed"
148else
149 log_info "DRY RUN: Would create tag $VERSION"
150fi
151
152# Build Docker image
153log_info "Building Docker image"
154docker build -t myapp:"$VERSION" -t myapp:latest .
155log_success "Docker image built"
156
157# Push Docker image
158if [ "$DRY_RUN" != "true" ]; then
159 log_info "Pushing Docker image"
160 docker push myapp:"$VERSION"
161 docker push myapp:latest
162 log_success "Docker image pushed"
163else
164 log_info "DRY RUN: Would push Docker images"
165fi
166
167# Create GitHub release (requires gh CLI)
168if command -v gh &> /dev/null; then
169 if [ "$DRY_RUN" != "true" ]; then
170 log_info "Creating GitHub release"
171 gh release create "$VERSION" \
172 --title "Release $VERSION" \
173 --notes-file "$DIST_DIR/CHANGELOG.md" \
174 dist/myapp-* dist/myapp-*.sha256
175
176 log_success "GitHub release created"
177 else
178 log_info "DRY RUN: Would create GitHub release"
179 fi
180else
181 log_warning "GitHub CLI not installed. Skipping GitHub release creation"
182 log_info "Install gh: https://cli.github.com/"
183fi
184
185log_success "Release $VERSION completed!"
186echo ""
187echo "Next steps:"
188echo " 1. Update documentation if needed"
189echo " 2. Announce the release"
190echo " 3. Update changelog in main branch"
1# Add to Makefile
2.PHONY: release
3release: ## Create a new release
4 @if [ -z "$(VERSION)" ]; then \
5 echo "Usage: make release VERSION=v1.0.0"; \
6 exit 1; \
7 fi
8 @./scripts/release.sh $(VERSION)
9
10.PHONY: release-dry-run
11release-dry-run: ## Test release process
12 @if [ -z "$(VERSION)" ]; then \
13 echo "Usage: make release-dry-run VERSION=v1.0.0"; \
14 exit 1; \
15 fi
16 @DRY_RUN=true ./scripts/release.sh $(VERSION)
1# .github/workflows/release.yml - Automated release workflow
2name: Release
3
4on:
5 push:
6 tags:
7 - 'v*'
8
9permissions:
10 contents: write
11 packages: write
12
13env:
14 GO_VERSION: '1.22'
15 REGISTRY: ghcr.io
16 IMAGE_NAME: ${{ github.repository }}
17
18jobs:
19 release:
20 name: Create Release
21 runs-on: ubuntu-latest
22
23 steps:
24 - name: Checkout
25 uses: actions/checkout@v4
26 with:
27 fetch-depth: 0
28
29 - name: Setup Go
30 uses: actions/setup-go@v4
31 with:
32 go-version: ${{ env.GO_VERSION }}
33
34 - name: Run tests
35 run: go test -v -race ./...
36
37 - name: Build binaries
38 run: make build-all
39
40 - name: Generate checksums
41 run: |
42 cd dist
43 for file in *; do
44 sha256sum "$file" > "$file.sha256"
45 done
46
47 - name: Generate changelog
48 id: changelog
49 uses: metcalfc/changelog-generator@v4
50 with:
51 myToken: ${{ secrets.GITHUB_TOKEN }}
52
53 - name: Create Release
54 uses: softprops/action-gh-release@v1
55 with:
56 body: ${{ steps.changelog.outputs.changelog }}
57 files: |
58 dist/*
59 draft: false
60 prerelease: false
61
62 - name: Log in to registry
63 uses: docker/login-action@v3
64 with:
65 registry: ${{ env.REGISTRY }}
66 username: ${{ github.actor }}
67 password: ${{ secrets.GITHUB_TOKEN }}
68
69 - name: Extract version
70 id: version
71 run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
72
73 - name: Build and push Docker image
74 uses: docker/build-push-action@v5
75 with:
76 context: .
77 push: true
78 tags: |
79 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
80 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
81 labels: |
82 org.opencontainers.image.title=${{ github.repository }}
83 org.opencontainers.image.version=${{ steps.version.outputs.VERSION }}
Further Reading
- Go Module Documentation - Dependency management
- GitHub Actions Documentation - CI/CD automation
- Docker Best Practices - Container development
- Effective Go - Go coding standards
- Makefile Tutorial - Build automation
Previous: 17 Debugging Go Applications | Next: Practice Exercises
Summary
Key Takeaways
๐๏ธ Workflow Architecture:
- Build assembly lines with quality gates at each stage
- Implement zero-trust development with automated validation
- Use Makefiles as command centers for complex workflows
- Set up hot reload for immediate development feedback
๐ง Automation Strategy:
- Git hooks catch issues before they reach repositories
- CI/CD pipelines ensure consistent quality across teams
- Containerized environments eliminate "it works on my machine" issues
- Task runners coordinate complex multi-step processes
โก Development Experience:
- Fast feedback loops with hot reload and incremental builds
- Comprehensive tooling setup with one-command installation
- Consistent IDE configuration across team members
- Documentation that evolves with the codebase
Next Steps in Your Learning Journey
๐ Recommended Learning Path:
- Implement these workflows in your own projects immediately
- Customize and extend the patterns to fit your specific needs
- Study team collaboration workflows and best practices
- Explore advanced CI/CD patterns for production systems
- Contribute to workflow tools to understand their internals
๐ ๏ธ Advanced Topics to Explore:
- Monorepo workflows with multiple Go services
- Feature flag systems for controlled deployments
- Progressive delivery patterns with canary releases
- Automated dependency updates and vulnerability scanning
- Cross-platform CI/CD for different deployment targets
๐ Production Readiness:
- Set up comprehensive monitoring and alerting
- Implement disaster recovery and rollback procedures
- Create onboarding documentation for new team members
- Establish code review processes and standards
Remember: Great development workflows evolve with your team and projects. Start with the basics, measure what works, and continuously improve based on real usage patterns.