# Project Makefile
.DEFAULT_GOAL := help
# Variables
APP_NAME := web-app
VERSION := $(shell git describe --tags --always --dirty)
COMMIT := $(shell git rev-parse --short HEAD)
BUILD_TIME := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
REGISTRY := registry.example.com
IMAGE := $(REGISTRY)/$(APP_NAME)
# Environment (override with: make deploy ENV=production)
ENV ?= staging
NAMESPACE ?= $(ENV)
DOCKER_COMPOSE := docker compose
.PHONY: help
help: ## Show this help message
@echo "Usage: make [target]"
@echo ""
@echo "Targets:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
# === Development ===
.PHONY: dev
dev: ## Start development environment
$(DOCKER_COMPOSE) up -d
@echo "Development environment running at http://localhost:3000"
.PHONY: dev-down
dev-down: ## Stop development environment
$(DOCKER_COMPOSE) down
.PHONY: dev-logs
dev-logs: ## Follow development logs
$(DOCKER_COMPOSE) logs -f app
.PHONY: install
install: ## Install dependencies
npm ci
.PHONY: dev-db
dev-db: ## Open database shell
$(DOCKER_COMPOSE) exec db psql -U postgres myapp
# === Testing ===
.PHONY: test
test: ## Run tests
npm test
.PHONY: test-watch
test-watch: ## Run tests in watch mode
npm test -- --watch
.PHONY: test-coverage
test-coverage: ## Run tests with coverage
npm test -- --coverage
@echo "Coverage report: coverage/lcov-report/index.html"
.PHONY: lint
lint: ## Run linter
npm run lint
.PHONY: format
format: ## Format code
npm run format
.PHONY: check
check: lint test ## Run all checks (lint + test)
# === Building ===
.PHONY: build
build: ## Build Docker image
docker build --build-arg VERSION=$(VERSION) --build-arg COMMIT=$(COMMIT) --build-arg BUILD_TIME=$(BUILD_TIME) -t $(IMAGE):$(VERSION) -t $(IMAGE):latest .
.PHONY: push
push: build ## Build and push Docker image
docker push $(IMAGE):$(VERSION)
docker push $(IMAGE):latest
# === Deployment ===
.PHONY: deploy
deploy: check push ## Full deploy pipeline (check, build, push, deploy)
@echo "Deploying $(VERSION) to $(ENV)..."
kubectl set image deployment/$(APP_NAME) $(APP_NAME)=$(IMAGE):$(VERSION) -n $(NAMESPACE)
kubectl rollout status deployment/$(APP_NAME) -n $(NAMESPACE)
@echo "Deploy complete!"
.PHONY: rollback
rollback: ## Rollback to previous version
kubectl rollout undo deployment/$(APP_NAME) -n $(NAMESPACE)
kubectl rollout status deployment/$(APP_NAME) -n $(NAMESPACE)
# === Infrastructure ===
.PHONY: tf-init
tf-init: ## Initialize Terraform
cd terraform && terraform init
.PHONY: tf-plan
tf-plan: ## Plan Terraform changes
cd terraform && terraform plan -var-file=$(ENV).tfvars -out=tfplan
.PHONY: tf-apply
tf-apply: ## Apply Terraform changes
cd terraform && terraform apply tfplan
# === Utilities ===
.PHONY: clean
clean: ## Clean build artifacts
rm -rf dist/ coverage/ node_modules/.cache
docker image prune -f
.PHONY: info
info: ## Show project info
@echo "App: $(APP_NAME)"
@echo "Version: $(VERSION)"
@echo "Commit: $(COMMIT)"
@echo "Image: $(IMAGE):$(VERSION)"
@echo "Env: $(ENV)"
Makefiles provide a simple, universal task runner for DevOps workflows. Targets define named tasks with optional dependencies. The .PHONY declaration prevents conflicts with files of the same name. Variables set at the top configure reusable values. $(shell ...) executes commands inline. Conditional logic with ifdef and ifeq adapts to environments. Self-documenting targets with ## comment syntax generate help text automatically. Include files split complex Makefiles into manageable pieces. Make is available on virtually every Unix system, requiring no installation. Targets chain together for complex workflows—deploy depends on test, which depends on build. Default target (first target or .DEFAULT_GOAL) runs with bare make.