# ============================================================================= # Makefile — Common development targets # ============================================================================= # Reads .ci/config.env for settings. All targets are fail-safe: # they skip gracefully if tools or files are not present. # ============================================================================= SHELL := /bin/bash .DEFAULT_GOAL := help # Load config if it exists -include .ci/config.env export # Defaults if config is missing ENABLE_DOCKER ?= false DOCKER_PUSH ?= false REGISTRY_HOST ?= git.hiddenden.cafe IMAGE_OWNER ?= auto IMAGE_NAME ?= auto # Derive image owner/name dynamically if set to "auto" _OWNER := $(if $(filter auto,$(IMAGE_OWNER)),$(shell basename $$(dirname $$(pwd))),$(IMAGE_OWNER)) _NAME := $(if $(filter auto,$(IMAGE_NAME)),$(shell basename $$(pwd)),$(IMAGE_NAME)) _IMAGE := $(REGISTRY_HOST)/$(_OWNER)/$(_NAME) # --------------------------------------------------------------------------- .PHONY: help help: ## Show this help message @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}' # --------------------------------------------------------------------------- # Code quality # --------------------------------------------------------------------------- .PHONY: fmt fmt: ## Format code (Python: black/ruff; JS: prettier) @if command -v ruff >/dev/null 2>&1; then \ echo ">>> ruff format ."; ruff format .; \ elif command -v black >/dev/null 2>&1; then \ echo ">>> black ."; black .; \ else \ echo "SKIP: no Python formatter found (install ruff or black)"; \ fi @if [ -f package.json ] && command -v npx >/dev/null 2>&1; then \ if npx --no-install prettier --version >/dev/null 2>&1; then \ echo ">>> npx prettier --write ."; npx prettier --write .; \ else \ echo "SKIP: prettier not installed"; \ fi \ fi .PHONY: lint lint: ## Run linters (Python: ruff/flake8; JS: eslint) @if command -v ruff >/dev/null 2>&1; then \ echo ">>> ruff check ."; ruff check .; \ elif command -v flake8 >/dev/null 2>&1; then \ echo ">>> flake8 ."; flake8 .; \ else \ echo "SKIP: no Python linter found (install ruff or flake8)"; \ fi @if [ -f package.json ] && command -v npx >/dev/null 2>&1; then \ if npx --no-install eslint --version >/dev/null 2>&1; then \ echo ">>> npx eslint ."; npx eslint .; \ else \ echo "SKIP: eslint not installed"; \ fi \ fi .PHONY: test test: ## Run tests (Python: pytest; JS: npm test) @if [ -d tests ] || [ -f pytest.ini ] || [ -f pyproject.toml ]; then \ if command -v pytest >/dev/null 2>&1; then \ echo ">>> pytest"; pytest; \ else \ echo "SKIP: pytest not installed"; \ fi \ else \ echo "SKIP: no Python tests detected"; \ fi @if [ -f package.json ]; then \ if grep -q '"test"' package.json 2>/dev/null; then \ echo ">>> npm test"; npm test; \ else \ echo "SKIP: no 'test' script in package.json"; \ fi \ fi # --------------------------------------------------------------------------- # Build # --------------------------------------------------------------------------- .PHONY: build build: ## Build the project (JS: npm run build; Python: pip install -e .) @if [ -f package.json ] && grep -q '"build"' package.json 2>/dev/null; then \ echo ">>> npm run build"; npm run build; \ elif [ -f setup.py ] || [ -f pyproject.toml ]; then \ echo ">>> pip install -e ."; pip install -e .; \ else \ echo "SKIP: no build step detected"; \ fi # --------------------------------------------------------------------------- # Docker # --------------------------------------------------------------------------- .PHONY: docker-build docker-build: ## Build Docker image @if [ "$(ENABLE_DOCKER)" != "true" ]; then \ echo "SKIP: ENABLE_DOCKER is not true"; exit 0; \ fi @if [ -f Dockerfile ]; then \ echo ">>> docker build -t $(_IMAGE):local ."; \ docker build -t "$(_IMAGE):local" .; \ elif [ -f docker-compose.yml ]; then \ echo ">>> docker compose build"; \ docker compose build; \ else \ echo "SKIP: no Dockerfile or docker-compose.yml found"; \ fi .PHONY: docker-push docker-push: ## Push Docker image (requires DOCKER_PUSH=true) @if [ "$(DOCKER_PUSH)" != "true" ]; then \ echo "ABORT: DOCKER_PUSH is not true in .ci/config.env"; \ echo "Set DOCKER_PUSH=true to enable pushing."; \ exit 1; \ fi @echo ">>> docker push $(_IMAGE):local" docker push "$(_IMAGE):local"