Files
ExcaliDash/Makefile
T
2026-01-14 10:38:28 -08:00

551 lines
20 KiB
Makefile

# ExcaliDash Makefile
# Comprehensive development, testing, and release automation
.PHONY: help install dev build test test-frontend test-backend test-e2e test-e2e-docker \
lint lint-frontend lint-backend clean docker-build docker-run docker-down docker-logs \
release pre-release version-bump changelog db-migrate db-reset
# Colors
GREEN := \033[0;32m
YELLOW := \033[1;33m
BLUE := \033[0;34m
RED := \033[0;31m
NC := \033[0m
# Configuration
DOCKER_USERNAME := zimengxiong
IMAGE_NAME := excalidash
VERSION := $(shell cat VERSION 2>/dev/null || echo "0.0.0")
# Default target
.DEFAULT_GOAL := help
#===============================================================================
# HELP
#===============================================================================
help: ## Show this help message
@echo ""
@echo "$(GREEN)ExcaliDash Makefile$(NC)"
@echo "$(GREEN)==================$(NC)"
@echo ""
@echo "$(YELLOW)Usage:$(NC) make [target]"
@echo ""
@echo "$(BLUE)Development:$(NC)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(install|dev|build|lint|clean)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}'
@echo ""
@echo "$(BLUE)Testing:$(NC)"
@grep -E '^test[-a-zA-Z0-9_]*:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}'
@echo ""
@echo "$(BLUE)Docker:$(NC)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(docker)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}'
@echo ""
@echo "$(BLUE)Release:$(NC)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(release|version|changelog)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}'
@echo ""
@echo "$(BLUE)Database:$(NC)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '(db-)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2}'
@echo ""
@echo "$(YELLOW)Current version:$(NC) $(VERSION)"
@echo ""
#===============================================================================
# DEVELOPMENT
#===============================================================================
install: ## Install all dependencies (frontend, backend, e2e)
@echo "$(YELLOW)Installing frontend dependencies...$(NC)"
cd frontend && npm install
@echo "$(YELLOW)Installing backend dependencies...$(NC)"
cd backend && npm install
@echo "$(YELLOW)Installing e2e dependencies...$(NC)"
cd e2e && npm install
@echo "$(GREEN)All dependencies installed!$(NC)"
dev: ## Start development servers (frontend + backend)
@echo "$(YELLOW)Starting development servers...$(NC)"
@echo "$(BLUE)Backend will run on port 8000, Frontend on port 5173$(NC)"
@trap 'kill 0' INT; \
(cd backend && npm run dev) & \
(cd frontend && npm run dev) & \
wait
dev-frontend: ## Start frontend dev server only
cd frontend && npm run dev
dev-backend: ## Start backend dev server only
cd backend && npm run dev
build: ## Build frontend and backend for production
@echo "$(YELLOW)Building frontend...$(NC)"
cd frontend && npm run build
@echo "$(GREEN)Build complete!$(NC)"
lint: lint-frontend lint-backend ## Run linters for frontend and backend
lint-frontend: ## Run frontend linter
@echo "$(YELLOW)Linting frontend...$(NC)"
cd frontend && npm run lint
lint-backend: ## Run backend linter (if available)
@echo "$(YELLOW)Backend linting not configured$(NC)"
clean: ## Clean build artifacts and node_modules
@echo "$(YELLOW)Cleaning build artifacts...$(NC)"
rm -rf frontend/dist
rm -rf frontend/node_modules/.vite
@echo "$(GREEN)Clean complete!$(NC)"
clean-all: clean ## Clean everything including node_modules
@echo "$(RED)Removing all node_modules...$(NC)"
rm -rf frontend/node_modules
rm -rf backend/node_modules
rm -rf e2e/node_modules
@echo "$(GREEN)Full clean complete!$(NC)"
#===============================================================================
# TESTING
#===============================================================================
test: test-frontend test-backend ## Run all tests (frontend + backend unit tests)
@echo "$(GREEN)All unit tests passed!$(NC)"
test-all: test test-e2e ## Run ALL tests (unit + e2e)
@echo "$(GREEN)All tests passed!$(NC)"
test-frontend: ## Run frontend unit tests
@echo "$(YELLOW)Running frontend tests...$(NC)"
cd frontend && npm test
test-backend: ## Run backend unit tests
@echo "$(YELLOW)Running backend tests...$(NC)"
cd backend && npm test
test-coverage: ## Run all unit tests with coverage
@echo "$(YELLOW)Running tests with coverage...$(NC)"
cd frontend && npm run test:coverage
cd backend && npm run test:coverage
test-e2e: ## Run e2e tests (starts servers automatically)
@echo "$(YELLOW)Running e2e tests...$(NC)"
cd e2e && ./run-e2e.sh
test-e2e-headed: ## Run e2e tests with visible browser
@echo "$(YELLOW)Running e2e tests (headed)...$(NC)"
cd e2e && ./run-e2e.sh --headed
test-e2e-docker: ## Run e2e tests in Docker containers
@echo "$(YELLOW)Running e2e tests in Docker...$(NC)"
cd e2e && ./run-e2e.sh --docker
test-watch: ## Run tests in watch mode
@trap 'kill 0' INT; \
(cd frontend && npm run test:watch) & \
(cd backend && npm run test:watch) & \
wait
#===============================================================================
# DOCKER
#===============================================================================
docker-build: ## Build Docker images locally
@echo "$(YELLOW)Building Docker images...$(NC)"
docker-compose build
@echo "$(GREEN)Docker images built!$(NC)"
docker-run: ## Start Docker containers (docker-compose up)
@echo "$(YELLOW)Starting Docker containers...$(NC)"
docker-compose up
docker-up: docker-run ## Alias for docker-run
docker-run-detached: ## Start Docker containers in background
@echo "$(YELLOW)Starting Docker containers (detached)...$(NC)"
docker-compose up -d
@echo "$(GREEN)Containers started! Access at http://localhost:6767$(NC)"
docker-down: ## Stop and remove Docker containers
@echo "$(YELLOW)Stopping Docker containers...$(NC)"
docker-compose down
@echo "$(GREEN)Containers stopped!$(NC)"
docker-down-volumes: ## Stop containers and remove volumes
@echo "$(RED)Stopping containers and removing volumes...$(NC)"
docker-compose down -v
docker-logs: ## Show Docker container logs
docker-compose logs -f
docker-ps: ## Show running Docker containers
docker-compose ps
docker-restart: docker-down docker-run ## Restart Docker containers
docker-rebuild: docker-down docker-build docker-run ## Rebuild and restart containers
#===============================================================================
# VERSION MANAGEMENT
#===============================================================================
version: ## Show current version
@echo "$(YELLOW)Current version:$(NC) $(VERSION)"
version-bump: ## Interactive version bump
@echo "$(YELLOW)Current version:$(NC) $(VERSION)"
@echo ""
@echo "$(BLUE)Select version bump type:$(NC)"
@echo " 1) patch ($(VERSION) -> $$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}'))"
@echo " 2) minor ($(VERSION) -> $$(echo $(VERSION) | awk -F. '{print $$1"."$$2+1".0"}'))"
@echo " 3) major ($(VERSION) -> $$(echo $(VERSION) | awk -F. '{print $$1+1".0.0"}'))"
@echo " 4) custom"
@echo ""
@read -p "Enter choice [1-4]: " choice; \
case $$choice in \
1) NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}') ;; \
2) NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2+1".0"}') ;; \
3) NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1+1".0.0"}') ;; \
4) read -p "Enter new version: " NEW_VERSION ;; \
*) echo "$(RED)Invalid choice$(NC)"; exit 1 ;; \
esac; \
echo "$(YELLOW)Bumping version to $$NEW_VERSION...$(NC)"; \
echo "$$NEW_VERSION" > VERSION; \
sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json 2>/dev/null || \
sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json; \
sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json 2>/dev/null || \
sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json; \
echo "$(GREEN)Version bumped to $$NEW_VERSION$(NC)"
#===============================================================================
# RELEASE
#===============================================================================
changelog: ## Edit release notes (RELEASE.md)
@echo "$(YELLOW)Opening RELEASE.md for editing...$(NC)"
@if [ -z "$$EDITOR" ]; then \
echo "$(RED)No EDITOR set. Using vim.$(NC)"; \
vim RELEASE.md; \
else \
$$EDITOR RELEASE.md; \
fi
release: ## Full release workflow (main branch only)
@echo "$(GREEN)===========================================$(NC)"
@echo "$(GREEN) ExcaliDash Release Workflow$(NC)"
@echo "$(GREEN)===========================================$(NC)"
@echo ""
@# Branch check
@CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \
if [ "$$CURRENT_BRANCH" != "main" ]; then \
echo "$(RED)ERROR: Releases must be made from 'main' branch!$(NC)"; \
echo "$(RED)Current branch: $$CURRENT_BRANCH$(NC)"; \
echo "$(YELLOW)Please switch to main and try again.$(NC)"; \
exit 1; \
fi
@echo "$(GREEN)✓ On main branch$(NC)"
@echo ""
@# Pull latest
@echo "$(YELLOW)Pulling latest changes...$(NC)"
@git pull origin main
@echo "$(GREEN)✓ Up to date with remote$(NC)"
@echo ""
@# Show current status
@echo "$(YELLOW)Current status:$(NC)"
@git status --short || true
@echo ""
@# Run tests
@echo "$(YELLOW)Running tests...$(NC)"
@$(MAKE) test
@echo "$(GREEN)✓ All tests passed$(NC)"
@echo ""
@# Version bump - inline with clear options
@CURRENT=$$(cat VERSION); \
PATCH=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2"."$$3+1}'); \
MINOR=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2+1".0"}'); \
MAJOR=$$(echo $$CURRENT | awk -F. '{print $$1+1".0.0"}'); \
echo "$(YELLOW)Current version: $$CURRENT$(NC)"; \
echo ""; \
echo "$(BLUE)Select version bump:$(NC)"; \
echo " 1) patch → $$PATCH"; \
echo " 2) minor → $$MINOR"; \
echo " 3) major → $$MAJOR"; \
echo " 4) custom"; \
echo " 5) skip (keep $$CURRENT)"; \
echo ""; \
read -p "Enter choice [1-5]: " choice; \
case $$choice in \
1) NEW_VERSION=$$PATCH ;; \
2) NEW_VERSION=$$MINOR ;; \
3) NEW_VERSION=$$MAJOR ;; \
4) read -p "Enter new version: " NEW_VERSION ;; \
5) NEW_VERSION=$$CURRENT ;; \
*) echo "$(RED)Invalid choice, using current$(NC)"; NEW_VERSION=$$CURRENT ;; \
esac; \
if [ "$$NEW_VERSION" != "$$CURRENT" ]; then \
echo "$(YELLOW)Bumping version to $$NEW_VERSION...$(NC)"; \
echo "$$NEW_VERSION" > VERSION; \
sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json 2>/dev/null || \
sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json; \
sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json 2>/dev/null || \
sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json; \
echo "$(GREEN)✓ Version bumped to $$NEW_VERSION$(NC)"; \
else \
echo "$(YELLOW)Keeping version $$CURRENT$(NC)"; \
fi
@echo ""
@# Release notes
@echo "$(YELLOW)Release notes (RELEASE.md):$(NC)"
@read -p "Edit RELEASE.md now? [Y/n]: " edit; \
if [ "$$edit" != "n" ] && [ "$$edit" != "N" ]; then \
$(MAKE) changelog; \
fi
@echo ""
@# Show summary before commit
@NEW_VERSION=$$(cat VERSION); \
echo "$(BLUE)===========================================$(NC)"; \
echo "$(BLUE)Release Summary$(NC)"; \
echo "$(BLUE)===========================================$(NC)"; \
echo " Version: v$$NEW_VERSION"; \
echo " Branch: main"; \
echo " Tag: v$$NEW_VERSION"; \
echo ""; \
echo "$(YELLOW)Changes to be committed:$(NC)"; \
git status --short; \
echo ""
@read -p "$(YELLOW)Proceed with release? [y/N]: $(NC)" confirm; \
if [ "$$confirm" != "y" ] && [ "$$confirm" != "Y" ]; then \
echo "$(RED)Release aborted.$(NC)"; \
exit 1; \
fi
@echo ""
@# Commit changes
@NEW_VERSION=$$(cat VERSION); \
echo "$(YELLOW)Committing release...$(NC)"; \
git add -A; \
git commit -m "chore: release v$$NEW_VERSION" || echo "$(YELLOW)Nothing to commit$(NC)"
@echo "$(GREEN)✓ Changes committed$(NC)"
@echo ""
@# Push to remote
@echo "$(YELLOW)Pushing to remote...$(NC)"
@git push origin main
@echo "$(GREEN)✓ Pushed to origin/main$(NC)"
@echo ""
@# Create git tag
@NEW_VERSION=$$(cat VERSION); \
echo "$(YELLOW)Creating tag v$$NEW_VERSION...$(NC)"; \
git tag -a "v$$NEW_VERSION" -m "Release v$$NEW_VERSION"; \
git push origin "v$$NEW_VERSION"
@echo "$(GREEN)✓ Tag v$$NEW_VERSION created and pushed$(NC)"
@echo ""
@# Create GitHub release
@NEW_VERSION=$$(cat VERSION); \
echo "$(YELLOW)Creating GitHub release...$(NC)"; \
if command -v gh &> /dev/null; then \
gh release create "v$$NEW_VERSION" \
--title "ExcaliDash v$$NEW_VERSION" \
--notes-file RELEASE.md; \
echo "$(GREEN)✓ GitHub release created$(NC)"; \
else \
echo "$(RED)gh CLI not installed!$(NC)"; \
echo "$(YELLOW)Install with: brew install gh$(NC)"; \
echo "$(YELLOW)Then run: gh auth login$(NC)"; \
exit 1; \
fi
@echo ""
@# Build and push Docker images
@echo "$(YELLOW)Building and pushing Docker images...$(NC)"
@./publish-docker.sh
@echo ""
@echo "$(GREEN)===========================================$(NC)"
@echo "$(GREEN) Release Complete!$(NC)"
@echo "$(GREEN)===========================================$(NC)"
@NEW_VERSION=$$(cat VERSION); \
echo ""; \
echo "$(GREEN)✓ Version: v$$NEW_VERSION$(NC)"; \
echo "$(GREEN)✓ Git tag pushed$(NC)"; \
echo "$(GREEN)✓ GitHub release created$(NC)"; \
echo "$(GREEN)✓ Docker images published$(NC)"
pre-release: ## Pre-release workflow (pre-release branch only)
@echo "$(BLUE)===========================================$(NC)"
@echo "$(BLUE) ExcaliDash Pre-Release Workflow$(NC)"
@echo "$(BLUE)===========================================$(NC)"
@echo ""
@# Branch check
@CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \
if [ "$$CURRENT_BRANCH" != "pre-release" ]; then \
echo "$(RED)ERROR: Pre-releases must be made from 'pre-release' branch!$(NC)"; \
echo "$(RED)Current branch: $$CURRENT_BRANCH$(NC)"; \
echo "$(YELLOW)Please switch to pre-release and try again.$(NC)"; \
exit 1; \
fi
@echo "$(GREEN)✓ On pre-release branch$(NC)"
@echo ""
@# Pull latest
@echo "$(YELLOW)Pulling latest changes...$(NC)"
@git pull origin pre-release
@echo "$(GREEN)✓ Up to date with remote$(NC)"
@echo ""
@# Show current status
@echo "$(YELLOW)Current status:$(NC)"
@git status --short || true
@echo ""
@# Run tests
@echo "$(YELLOW)Running tests...$(NC)"
@$(MAKE) test
@echo "$(GREEN)✓ All tests passed$(NC)"
@echo ""
@# Version bump - inline with clear options
@CURRENT=$$(cat VERSION); \
PATCH=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2"."$$3+1}'); \
MINOR=$$(echo $$CURRENT | awk -F. '{print $$1"."$$2+1".0"}'); \
MAJOR=$$(echo $$CURRENT | awk -F. '{print $$1+1".0.0"}'); \
echo "$(YELLOW)Current version: $$CURRENT$(NC)"; \
echo ""; \
echo "$(BLUE)Select version bump:$(NC)"; \
echo " 1) patch → $$PATCH-dev"; \
echo " 2) minor → $$MINOR-dev"; \
echo " 3) major → $$MAJOR-dev"; \
echo " 4) custom"; \
echo " 5) skip (keep $$CURRENT-dev)"; \
echo ""; \
read -p "Enter choice [1-5]: " choice; \
case $$choice in \
1) NEW_VERSION=$$PATCH ;; \
2) NEW_VERSION=$$MINOR ;; \
3) NEW_VERSION=$$MAJOR ;; \
4) read -p "Enter new version (without -dev suffix): " NEW_VERSION ;; \
5) NEW_VERSION=$$CURRENT ;; \
*) echo "$(RED)Invalid choice, using current$(NC)"; NEW_VERSION=$$CURRENT ;; \
esac; \
if [ "$$NEW_VERSION" != "$$CURRENT" ]; then \
echo "$(YELLOW)Bumping version to $$NEW_VERSION...$(NC)"; \
echo "$$NEW_VERSION" > VERSION; \
sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json 2>/dev/null || \
sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" frontend/package.json; \
sed -i '' "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json 2>/dev/null || \
sed -i "s/\"version\": \".*\"/\"version\": \"$$NEW_VERSION\"/" backend/package.json; \
echo "$(GREEN)✓ Version bumped to $$NEW_VERSION$(NC)"; \
else \
echo "$(YELLOW)Keeping version $$CURRENT$(NC)"; \
fi
@echo ""
@# Release notes
@echo "$(YELLOW)Release notes (RELEASE.md):$(NC)"
@read -p "Edit RELEASE.md now? [Y/n]: " edit; \
if [ "$$edit" != "n" ] && [ "$$edit" != "N" ]; then \
$(MAKE) changelog; \
fi
@echo ""
@# Show summary before commit
@NEW_VERSION=$$(cat VERSION); \
echo "$(BLUE)===========================================$(NC)"; \
echo "$(BLUE)Pre-Release Summary$(NC)"; \
echo "$(BLUE)===========================================$(NC)"; \
echo " Version: v$$NEW_VERSION-dev"; \
echo " Branch: pre-release"; \
echo " Tag: v$$NEW_VERSION-dev (pre-release)"; \
echo ""; \
echo "$(YELLOW)Changes to be committed:$(NC)"; \
git status --short; \
echo ""
@read -p "$(YELLOW)Proceed with pre-release? [y/N]: $(NC)" confirm; \
if [ "$$confirm" != "y" ] && [ "$$confirm" != "Y" ]; then \
echo "$(RED)Pre-release aborted.$(NC)"; \
exit 1; \
fi
@echo ""
@# Commit changes
@NEW_VERSION=$$(cat VERSION); \
echo "$(YELLOW)Committing pre-release...$(NC)"; \
git add -A; \
git commit -m "chore: pre-release v$$NEW_VERSION-dev" || echo "$(YELLOW)Nothing to commit$(NC)"
@echo "$(GREEN)✓ Changes committed$(NC)"
@echo ""
@# Push to remote
@echo "$(YELLOW)Pushing to remote...$(NC)"
@git push origin pre-release
@echo "$(GREEN)✓ Pushed to origin/pre-release$(NC)"
@echo ""
@# Create git tag
@NEW_VERSION=$$(cat VERSION); \
PRE_TAG="v$$NEW_VERSION-dev"; \
echo "$(YELLOW)Creating tag $$PRE_TAG...$(NC)"; \
git tag -a "$$PRE_TAG" -m "Pre-release $$PRE_TAG"; \
git push origin "$$PRE_TAG"
@echo "$(GREEN)✓ Tag $$PRE_TAG created and pushed$(NC)"
@echo ""
@# Create GitHub pre-release
@NEW_VERSION=$$(cat VERSION); \
PRE_TAG="v$$NEW_VERSION-dev"; \
echo "$(YELLOW)Creating GitHub pre-release...$(NC)"; \
if command -v gh &> /dev/null; then \
gh release create "$$PRE_TAG" \
--title "ExcaliDash $$PRE_TAG (Pre-release)" \
--notes-file RELEASE.md \
--prerelease; \
echo "$(GREEN)✓ GitHub pre-release created$(NC)"; \
else \
echo "$(RED)gh CLI not installed!$(NC)"; \
echo "$(YELLOW)Install with: brew install gh$(NC)"; \
echo "$(YELLOW)Then run: gh auth login$(NC)"; \
exit 1; \
fi
@echo ""
@# Build and push Docker images
@echo "$(YELLOW)Building and pushing Docker images...$(NC)"
@./publish-docker-prerelease.sh
@echo ""
@echo "$(BLUE)===========================================$(NC)"
@echo "$(GREEN) Pre-Release Complete!$(NC)"
@echo "$(BLUE)===========================================$(NC)"
@NEW_VERSION=$$(cat VERSION); \
echo ""; \
echo "$(GREEN)✓ Version: v$$NEW_VERSION-dev$(NC)"; \
echo "$(GREEN)✓ Git tag pushed$(NC)"; \
echo "$(GREEN)✓ GitHub pre-release created$(NC)"; \
echo "$(GREEN)✓ Docker images published$(NC)"
release-docker: ## Build and push release Docker images
./publish-docker.sh
pre-release-docker: ## Build and push pre-release Docker images
./publish-docker-prerelease.sh
#===============================================================================
# DATABASE
#===============================================================================
db-migrate: ## Run database migrations
@echo "$(YELLOW)Running database migrations...$(NC)"
cd backend && npx prisma migrate dev
@echo "$(GREEN)Migrations complete!$(NC)"
db-generate: ## Generate Prisma client
@echo "$(YELLOW)Generating Prisma client...$(NC)"
cd backend && npx prisma generate
@echo "$(GREEN)Client generated!$(NC)"
db-reset: ## Reset database (WARNING: destroys all data)
@echo "$(RED)WARNING: This will destroy all data!$(NC)"
@read -p "Are you sure? [y/N]: " confirm; \
if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \
cd backend && npx prisma migrate reset --force; \
echo "$(GREEN)Database reset complete!$(NC)"; \
else \
echo "$(YELLOW)Cancelled$(NC)"; \
fi
db-studio: ## Open Prisma Studio (database GUI)
@echo "$(YELLOW)Opening Prisma Studio...$(NC)"
cd backend && npx prisma studio
#===============================================================================
# QUICK ALIASES
#===============================================================================
up: docker-run ## Alias: Start Docker containers
down: docker-down ## Alias: Stop Docker containers
logs: docker-logs ## Alias: Show Docker logs
t: test ## Alias: Run unit tests
ta: test-all ## Alias: Run all tests