Go Development Workflow

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:

  1. 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
  2. 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
  3. 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


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:

  1. Implement these workflows in your own projects immediately
  2. Customize and extend the patterns to fit your specific needs
  3. Study team collaboration workflows and best practices
  4. Explore advanced CI/CD patterns for production systems
  5. 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.