
Code quality is crucial for maintaining a scalable, efficient, and secure application. In Go, enforcing best practices through automated checks ensures a consistent codebase and minimizes bugs. In this guide, we will explore a structured approach to applying coding standards in Go using a script that automates key validation steps.
Maintaining coding standards in Go provides several benefits:
Neglecting these checks can result in:
The provided steps ensure Go checks to enforce best practices.
go fmt)go vet)goimports)golangci-lint)swag init)go mod tidy)go build)gosec)gitleaks)go test)go test -cover)go fmt)go fmt ./...Example (Before Formatting):
package main
import "fmt"func main() {
fmt.Println("Hello, World!")
}After Running go fmt:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}go vet)go vet ./...Example:
package main
import "fmt"
func main() {
var unusedVar int // go vet will flag this as an unused variable
fmt.Println("Hello, world!")
}Output from go vet:
main.go:5:6: unused variable 'unusedVar'goimports)goimports -w .Example (Before Formatting):
import ("fmt" "os")After Running goimports:
import (
"fmt"
"os"
)golangci-lint)golangci-lint runExample:
package main
import "fmt"
func main() {
fmt.Println("Hello") // golangci-lint will warn if error handling is missing
}swag init)swag initExample:
// @Summary Get user by ID
// @Description Get details of a user
// @Param id path int true "User ID"
// @Success 200 {object} User
// @Router /users/{id} [get]
func GetUserByID(c *gin.Context) {
// Handler logic
}go mod tidy)go.mod.go mod tidy && go list -m allgo build)go build ./...gosec)gosec ./...Example:
package main
func main() {
password := "mypassword" // gosec will flag this as a potential security risk
}gitleaks)gitleaks detect . --verboseExample:
package main
func main() {
apiKey := "sk_test_123456789" // gitleaks will detect this as a sensitive key
}go test)go test ./...Example:
package math
import "testing"
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, but got %d", result)
}
}go test -cover)go test -cover ./...Example:
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.outTo automate these checks, use the following script:
#!/bin/bash
# ============================ PARAMETERS ============================
# Set default flags to execute all steps
RUN_FMT=true
RUN_VET=true
RUN_IMPORTS=true
RUN_LINT=true
RUN_SWAG=true
RUN_TIDY=true
RUN_BUILD=true
RUN_SECURITY=true
RUN_GIT_LEAKS=true
RUN_TEST=true
# Parse flags
while [[ "$#" -gt 0 ]]; do
case $1 in
--skip-fmt) RUN_FMT=false ;;
--skip-vet) RUN_VET=false ;;
--skip-imports) RUN_IMPORTS=false ;;
--skip-lint) RUN_LINT=false ;;
--skip-swag) RUN_SWAG=false ;;
--skip-tidy) RUN_TIDY=false ;;
--skip-build) RUN_BUILD=false ;;
--skip-security) RUN_SECURITY=false ;;
--skip-git-leak) RUN_GIT_LEAKS=false ;;
--skip-test) RUN_TEST=false ;;
*) echo "Unknown parameter: $1" ;;
esac
shift
done
# ============================ STATUS TRACKING ============================
STEPS=("go fmt" "swag init" "go vet" "Tidying modules" "goimports" "golangci-lint" "Building project" "Security checks (gosec)" "Git leaks check" "Unit tests" "Test coverage")
STATUSES=() # Parallel array for statuses
PASSED_COUNT=0
FAILED_COUNT=0
# ============================ FUNCTIONS ============================
log_banner() {
echo ""
echo "==================================================================="
echo " $1 : $2"
echo "==================================================================="
echo ""
}
run_step() {
local step_name="$1"
local command="$2"
local description="$3"
log_banner "Executing: $step_name" "$command" "$description"
eval "$command"
if [[ $? -eq 0 ]]; then
echo "✔️ $step_name completed successfully."
STATUSES+=("✔️ > $step_name: PASSED")
PASSED_COUNT=$((PASSED_COUNT + 1))
else
echo "❌ $step_name failed."
STATUSES+=("❌> $step_name: FAILED > Run: '$command'" )
FAILED_COUNT=$((FAILED_COUNT + 1))
fi
}
print_status_board() {
echo ""
echo "==================================================================="
echo " FINAL STATUS BOARD "
echo "==================================================================="
for status in "${STATUSES[@]}"; do
echo "$status"
done
echo "==================================================================="
echo " SUMMARY "
echo "==================================================================="
echo "✔️ Passed: $PASSED_COUNT"
echo "❌ Failed: $FAILED_COUNT"
echo "==================================================================="
echo ""
}
# ============================ EXECUTION ============================
if $RUN_FMT; then
run_step "Formatting" "go fmt ./..." "Code Formatting and Style"
else
STATUSES+=("go fmt: SKIPPED")
fi
if $RUN_SWAG; then
run_step "Swag Init" "swag init --dir cmd,internal,pkg --output ./docs --parseDependency --parseFuncBody --parseVendor --parseDependencyLevel 3 --parseInternal" "Updates swagger doc"
else
STATUSES+=("swag init: SKIPPED")
fi
if $RUN_VET; then
run_step "Code Analysis" "go vet ./..." "Static Code Analysis: Detects issues like unreachable code, incorrect struct tags, and more."
else
STATUSES+=("go vet: SKIPPED")
fi
if $RUN_TIDY; then
run_step "Dependency Management" "go mod tidy && go list -m all" "Dependency Management : Clean up and Ensures none are missing"
else
STATUSES+=("Tidying modules: SKIPPED")
fi
if $RUN_IMPORTS; then
run_step "Import formatting" "goimports -w ." "Check import Formatting and Ordering"
else
STATUSES+=("goimports: SKIPPED")
fi
if $RUN_LINT; then
run_step "LINT" "golangci-lint run" "Code Linting: Ensures error handling , standards, bug findings, suggestions"
else
STATUSES+=("golangci-lint: SKIPPED")
fi
if $RUN_BUILD; then
run_step "Build" "go build ./..." "Compiles the project to check for build errors."
else
STATUSES+=("Building project: SKIPPED")
fi
if $RUN_SECURITY; then
run_step "Security Check" "gosec ./..." "Security Checks : vulnerabilities"
else
STATUSES+=("Security checks (gosec): SKIPPED")
fi
if $RUN_GIT_LEAKS; then
run_step "Git Leaks Check" "gitleaks detect . --verbose" "Check for secrets exposing"
else
STATUSES+=("Git leaks check: SKIPPED")
fi
if $RUN_TEST; then
run_step "Unit tests" "go test ./..." "Unit test Executions"
run_step "Test coverage" "go test -cover ./..." "Code coverage validations"
else
STATUSES+=("Unit Testing: SKIPPED")
STATUSES+=("Test Coverage: SKIPPED")
fi
log_banner "ALL CHECKS COMPLETED!"
print_status_board
# ============================ END OF SCRIPT ============================Automating these checks ensures that Go applications maintain high-quality standards. By integrating these steps into your CI/CD pipeline, you can enforce best practices, prevent regressions, and improve team collaboration.
