SHELL := /bin/bash
# Detect host architecture for musl target
MUSL_TARGET := $(shell uname -m | sed 's/x86_64/x86_64-unknown-linux-musl/' | sed 's/aarch64/aarch64-unknown-linux-musl/')
.PHONY: build build-debug build-static test test-unit test-e2e test-e2e-all test-e2e-browser test-perf test-e2e-podman test-compose test-compose-validate coverage run fmt clippy check docker-build docker-build-alpine docker-build-all docker-build-cross docker-build-multiarch docker-build-package docker-run docker-run-alpine compose-up compose-down hash-password clean security-scan test-all quick-start init completions manpage bench changelog install-act ensure-podman-socket ci-lint ci-test ci-docker-lint ci-e2e ci validate validate-docker validate-ci validate-msrv validate-coverage validate-security
build:
cargo build --release
build-debug:
cargo build
build-static:
rustup target add $(MUSL_TARGET) 2>/dev/null || true
cargo build --release --target $(MUSL_TARGET)
@echo "Static binary: target/$(MUSL_TARGET)/release/sks5"
test:
cargo test --all-targets
test-unit:
cargo test --lib
test-e2e:
cargo test --test '*'
test-e2e-all:
cargo test --test '*' -- --include-ignored
test-e2e-browser:
@command -v podman >/dev/null 2>&1 || { echo "Error: podman is required for browser E2E tests"; exit 1; }
@podman image exists docker.io/chromedp/headless-shell:latest 2>/dev/null || \
{ echo "Pulling chromedp/headless-shell..."; podman pull docker.io/chromedp/headless-shell:latest; }
@status=0; \
cargo test --test e2e_browser_dashboard -- --ignored --nocapture || status=$$?; \
podman ps -aq --filter "name=sks5-chrome" | xargs -r podman stop 2>/dev/null || true; \
exit $$status
test-perf:
cargo test --test e2e_performance -- --ignored --nocapture
test-e2e-podman:
./scripts/test-e2e-podman.sh
test-compose:
./scripts/test-compose.sh
test-compose-validate:
podman-compose config
test-all: test security-scan
coverage:
cargo llvm-cov --all-targets
run:
cargo run -- --config config.example.toml
fmt:
cargo fmt
clippy:
cargo clippy --all-targets -- -D warnings
check:
cargo check --all-targets
security-scan:
./scripts/security-scan.sh
validate-msrv:
@rustup toolchain list | grep -q '^1\.88' || rustup toolchain install 1.88
cargo +1.88 check
validate-coverage:
@rustup component add llvm-tools-preview 2>/dev/null || true
@command -v cargo-llvm-cov >/dev/null 2>&1 || cargo install cargo-llvm-cov --locked
cargo llvm-cov --lcov --output-path lcov.info --lib --test unit
validate-security:
@cargo audit
@cargo deny check
docker-build:
podman build -t sks5:latest .
docker-build-alpine:
podman build -f Containerfile.alpine -t sks5:alpine .
docker-build-all: docker-build docker-build-alpine
@echo "Built sks5:latest (scratch) and sks5:alpine"
docker-build-cross:
./scripts/build-multiarch-cross.sh
docker-build-multiarch:
./scripts/build-multiarch-qemu.sh
docker-build-package:
@echo "Building multi-arch image from pre-built binaries..."
@test -f binaries/amd64/sks5 || { echo "Error: binaries/amd64/sks5 not found. Run 'make build-static' first."; exit 1; }
@test -f binaries/arm64/sks5 || { echo "Error: binaries/arm64/sks5 not found. Cross-compile for aarch64 first."; exit 1; }
podman build -f Containerfile.package --target minimal -t sks5:latest .
podman build -f Containerfile.package --target alpine -t sks5:alpine .
docker-run:
podman run --rm -p 2222:2222 -p 1080:1080 \
-v ./config.example.toml:/etc/sks5/config.toml:ro sks5:latest
docker-run-alpine:
podman run --rm -p 2222:2222 -p 1080:1080 \
-v ./config.example.toml:/etc/sks5/config.toml:ro sks5:alpine
compose-up:
podman-compose up -d
compose-down:
podman-compose down
hash-password:
@read -sp "Enter password: " pass && echo && \
hash=$$(cargo run --quiet -- hash-password --password "$$pass") && \
echo "" && \
echo "password_hash = \"$$hash\""
quick-start:
cargo run -- quick-start --password demo
init:
cargo run -- init --password demo --output config.toml
completions:
@mkdir -p completions
cargo run --quiet -- completions bash > completions/sks5.bash
cargo run --quiet -- completions zsh > completions/_sks5
cargo run --quiet -- completions fish > completions/sks5.fish
@echo "Shell completions generated in completions/"
manpage:
@mkdir -p man
cargo run --quiet -- manpage > man/sks5.1
@echo "Man page generated: man/sks5.1"
bench:
cargo bench
changelog:
git-cliff --output CHANGELOG.md
clean:
cargo clean
# ===========================================================================
# Local CI with act + Podman
# ===========================================================================
install-act:
@echo "Installing act to ~/.local/bin..."
@mkdir -p ~/.local/bin
@curl -fsSL https://raw.githubusercontent.com/nektos/act/master/install.sh | bash -s -- -b ~/.local/bin
@echo "act installed: $$(~/.local/bin/act --version)"
ensure-podman-socket:
@systemctl --user is-active podman.socket >/dev/null 2>&1 || \
{ echo "Starting Podman socket..."; systemctl --user start podman.socket; }
@test -S "$${XDG_RUNTIME_DIR}/podman/podman.sock" || \
{ echo "Error: Podman socket not found at $${XDG_RUNTIME_DIR}/podman/podman.sock"; exit 1; }
ci-lint: ensure-podman-socket
DOCKER_HOST="unix://$${XDG_RUNTIME_DIR}/podman/podman.sock" \
act push -j lint \
--container-daemon-socket "$${XDG_RUNTIME_DIR}/podman/podman.sock" \
--eventpath .github/act-event.json \
--env RUST_BACKTRACE=1
ci-test: ensure-podman-socket
DOCKER_HOST="unix://$${XDG_RUNTIME_DIR}/podman/podman.sock" \
act push -j test \
--container-daemon-socket "$${XDG_RUNTIME_DIR}/podman/podman.sock" \
--eventpath .github/act-event.json \
--env RUST_BACKTRACE=1
ci-docker-lint: ensure-podman-socket
DOCKER_HOST="unix://$${XDG_RUNTIME_DIR}/podman/podman.sock" \
act push -j docker-lint \
--container-daemon-socket "$${XDG_RUNTIME_DIR}/podman/podman.sock" \
--eventpath .github/act-event.json
ci-e2e: ensure-podman-socket
DOCKER_HOST="unix://$${XDG_RUNTIME_DIR}/podman/podman.sock" \
act push -j e2e-tests \
--container-daemon-socket "$${XDG_RUNTIME_DIR}/podman/podman.sock" \
--eventpath .github/act-event.json \
--env RUST_BACKTRACE=1
ci: ci-lint ci-test ci-e2e ci-docker-lint
@echo "Local CI passed (lint + test + e2e + docker-lint)"
validate:
@./scripts/validate.sh
validate-docker:
@./scripts/validate.sh --with-docker
# Sequential CI reproduction (kept for backwards compatibility)
validate-ci: ci-lint ci-test ci-e2e ci-docker-lint
@echo "CI reproduction passed (via act)"