Dockerfile changes: - Replace pip with uv package manager for faster builds - Remove build-essential and git dependencies - Use pyproject.toml instead of requirements.txt - Add uv installation and virtual environment setup - Modernize environment variables and caching - Fix hadolint warnings (add --no-install-recommends and pipefail) Makefile changes: - Remove docker-build-legacy target (legacy builder) - Remove docker-dev-host and docker-prod-host targets (networking workarounds) - Remove docker-reset target (networking fixes) - Remove docker-clean-legacy target (duplicate functionality) - Clean up help output and reduce maintenance overhead This modernizes the build system and removes workarounds for older Docker versions and networking issues.
436 lines
15 KiB
Makefile
436 lines
15 KiB
Makefile
# UnitForge Makefile
|
|
# Development workflow automation using uv package manager
|
|
|
|
.PHONY: help install install-dev clean test test-cov lint format type-check
|
|
.PHONY: server cli demo docker-build docker-dev docker-prod docker-test
|
|
.PHONY: docker-push docker-pull docker-tag docker-login registry-build registry-push
|
|
.PHONY: pre-commit setup-dev deps-update deps-check security-check
|
|
.PHONY: build package publish docs release validate-config
|
|
|
|
# Default target
|
|
.DEFAULT_GOAL := help
|
|
|
|
# Colors for output (load from centralized utility)
|
|
include scripts/colors.mk
|
|
|
|
# Configuration
|
|
PYTHON_VERSION := 3.8
|
|
PROJECT_NAME := unitforge
|
|
VENV_PATH := .venv
|
|
UV_INSTALLED := $(shell command -v uv 2> /dev/null)
|
|
|
|
help: ## Show this help message
|
|
$(call header,UnitForge Development Makefile)
|
|
@echo ""
|
|
@echo -e "$(GREEN)Available targets:$(NC)"
|
|
@awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z_-]+:.*##/ { printf " $(YELLOW)%-20s$(NC) %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
|
|
@echo ""
|
|
@echo -e "$(GREEN)Quick start:$(NC)"
|
|
@echo " make setup-dev # Set up development environment"
|
|
@echo " make server # Start development server"
|
|
@echo " make test # Run tests"
|
|
@echo ""
|
|
|
|
check-uv: ## Check if uv is installed
|
|
ifndef UV_INSTALLED
|
|
$(call error,uv is not installed)
|
|
@echo "Install it with: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
|
@exit 1
|
|
else
|
|
$(call success,uv is available)
|
|
endif
|
|
|
|
setup-dev: check-uv ## Set up development environment with uv
|
|
$(call info,Setting up development environment...)
|
|
@if [ -d "$(VENV_PATH)" ]; then \
|
|
echo -e "$(YELLOW)$(WARNING_SYMBOL)$(NC) Removing existing virtual environment..."; \
|
|
rm -rf $(VENV_PATH); \
|
|
fi
|
|
uv venv --python python3
|
|
$(call success,Virtual environment created)
|
|
@$(MAKE) install-dev
|
|
@if [ ! -f ".env" ] && [ -f ".env.example" ]; then \
|
|
echo -e "$(BLUE)$(INFO_SYMBOL)$(NC) Creating .env file from template..."; \
|
|
cp .env.example .env; \
|
|
echo -e "$(GREEN)$(SUCCESS_SYMBOL)$(NC) .env file created. Please review and customize the settings."; \
|
|
fi
|
|
@if [ -f ".pre-commit-config.yaml" ]; then \
|
|
echo -e "$(BLUE)$(INFO_SYMBOL)$(NC) Installing pre-commit hooks..."; \
|
|
. $(VENV_PATH)/bin/activate && pre-commit install; \
|
|
echo -e "$(GREEN)$(SUCCESS_SYMBOL)$(NC) Pre-commit hooks installed"; \
|
|
fi
|
|
$(call success,Development environment ready!)
|
|
@echo ""
|
|
@echo -e "$(YELLOW)Next steps:$(NC)"
|
|
@echo " source $(VENV_PATH)/bin/activate # Activate environment"
|
|
@echo " make server # Start development server"
|
|
@echo " make test # Run tests"
|
|
|
|
install: check-uv ## Install production dependencies
|
|
$(call info,Installing production dependencies...)
|
|
uv pip install -e .
|
|
$(call success,Production dependencies installed)
|
|
|
|
install-dev: check-uv ## Install development dependencies
|
|
$(call info,Installing development dependencies...)
|
|
uv pip install -e ".[dev,web]"
|
|
$(call success,Development dependencies installed)
|
|
|
|
deps-update: check-uv ## Update all dependencies
|
|
$(call info,Updating dependencies...)
|
|
uv pip install --upgrade -e ".[dev,web]"
|
|
$(call success,Dependencies updated)
|
|
|
|
deps-check: ## Check for dependency vulnerabilities
|
|
$(call info,Checking dependencies for vulnerabilities...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run pip-audit --desc || true; \
|
|
else \
|
|
echo -e "$(YELLOW)$(WARNING_SYMBOL)$(NC) Virtual environment not found. Run 'make setup-dev' first."; \
|
|
fi
|
|
|
|
clean: ## Clean up cache files and build artifacts
|
|
$(call info,Cleaning up...)
|
|
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
|
find . -type f -name "*.pyc" -delete 2>/dev/null || true
|
|
find . -type f -name "*.pyo" -delete 2>/dev/null || true
|
|
find . -type f -name "*.orig" -delete 2>/dev/null || true
|
|
rm -rf .pytest_cache/ htmlcov/ .coverage .mypy_cache/ dist/ build/
|
|
rm -rf *.egg-info/
|
|
rm -f *.service *.timer *.socket *.mount *.target *.path
|
|
$(call success,Cleanup complete)
|
|
|
|
test: ## Run tests
|
|
$(call info,Running tests...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run pytest tests/ -v; \
|
|
else \
|
|
uv run pytest tests/ -v; \
|
|
fi
|
|
|
|
test-cov: ## Run tests with coverage
|
|
$(call info,Running tests with coverage...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run pytest tests/ --cov=backend --cov-report=html --cov-report=term; \
|
|
else \
|
|
uv run pytest tests/ --cov=backend --cov-report=html --cov-report=term; \
|
|
fi
|
|
$(call success,Coverage report generated in htmlcov/)
|
|
|
|
test-watch: ## Run tests in watch mode
|
|
$(call info,Running tests in watch mode...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run pytest tests/ -v --watch; \
|
|
else \
|
|
uv run pytest tests/ -v --watch; \
|
|
fi
|
|
|
|
lint: ## Run linters
|
|
$(call info,Running linters...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && \
|
|
uv run black --check backend/ tests/ && \
|
|
uv run isort --check-only backend/ tests/ && \
|
|
uv run flake8 backend/ tests/ --max-line-length=88 --extend-ignore=E203,W503 --exclude=.venv,__pycache__,build,dist; \
|
|
else \
|
|
uv run black --check backend/ tests/ && \
|
|
uv run isort --check-only backend/ tests/ && \
|
|
uv run flake8 backend/ tests/ --max-line-length=88 --extend-ignore=E203,W503 --exclude=.venv,__pycache__,build,dist; \
|
|
fi
|
|
$(call success,Linting passed)
|
|
|
|
format: ## Format code
|
|
$(call info,Formatting code...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && \
|
|
uv run black backend/ tests/ && \
|
|
uv run isort backend/ tests/; \
|
|
else \
|
|
uv run black backend/ tests/ && \
|
|
uv run isort backend/ tests/; \
|
|
fi
|
|
$(call success,Code formatted)
|
|
|
|
type-check: ## Run type checking
|
|
$(call info,Running type checks...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && PYTHONPATH=. uv run mypy backend/app/ backend/cli/ --ignore-missing-imports; \
|
|
else \
|
|
PYTHONPATH=. uv run mypy backend/app/ backend/cli/ --ignore-missing-imports; \
|
|
fi
|
|
$(call success,Type checking passed)
|
|
|
|
security-check: ## Run security checks
|
|
$(call info,Running security checks...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run bandit -r backend/ -x tests/; \
|
|
else \
|
|
uv run bandit -r backend/ -x tests/; \
|
|
fi
|
|
$(call success,Security checks passed)
|
|
|
|
pre-commit: ## Run pre-commit hooks on all files
|
|
$(call info,Running pre-commit hooks...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run pre-commit run --all-files; \
|
|
else \
|
|
uv run pre-commit run --all-files; \
|
|
fi
|
|
|
|
server: ## Start development server
|
|
$(call info,Starting development server...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && ./start-server.sh --log-level debug; \
|
|
else \
|
|
./start-server.sh --log-level debug; \
|
|
fi
|
|
|
|
server-prod: ## Start production server
|
|
$(call info,Starting production server...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && ./start-server.sh --no-reload --log-level info; \
|
|
else \
|
|
./start-server.sh --no-reload --log-level info; \
|
|
fi
|
|
|
|
cli: ## Run CLI with help
|
|
$(call header,UnitForge CLI)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && ./unitforge-cli --help; \
|
|
else \
|
|
./unitforge-cli --help; \
|
|
fi
|
|
|
|
demo: ## Run interactive demo
|
|
$(call header,UnitForge Demo)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && ./demo.sh; \
|
|
else \
|
|
./demo.sh; \
|
|
fi
|
|
|
|
validate-config: ## Validate environment configuration
|
|
$(call header,Validating Configuration)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && python scripts/validate_config.py; \
|
|
else \
|
|
python3 scripts/validate_config.py; \
|
|
fi
|
|
|
|
# Docker targets
|
|
docker-build: ## Build Docker images
|
|
$(call info,Building Docker images...)
|
|
docker-compose build
|
|
$(call success,Docker images built)
|
|
|
|
# Container Registry targets
|
|
docker-tag: ## Tag Docker images with registry URL
|
|
$(call info,Tagging Docker images for registry...)
|
|
@if [ -f ".env" ]; then \
|
|
. ./.env && \
|
|
docker tag unitforge:latest $${CONTAINER_REGISTRY_URL}:$${CONTAINER_TAG} && \
|
|
docker tag unitforge:latest $${CONTAINER_REGISTRY_URL}:latest; \
|
|
else \
|
|
echo -e "$(YELLOW)$(WARNING_SYMBOL)$(NC) .env file not found. Using default values."; \
|
|
docker tag unitforge:latest http://gitea-http.taildb3494.ts.net/will/unitforge:latest; \
|
|
fi
|
|
$(call success,Docker images tagged for registry)
|
|
|
|
docker-push: docker-tag ## Push Docker images to registry
|
|
$(call info,Pushing Docker images to registry...)
|
|
@if [ -f ".env" ]; then \
|
|
. ./.env && \
|
|
docker push $${CONTAINER_REGISTRY_URL}:$${CONTAINER_TAG} && \
|
|
docker push $${CONTAINER_REGISTRY_URL}:latest; \
|
|
else \
|
|
echo -e "$(YELLOW)$(WARNING_SYMBOL)$(NC) .env file not found. Using default values."; \
|
|
docker push http://gitea-http.taildb3494.ts.net/will/unitforge:latest; \
|
|
fi
|
|
$(call success,Docker images pushed to registry)
|
|
|
|
docker-pull: ## Pull Docker images from registry
|
|
$(call info,Pulling Docker images from registry...)
|
|
@if [ -f ".env" ]; then \
|
|
. ./.env && \
|
|
docker pull $${CONTAINER_REGISTRY_URL}:$${CONTAINER_TAG}; \
|
|
else \
|
|
echo -e "$(YELLOW)$(WARNING_SYMBOL)$(NC) .env file not found. Using default values."; \
|
|
docker pull http://gitea-http.taildb3494.ts.net/will/unitforge:latest; \
|
|
fi
|
|
$(call success,Docker images pulled from registry)
|
|
|
|
registry-build: docker-build docker-tag ## Build and tag images for registry
|
|
$(call success,Images built and tagged for registry)
|
|
|
|
registry-push: registry-build docker-push ## Build, tag, and push images to registry
|
|
$(call success,Images built, tagged, and pushed to registry)
|
|
|
|
docker-login: ## Login to container registry (interactive)
|
|
$(call info,Logging into container registry...)
|
|
@if [ -f ".env" ]; then \
|
|
. ./.env && \
|
|
echo "Logging into registry at: $${CONTAINER_REGISTRY_URL}"; \
|
|
docker login $${CONTAINER_REGISTRY_URL}; \
|
|
else \
|
|
echo -e "$(YELLOW)$(WARNING_SYMBOL)$(NC) .env file not found. Using default values."; \
|
|
docker login http://gitea-http.taildb3494.ts.net; \
|
|
fi
|
|
$(call success,Logged into container registry)
|
|
|
|
|
|
|
|
docker-dev: ## Start development environment with Docker
|
|
$(call info,Starting development environment with Docker...)
|
|
docker-compose up unitforge-dev
|
|
|
|
|
|
|
|
docker-prod: ## Start production environment with Docker
|
|
$(call info,Starting production environment with Docker...)
|
|
docker-compose up unitforge-prod
|
|
|
|
|
|
|
|
docker-cli: ## Run CLI in Docker
|
|
$(call info,Running CLI in Docker...)
|
|
docker-compose --profile cli run --rm unitforge-cli --help
|
|
|
|
docker-logs: ## Show logs from all Docker services
|
|
$(call info,Showing Docker logs...)
|
|
docker-compose logs -f
|
|
|
|
docker-shell: ## Open shell in development container
|
|
$(call info,Opening shell in development container...)
|
|
docker-compose exec unitforge-dev /bin/bash
|
|
|
|
docker-clean: ## Clean up Docker containers and volumes
|
|
$(call info,Cleaning up Docker resources...)
|
|
docker-compose down --remove-orphans --volumes
|
|
docker system prune -f
|
|
$(call success,Docker cleanup completed)
|
|
|
|
|
|
|
|
docker-clean-registry: ## Remove locally cached registry images
|
|
$(call info,Cleaning registry images from local cache...)
|
|
@if [ -f ".env" ]; then \
|
|
. ./.env && \
|
|
docker rmi $${CONTAINER_REGISTRY_URL}:$${CONTAINER_TAG} 2>/dev/null || true && \
|
|
docker rmi $${CONTAINER_REGISTRY_URL}:latest 2>/dev/null || true; \
|
|
else \
|
|
docker rmi http://gitea-http.taildb3494.ts.net/will/unitforge:latest 2>/dev/null || true; \
|
|
fi
|
|
$(call success,Registry images cleaned from local cache)
|
|
|
|
# Package and release targets
|
|
build: clean ## Build package
|
|
$(call info,Building package...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run python -m build; \
|
|
else \
|
|
uv run python -m build; \
|
|
fi
|
|
$(call success,Package built in dist/)
|
|
|
|
package: build ## Create distribution packages
|
|
$(call info,Creating distribution packages...)
|
|
@ls -la dist/
|
|
$(call success,Distribution packages ready)
|
|
|
|
publish-test: package ## Publish to TestPyPI
|
|
$(call info,Publishing to TestPyPI...)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run twine upload --repository testpypi dist/*; \
|
|
else \
|
|
uv run twine upload --repository testpypi dist/*; \
|
|
fi
|
|
|
|
publish: package ## Publish to PyPI
|
|
$(call info,Publishing to PyPI...)
|
|
$(call warning,This will publish to the real PyPI. Are you sure? [y/N])
|
|
@read -r confirm && [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ] || (echo "Aborted" && exit 1)
|
|
@if [ -f "$(VENV_PATH)/bin/activate" ]; then \
|
|
. $(VENV_PATH)/bin/activate && uv run twine upload dist/*; \
|
|
else \
|
|
uv run twine upload dist/*; \
|
|
fi
|
|
|
|
docs: ## Generate documentation
|
|
$(call info,Generating documentation...)
|
|
$(call warning,Documentation generation not yet implemented)
|
|
|
|
# Comprehensive quality check
|
|
check-all: lint security-check test ## Run all quality checks
|
|
$(call success,All quality checks passed)
|
|
|
|
# Release preparation
|
|
release-check: check-all build ## Prepare for release
|
|
$(call info,Performing release checks...)
|
|
$(call success,Release checks completed)
|
|
@echo ""
|
|
@echo -e "$(YELLOW)Release checklist:$(NC)"
|
|
@echo " □ Update version in pyproject.toml"
|
|
@echo " □ Update CHANGELOG.md"
|
|
@echo " □ Commit changes"
|
|
@echo " □ Create git tag"
|
|
@echo " □ Run 'make publish'"
|
|
|
|
# Quick development cycle
|
|
dev: format lint test ## Quick development cycle (format, lint, test)
|
|
$(call success,Development cycle complete)
|
|
|
|
# Initialize new development environment
|
|
init: setup-dev ## Initialize new development environment
|
|
$(call success,Development environment initialized)
|
|
@echo ""
|
|
@echo -e "$(BLUE)Try these commands:$(NC)"
|
|
@echo " make server # Start web server"
|
|
@echo " make cli # Try CLI tool"
|
|
@echo " make demo # Interactive demo"
|
|
@echo " make test # Run tests"
|
|
|
|
# Show project status
|
|
status: ## Show project status
|
|
$(call header,UnitForge Project Status)
|
|
@echo ""
|
|
@echo -e "$(YELLOW)Virtual Environment:$(NC)"
|
|
@if [ -d "$(VENV_PATH)" ]; then \
|
|
echo " ✓ Virtual environment exists at $(VENV_PATH)"; \
|
|
if [ -n "$$VIRTUAL_ENV" ]; then \
|
|
echo " ✓ Virtual environment is activated"; \
|
|
else \
|
|
echo " ⚠ Virtual environment not activated"; \
|
|
fi \
|
|
else \
|
|
echo " ✗ Virtual environment not found"; \
|
|
fi
|
|
@echo ""
|
|
@echo -e "$(YELLOW)Dependencies:$(NC)"
|
|
@if command -v uv >/dev/null 2>&1; then \
|
|
echo " ✓ uv package manager available"; \
|
|
else \
|
|
echo " ✗ uv package manager not found - REQUIRED"; \
|
|
fi
|
|
@if command -v docker >/dev/null 2>&1; then \
|
|
echo " ✓ Docker available"; \
|
|
else \
|
|
echo " ✗ Docker not found"; \
|
|
fi
|
|
@echo ""
|
|
@echo -e "$(YELLOW)Container Registry:$(NC)"
|
|
@if [ -f ".env" ]; then \
|
|
. ./.env && \
|
|
echo " ✓ Registry URL: $${CONTAINER_REGISTRY_URL}"; \
|
|
echo " ✓ Container Tag: $${CONTAINER_TAG}"; \
|
|
else \
|
|
echo " ⚠ .env file not found - using defaults"; \
|
|
fi
|
|
@echo ""
|
|
@echo -e "$(YELLOW)Project Files:$(NC)"
|
|
@if [ -f "pyproject.toml" ]; then echo " ✓ pyproject.toml"; else echo " ✗ pyproject.toml"; fi
|
|
@if [ -f "unitforge-cli" ]; then echo " ✓ CLI tool"; else echo " ✗ CLI tool"; fi
|
|
@if [ -f "start-server.sh" ]; then echo " ✓ Server script"; else echo " ✗ Server script"; fi
|
|
@if [ -d "backend" ]; then echo " ✓ Backend code"; else echo " ✗ Backend code"; fi
|
|
@if [ -d "frontend" ]; then echo " ✓ Frontend code"; else echo " ✗ Frontend code"; fi
|
|
@if [ -d "tests" ]; then echo " ✓ Tests"; else echo " ✗ Tests"; fi
|