zinit 0.3.9

Process supervisor with dependency management
Documentation
# ============================================================================
# Zinit Makefile - Process supervisor with dependency management
# ============================================================================

VERSION := $(shell grep '^version' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
BINARY_NAMES := zinit zinit-server zinit-pid1
INSTALL_DIR := $(HOME)/hero/bin
OS := $(shell uname -s)

# Default socket location (platform-specific)
ifeq ($(OS),Linux)
    DEFAULT_SOCKET := /run/zinit.sock
    DEFAULT_CONFIG := /etc/zinit/services
else
    DEFAULT_SOCKET := $(HOME)/hero/var/zinit.sock
    DEFAULT_CONFIG := $(HOME)/hero/cfg/zinit
endif

SOCKET := $(or $(ZINIT_SOCKET),$(DEFAULT_SOCKET))
CONFIG_DIR := $(or $(ZINIT_CONFIG_DIR),$(DEFAULT_CONFIG))
LOG_LEVEL := $(or $(ZINIT_LOG_LEVEL),info)

# ============================================================================
# Phony Targets
# ============================================================================

.PHONY: help version status build run dev install installdev clean all \
        check fmt fmt-check lint test test-all docs \
        build-publish deps update-deps stop install-download publish-dry-run build-package

# ============================================================================
# Help & Information
# ============================================================================

help: ## Display all available targets with descriptions
	@echo "zinit v$(VERSION)"
	@echo ""
	@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "  %-20s %s\n", $$1, $$2}'

version: ## Show current version
	@echo "zinit v$(VERSION)"

status: ## Show configuration and installation status
	@echo "=== Zinit Configuration ==="
	@echo "Version:       $(VERSION)"
	@echo "Platform:      $(OS)"
	@echo "Install dir:   $(INSTALL_DIR)"
	@echo "Socket:        $(SOCKET)"
	@echo "Config dir:    $(CONFIG_DIR)"
	@echo "Log level:     $(LOG_LEVEL)"
	@echo ""
	@echo "Installed binaries:"
	@if [ -f "$(INSTALL_DIR)/zinit" ]; then echo "  ✓ zinit"; else echo "  ✗ zinit"; fi
	@if [ -f "$(INSTALL_DIR)/zinit-server" ]; then echo "  ✓ zinit-server"; else echo "  ✗ zinit-server"; fi
	@if [ -f "$(INSTALL_DIR)/zinit-pid1" ]; then echo "  ✓ zinit-pid1"; else echo "  ✗ zinit-pid1"; fi
	@echo ""
	@echo "Check PATH configuration:"
	@if echo "$$PATH" | grep -q "$(INSTALL_DIR)"; then \
		echo "$(INSTALL_DIR) is in PATH"; \
	else \
		echo "$(INSTALL_DIR) is NOT in PATH"; \
		echo "    Add to ~/.bashrc or ~/.zshrc:"; \
		echo "    export PATH=\"\$$HOME/hero/bin:\$$PATH\""; \
	fi

# ============================================================================
# Development
# ============================================================================

build: ## Build release binaries (zinit, zinit-server, zinit-pid1)
	@echo "Building zinit v$(VERSION)..."
	@cargo update
	@cargo build --release --features full
	@echo "✓ Build complete"

dev: ## Run development server with debug logging
	@echo "Starting zinit-server in development mode..."
	@RUST_LOG=debug ZINIT_SOCKET="$(SOCKET)" ZINIT_CONFIG_DIR="$(CONFIG_DIR)" \
		ZINIT_LOG_LEVEL=debug cargo run --release --bin zinit-server --features full

run: install stop ## Build, install, and run zinit-server
	@echo "=== Starting zinit-server ==="
	@echo "  Socket:  $(SOCKET)"
	@echo "  Config:  $(CONFIG_DIR)"
	@echo "  Log level: $(LOG_LEVEL)"
	@mkdir -p "$(INSTALL_DIR)" "$$(dirname "$(SOCKET)")" "$(CONFIG_DIR)"
	@rm -f "$(SOCKET)" 2>/dev/null || true
	@ZINIT_SOCKET="$(SOCKET)" \
		ZINIT_CONFIG_DIR="$(CONFIG_DIR)" \
		ZINIT_LOG_LEVEL="$(LOG_LEVEL)" \
		"$(INSTALL_DIR)/zinit-server" > /tmp/zinit-server.log 2>&1 &
	@echo "✓ Server started (PID: $$!)"
	@echo "  To stop: make stop"
	@echo "  To view logs: tail -f /tmp/zinit-server.log"

stop: ## Stop any running zinit-server processes
	@echo -n "Stopping zinit-server... "
	@pkill -f "zinit-server" 2>/dev/null || true
	@sleep 0.5
	@pkill -9 -f "zinit-server" 2>/dev/null || true
	@echo "done"

check: ## Fast code check without building
	@cargo check --all-features

fmt: ## Format code using rustfmt
	@cargo fmt

fmt-check: ## Check code formatting without modifying
	@cargo fmt --check

lint: ## Run clippy linter on all targets
	@cargo clippy --all-targets --all-features

# ============================================================================
# Testing
# ============================================================================

test: ## Run unit tests
	@cargo test --lib --features full -- --test-threads=1

test-all: ## Run all tests (unit + integration)
	@cargo test --features full -- --test-threads=1

example: ## Run the Rust client example (requires running zinit-server)
	@cargo run --example main --features client

# ============================================================================
# Documentation
# ============================================================================

docs: ## Generate documentation
	@echo "Generating documentation..."
	@cargo doc --no-deps --features full --release
	@echo "✓ Documentation generated at target/doc/zinit/index.html"

# ============================================================================
# Deployment
# ============================================================================

install: build ## Build and install binaries to $(INSTALL_DIR)
	@echo "Installing binaries to $(INSTALL_DIR)..."
	@mkdir -p "$(INSTALL_DIR)"
	@for binary in zinit zinit-server; do \
		if [ -f "target/release/$$binary" ]; then \
			rm -f "$(INSTALL_DIR)/$$binary"; \
			cp "target/release/$$binary" "$(INSTALL_DIR)/"; \
			chmod +x "$(INSTALL_DIR)/$$binary"; \
			echo "$$binary"; \
		fi \
	done
	@if [ "$(OS)" = "Linux" ] && [ -f "target/release/zinit-pid1" ]; then \
		rm -f "$(INSTALL_DIR)/zinit-pid1"; \
		cp "target/release/zinit-pid1" "$(INSTALL_DIR)/"; \
		chmod +x "$(INSTALL_DIR)/zinit-pid1"; \
		echo "  ✓ zinit-pid1"; \
	fi
	@echo ""
	@if ! echo "$$PATH" | grep -q "$(INSTALL_DIR)"; then \
		echo "$(INSTALL_DIR) is not in your PATH"; \
		echo "Add this to ~/.bashrc or ~/.zshrc:"; \
		echo "  export PATH=\"\$$HOME/hero/bin:\$$PATH\""; \
	else \
		echo "✓ Binaries ready in PATH"; \
	fi

installdev: ## Build dev mode and install binaries (fastest compile)
	@echo "Building and installing dev binaries to $(INSTALL_DIR)..."
	@cargo build --features full
	@mkdir -p "$(INSTALL_DIR)"
	@for binary in zinit zinit-server; do \
		if [ -f "target/debug/$$binary" ]; then \
			rm -f "$(INSTALL_DIR)/$$binary"; \
			cp "target/debug/$$binary" "$(INSTALL_DIR)/"; \
			chmod +x "$(INSTALL_DIR)/$$binary"; \
			echo "$$binary"; \
		fi \
	done
	@if [ "$(OS)" = "Linux" ] && [ -f "target/debug/zinit-pid1" ]; then \
		rm -f "$(INSTALL_DIR)/zinit-pid1"; \
		cp "target/debug/zinit-pid1" "$(INSTALL_DIR)/"; \
		chmod +x "$(INSTALL_DIR)/zinit-pid1"; \
		echo "  ✓ zinit-pid1"; \
	fi
	@echo "✓ Dev binaries installed to $(INSTALL_DIR)"

clean: ## Remove all build artifacts
	@echo "Cleaning build artifacts..."
	@cargo clean
	@echo "✓ Clean complete"

# ============================================================================
# Quality Assurance
# ============================================================================

build-publish: clean fmt lint test build docs ## Full publish workflow (clean, fmt, lint, test, build, docs)
	@echo ""
	@echo "=== Build & Publish Workflow Complete ==="
	@echo "Version: $(VERSION)"
	@echo "Platform: $(OS)"
	@echo ""
	@echo "Artifacts ready:"
	@echo "  Binaries: target/release/zinit*"
	@echo "  Docs:     target/doc/zinit/index.html"

all: clean fmt-check lint test build ## Clean, check, lint, test, and build

# ============================================================================
# Dependencies
# ============================================================================

deps: ## Show dependency tree
	@cargo tree

update-deps: ## Update all dependencies
	@cargo update
	@echo "✓ Dependencies updated"

# ============================================================================
# Publishing & Distribution
# ============================================================================

install-download: ## Download and install pre-built binaries (like scripts/install_download_run.sh)
	@echo "Installing zinit binaries from Forgejo package registry..."
	@INSTALL_DIR="$(INSTALL_DIR)" \
		SOCKET="$(SOCKET)" \
		CONFIG_DIR="$(CONFIG_DIR)" \
		bash -c '\
		OS=$$(uname -s); \
		ARCH=$$(uname -m); \
		case "$$ARCH" in \
			x86_64) ARCH=amd64 ;; \
			aarch64|arm64) ARCH=arm64 ;; \
		esac; \
		PACKAGE_URL="https://forge.ourworld.tf/api/packages/geomind_code/generic/zinit/dev"; \
		mkdir -p "$(INSTALL_DIR)"; \
		for binary in zinit zinit-server; do \
			BINARY_NAME="$${binary}-$$(echo $$OS | tr A-Z a-z)-$$ARCH"; \
			echo "Downloading $$binary..."; \
			if command -v curl &>/dev/null; then \
				curl -L -f -o "$(INSTALL_DIR)/$${binary}" "$$PACKAGE_URL/$$BINARY_NAME" || exit 1; \
			elif command -v wget &>/dev/null; then \
				wget -O "$(INSTALL_DIR)/$${binary}" "$$PACKAGE_URL/$$BINARY_NAME" || exit 1; \
			else \
				echo "Error: Neither curl nor wget found"; \
				exit 1; \
			fi; \
			chmod +x "$(INSTALL_DIR)/$${binary}"; \
			echo "✓ Installed $$binary"; \
		done; \
		echo ""; \
		echo "✓ Installation complete"; \
		echo "Add to PATH: export PATH=\"$(INSTALL_DIR):$$PATH\""; \
		'

publish-dry-run: clean fmt lint test build docs ## Dry-run for crates.io publish
	@echo "Running crates.io publish dry-run..."
	@cargo publish --dry-run
	@echo ""
	@echo "=== Publish dry-run complete ==="
	@echo "Version: $(VERSION)"
	@echo ""
	@echo "To publish for real, run:"
	@echo "  cargo publish"

build-package: ## Build and package for distribution (requires FORGEJO_TOKEN and forgejo-runner)
	@if [ -z "$$FORGEJO_TOKEN" ]; then \
		echo "Error: FORGEJO_TOKEN is not set"; \
		echo "Please set it with: export FORGEJO_TOKEN='your-token-here'"; \
		exit 1; \
	fi
	@echo "Building package for distribution..."
	@echo "Version: $(VERSION)"
	@echo ""
	@echo "Verifying Forgejo token..."
	@if ! curl -sf -H "Authorization: token $$FORGEJO_TOKEN" "https://forge.ourworld.tf/api/v1/user" > /dev/null; then \
		echo "Error: FORGEJO_TOKEN is invalid or expired"; \
		exit 1; \
	fi
	@echo "Token verified"
	@echo ""
	@echo "Checking for forgejo-runner..."
	@if ! command -v forgejo-runner &> /dev/null; then \
		echo "Installing forgejo-runner..."; \
		curl -sSL https://forge.ourworld.tf/api/packages/lhumina_research/generic/forgejo-runner/dev/install.sh | bash; \
	else \
		echo "forgejo-runner already installed"; \
	fi
	@echo ""
	@echo "Running build workflow for $(OS)..."
ifeq ($(OS),Darwin)
	@forgejo-runner exec \
		-j build-macos \
		-s FORGEJO_TOKEN="$$FORGEJO_TOKEN" \
		--forgejo-instance "https://forge.ourworld.tf" \
		--env VERSION="$(VERSION)" \
		--env OWNER="geomind_code" \
		-i "-self-hosted" \
		-W .forgejo/workflows/build-macos.yaml
else
	@forgejo-runner exec \
		-j build-linux \
		-s FORGEJO_TOKEN="$$FORGEJO_TOKEN" \
		--forgejo-instance "https://forge.ourworld.tf" \
		--env VERSION="$(VERSION)" \
		--env OWNER="geomind_code" \
		-i "-self-hosted" \
		-W .forgejo/workflows/build-linux.yaml
endif
	@echo ""
	@echo "✓ Build complete. Pushing to remote..."

# ============================================================================
# Usage
# ============================================================================

.DEFAULT_GOAL := help