Add convenient 'make setup-hooks' command for configuring git hooks. This makes onboarding easier for new contributors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
316 lines
13 KiB
Makefile
316 lines
13 KiB
Makefile
.PHONY: help dev dev-down dev-build dev-shell dev-logs dev-vite k-setup k-dev k-down k-logs k-shell k-artisan k-composer k-vite k-status prod-build prod-up prod-down prod-logs prod-shell prod-vite prod-test prod-test-build prod-test-up prod-test-down prod-test-logs prod-test-shell prod-test-status prod-test-clean migrate test clean install
|
|
|
|
# Default target
|
|
.DEFAULT_GOAL := help
|
|
|
|
# ==================== K8s Variables ====================
|
|
# Detect if we're in a worktree or project root
|
|
GIT_DIR := $(shell git rev-parse --git-dir 2>/dev/null)
|
|
IS_WORKTREE := $(shell echo "$(GIT_DIR)" | grep -q ".worktrees" && echo "true" || echo "false")
|
|
|
|
# Find project root (handles both worktree and main repo)
|
|
ifeq ($(IS_WORKTREE),true)
|
|
# In a worktree - project root is two levels up
|
|
PROJECT_ROOT := $(shell cd ../.. && pwd)
|
|
WORKTREE_NAME := $(shell basename $(CURDIR))
|
|
K8S_VOLUME_PATH := /worktrees/$(WORKTREE_NAME)
|
|
HOST_WORKTREE_PATH := $(PROJECT_ROOT)/.worktrees
|
|
else
|
|
# In project root
|
|
PROJECT_ROOT := $(shell pwd)
|
|
WORKTREE_NAME := root
|
|
K8S_VOLUME_PATH := /project-root
|
|
HOST_WORKTREE_PATH := $(PROJECT_ROOT)/.worktrees
|
|
endif
|
|
|
|
# Generate namespace from branch name (feat-branch-name)
|
|
CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
|
|
K8S_NS := $(shell echo "$(CURRENT_BRANCH)" | sed 's/feature\//feat-/' | sed 's/bugfix\//fix-/' | sed 's/\//-/g')
|
|
# Generate sanitized branch name for database
|
|
SANITIZED_BRANCH := $(shell echo "$(CURRENT_BRANCH)" | sed 's/[^a-zA-Z0-9]/_/g')
|
|
# Generate host from branch
|
|
K8S_HOST := $(shell echo "$(CURRENT_BRANCH)" | sed 's/feature\///' | sed 's/bugfix\///' | sed 's/\//-/g').cannabrands.test
|
|
# Read database credentials from .env
|
|
DB_USERNAME := $(shell grep '^DB_USERNAME=' .env 2>/dev/null | cut -d '=' -f2)
|
|
DB_PASSWORD := $(shell grep '^DB_PASSWORD=' .env 2>/dev/null | cut -d '=' -f2)
|
|
DB_DATABASE := $(shell grep '^DB_DATABASE=' .env 2>/dev/null | cut -d '=' -f2)
|
|
|
|
# ==================== Local Development (Sail) ====================
|
|
dev: ## Start local development environment with Sail
|
|
./vendor/bin/sail up -d
|
|
|
|
dev-down: ## Stop local development environment
|
|
./vendor/bin/sail down
|
|
|
|
dev-build: ## Build Sail containers
|
|
./vendor/bin/sail build --no-cache
|
|
|
|
dev-shell: ## Open shell in Sail container
|
|
./vendor/bin/sail shell
|
|
|
|
dev-logs: ## View Sail logs
|
|
./vendor/bin/sail logs -f
|
|
|
|
dev-artisan: ## Run artisan command (usage: make dev-artisan CMD="migrate")
|
|
./vendor/bin/sail artisan $(CMD)
|
|
|
|
dev-npm: ## Run npm command (usage: make dev-npm CMD="run dev")
|
|
./vendor/bin/sail npm $(CMD)
|
|
|
|
dev-composer: ## Run composer command (usage: make dev-composer CMD="install")
|
|
./vendor/bin/sail composer $(CMD)
|
|
|
|
dev-vite: ## Start Vite dev server (run after 'make dev')
|
|
./vendor/bin/sail npm run dev
|
|
|
|
# ==================== K8s Local Development ====================
|
|
k-setup: ## One-time setup: Create K3d cluster with auto-detected volume mounts
|
|
@echo "🔧 Setting up K3d cluster 'dev' with auto-detected paths"
|
|
@echo " Project Root: $(PROJECT_ROOT)"
|
|
@echo " Worktrees Path: $(HOST_WORKTREE_PATH)"
|
|
@echo ""
|
|
@# Check if cluster already exists
|
|
@if k3d cluster list | grep -q "^dev "; then \
|
|
echo "⚠️ Cluster 'dev' already exists!"; \
|
|
echo " To recreate, run: k3d cluster delete dev && make k-setup"; \
|
|
exit 1; \
|
|
fi
|
|
@# Create cluster with dynamic volume mounts
|
|
k3d cluster create dev \
|
|
--api-port 6443 \
|
|
--port "80:80@loadbalancer" \
|
|
--port "443:443@loadbalancer" \
|
|
--volume $(HOST_WORKTREE_PATH):/worktrees \
|
|
--volume $(PROJECT_ROOT):/project-root
|
|
@echo ""
|
|
@echo "✅ K3d cluster created successfully!"
|
|
@echo " Next step: Run 'make k-dev' to start your environment"
|
|
|
|
k-dev: ## Start k8s local environment (like Sail, but with namespace isolation)
|
|
@echo "🚀 Starting k8s environment"
|
|
@echo " Location: $(if $(filter true,$(IS_WORKTREE)),Worktree ($(WORKTREE_NAME)),Project Root)"
|
|
@echo " Namespace: $(K8S_NS)"
|
|
@echo " Branch: $(CURRENT_BRANCH)"
|
|
@echo " URL: http://$(K8S_HOST)"
|
|
@echo ""
|
|
@# Create namespace
|
|
@kubectl create ns $(K8S_NS) --dry-run=client -o yaml | kubectl apply -f -
|
|
@# Create secrets from .env
|
|
@kubectl -n $(K8S_NS) delete secret app-env --ignore-not-found
|
|
@kubectl -n $(K8S_NS) create secret generic app-env --from-env-file=.env
|
|
@# Create PostgreSQL auth secret (using credentials from .env)
|
|
@kubectl -n $(K8S_NS) create secret generic pg-auth --dry-run=client -o yaml \
|
|
--from-literal=POSTGRES_DB=$(DB_DATABASE) \
|
|
--from-literal=POSTGRES_USER=$(DB_USERNAME) \
|
|
--from-literal=POSTGRES_PASSWORD=$(DB_PASSWORD) | kubectl apply -f -
|
|
@# Deploy PostgreSQL
|
|
@export NS=$(K8S_NS) PG_DB=$(DB_DATABASE) PG_USER=$(DB_USERNAME) PG_PASS=$(DB_PASSWORD) && \
|
|
envsubst < k8s/local/postgres.yaml | kubectl apply -f -
|
|
@# Deploy Redis
|
|
@export NS=$(K8S_NS) && \
|
|
envsubst < k8s/local/redis.yaml | kubectl apply -f -
|
|
@# Deploy Reverb (WebSocket server)
|
|
@export NS=$(K8S_NS) K8S_VOLUME_PATH=$(K8S_VOLUME_PATH) K8S_HOST=$(K8S_HOST) && \
|
|
envsubst < k8s/local/reverb.yaml | kubectl apply -f -
|
|
@# Wait for DB
|
|
@echo "⏳ Waiting for PostgreSQL..."
|
|
@kubectl -n $(K8S_NS) wait --for=condition=ready pod -l app=postgres --timeout=60s
|
|
@# Deploy app (with code volume mounted)
|
|
@export NS=$(K8S_NS) K8S_VOLUME_PATH=$(K8S_VOLUME_PATH) K8S_HOST=$(K8S_HOST) && \
|
|
envsubst < k8s/local/deployment.yaml | kubectl apply -f -
|
|
@# Create service + ingress
|
|
@export NS=$(K8S_NS) K8S_HOST=$(K8S_HOST) && \
|
|
envsubst < k8s/local/service.yaml | kubectl apply -f - && \
|
|
envsubst < k8s/local/ingress.yaml | kubectl apply -f -
|
|
@echo ""
|
|
@echo "✅ Ready! Visit: http://$(K8S_HOST)"
|
|
@echo ""
|
|
@echo "💡 Your code is volume-mounted - changes are instant!"
|
|
@echo " Edit files → refresh browser → see changes"
|
|
@echo ""
|
|
@echo "📝 Useful commands:"
|
|
@echo " make k-logs # View app logs"
|
|
@echo " make k-shell # Open shell in pod"
|
|
@echo " make k-vite # Start Vite dev server"
|
|
@echo ""
|
|
@echo "🔌 WebSocket (Reverb) available at: ws://reverb.$(K8S_HOST):8080"
|
|
|
|
k-down: ## Stop k8s environment
|
|
@echo "🗑 Removing namespace: $(K8S_NS)"
|
|
@kubectl delete ns $(K8S_NS) --ignore-not-found
|
|
@echo "✅ Cleaned up"
|
|
|
|
k-logs: ## View app logs
|
|
@kubectl -n $(K8S_NS) logs -f deploy/web --all-containers=true
|
|
|
|
k-shell: ## Shell into app container
|
|
@kubectl -n $(K8S_NS) exec -it deploy/web -- /bin/bash
|
|
|
|
k-artisan: ## Run artisan command (usage: make k-artisan CMD="migrate")
|
|
@kubectl -n $(K8S_NS) exec deploy/web -- php artisan $(CMD)
|
|
|
|
k-composer: ## Run composer (usage: make k-composer CMD="install")
|
|
@kubectl -n $(K8S_NS) exec deploy/web -- composer $(CMD)
|
|
|
|
k-vite: ## Run Vite dev server in k8s pod
|
|
@echo "🎨 Starting Vite dev server in pod..."
|
|
@echo " Access at: http://vite.$(K8S_HOST)"
|
|
@kubectl -n $(K8S_NS) exec deploy/web -- npm run dev
|
|
|
|
k-test: ## Run tests in k8s pod
|
|
@echo "🧪 Running tests in k8s pod..."
|
|
@kubectl -n $(K8S_NS) exec deploy/web -- php artisan test
|
|
|
|
k-seed: ## Run database seeders in k8s (usage: make k-seed SEEDER=DevSeeder)
|
|
@kubectl -n $(K8S_NS) exec deploy/web -- php artisan db:seed --class=$(SEEDER)
|
|
|
|
k-migrate-fresh: ## Fresh database with seeding in k8s pod
|
|
@echo "🔄 Running fresh migration with seeding..."
|
|
@kubectl -n $(K8S_NS) exec deploy/web -- php artisan migrate:fresh --seed
|
|
|
|
k-status: ## Show k8s environment status
|
|
@echo "📊 Status for namespace: $(K8S_NS)"
|
|
@echo ""
|
|
@kubectl -n $(K8S_NS) get pods,svc,ingress
|
|
|
|
# ==================== Production ====================
|
|
prod-build: ## Build production Docker image
|
|
docker build -t cannabrands/app:latest -f Dockerfile .
|
|
|
|
prod-up: ## Start production containers
|
|
docker-compose -f docker-compose.production.yml up -d
|
|
|
|
prod-down: ## Stop production containers
|
|
docker-compose -f docker-compose.production.yml down
|
|
|
|
prod-restart: ## Restart production containers
|
|
docker-compose -f docker-compose.production.yml restart
|
|
|
|
prod-logs: ## View production logs
|
|
docker-compose -f docker-compose.production.yml logs -f
|
|
|
|
prod-shell: ## Open shell in production app container
|
|
docker-compose -f docker-compose.production.yml exec app sh
|
|
|
|
prod-artisan: ## Run artisan in production (usage: make prod-artisan CMD="migrate")
|
|
docker-compose -f docker-compose.production.yml exec app php artisan $(CMD)
|
|
|
|
prod-vite: ## Build production assets (for CI/CD)
|
|
./vendor/bin/sail npm run build
|
|
|
|
# ==================== Production Testing (Local) ====================
|
|
prod-test: ## Test production image locally (foreground)
|
|
@echo "🔨 Building and testing production image locally..."
|
|
docker-compose -f docker-compose.prod-test.yml up --build
|
|
|
|
prod-test-build: ## Build production test image (no cache)
|
|
@echo "🔨 Building production test image..."
|
|
docker-compose -f docker-compose.prod-test.yml build --no-cache --pull
|
|
|
|
prod-test-up: ## Start production test environment (background)
|
|
@echo "🚀 Starting production test environment..."
|
|
docker-compose -f docker-compose.prod-test.yml up -d
|
|
@echo ""
|
|
@echo "✅ Production test running!"
|
|
@echo " App: http://localhost:8080"
|
|
@echo " Database: localhost:5433"
|
|
@echo ""
|
|
@echo "View logs: make prod-test-logs"
|
|
|
|
prod-test-down: ## Stop production test environment
|
|
docker-compose -f docker-compose.prod-test.yml down
|
|
|
|
prod-test-logs: ## View production test logs
|
|
docker-compose -f docker-compose.prod-test.yml logs -f app
|
|
|
|
prod-test-shell: ## Open shell in production test container
|
|
docker-compose -f docker-compose.prod-test.yml exec app /bin/sh
|
|
|
|
prod-test-status: ## Check supervisor status in production test
|
|
@echo "📊 Supervisor status:"
|
|
docker-compose -f docker-compose.prod-test.yml exec app supervisorctl status
|
|
|
|
prod-test-clean: ## Clean production test environment (removes volumes)
|
|
@echo "🧹 Cleaning production test environment..."
|
|
docker-compose -f docker-compose.prod-test.yml down -v
|
|
@echo "✅ Cleaned! Run 'make prod-test' for fresh start"
|
|
|
|
# ==================== Database ====================
|
|
migrate: ## Run database migrations (Sail)
|
|
./vendor/bin/sail artisan migrate
|
|
|
|
migrate-fresh: ## Fresh database with seeding (Sail)
|
|
./vendor/bin/sail artisan migrate:fresh --seed
|
|
|
|
migrate-prod: ## Run database migrations (Production)
|
|
docker-compose -f docker-compose.production.yml exec app php artisan migrate --force
|
|
|
|
seed: ## Run database seeders (Sail)
|
|
./vendor/bin/sail artisan db:seed --class=$(SEEDER)
|
|
|
|
# ==================== Testing ====================
|
|
test: ## Run tests (Sail)
|
|
./vendor/bin/sail test
|
|
|
|
test-coverage: ## Run tests with coverage (Sail)
|
|
./vendor/bin/sail test --coverage
|
|
|
|
# ==================== Utilities ====================
|
|
clean: ## Clean up Docker resources
|
|
docker system prune -f
|
|
docker volume prune -f
|
|
|
|
install: ## Initial project setup
|
|
@echo "Installing dependencies..."
|
|
@composer install
|
|
@npm install
|
|
@if [ ! -f .env ]; then cp .env.example .env && echo "Created .env from .env.example"; fi
|
|
@echo "\n✅ Setup complete!"
|
|
@echo "Next steps:"
|
|
@echo " 1. Update .env with your settings"
|
|
@echo " 2. Run 'make dev' to start development environment"
|
|
@echo " 3. Run 'make migrate' to set up database"
|
|
|
|
setup-hooks: ## Configure git hooks for code quality
|
|
@git config core.hooksPath .githooks
|
|
@chmod +x .githooks/*
|
|
@echo "✅ Git hooks configured!"
|
|
@echo " - pre-commit: Auto-formats code with Laravel Pint"
|
|
@echo " - pre-push: Optionally runs tests before pushing"
|
|
|
|
mailpit: ## Open Mailpit web UI
|
|
@open http://localhost:8025 || xdg-open http://localhost:8025 || echo "Open http://localhost:8025 in your browser"
|
|
|
|
new-worktree: ## Create new worktree (usage: make new-worktree BRANCH=feature/my-feature or make new-worktree BRANCH=feature/my-feature NEW=true)
|
|
@if [ -z "$(BRANCH)" ]; then \
|
|
echo "❌ Error: BRANCH parameter required"; \
|
|
echo ""; \
|
|
echo "Usage:"; \
|
|
echo " make new-worktree BRANCH=feature/my-feature # Checkout existing branch"; \
|
|
echo " make new-worktree BRANCH=feature/my-feature NEW=true # Create new branch"; \
|
|
exit 1; \
|
|
fi
|
|
@if [ "$(NEW)" = "true" ]; then \
|
|
./scripts/new-worktree.sh -b $(BRANCH); \
|
|
else \
|
|
./scripts/new-worktree.sh $(BRANCH); \
|
|
fi
|
|
|
|
help: ## Show this help message
|
|
@echo "\n📦 CannaBrands Docker Commands\n"
|
|
@echo "Local Development (Sail):"
|
|
@grep -E '^dev.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo "\nK8s Local Development:"
|
|
@grep -E '^k-.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[35m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo "\nProduction Testing (Local):"
|
|
@grep -E '^prod-test.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[33m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo "\nProduction (K8s/Deployment):"
|
|
@grep -E '^prod-[^t].*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo "\nDatabase:"
|
|
@grep -E '^(migrate|seed).*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo "\nTesting:"
|
|
@grep -E '^test.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo "\nUtilities:"
|
|
@grep -E '^(clean|install|mailpit).*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
|
@echo ""
|