.PHONY: help setup check build fmt format fmt-check lint test \
ci-format ci-lint ci-lockfile-diff ci-check ci-test ci-coverage ci-e2e ci-audit ci-changelog ci-build-check ci-release-readiness \
install-nextest install-llvm-cov \
e2e-up e2e-down e2e-logs e2e-run clean pre-commit lockfile spec-check
.DEFAULT_GOAL := help
CARGO := $(shell which cargo 2>/dev/null || echo $(HOME)/.cargo/bin/cargo)
GREEN := \033[32m
YELLOW := \033[33m
RESET := \033[0m
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'
setup:
rustup component add rustfmt clippy
check:
$(CARGO) check --workspace
build:
$(CARGO) build --workspace
fmt:
$(CARGO) fmt --all
format: fmt
fmt-check:
@echo "$(YELLOW)Checking formatting...$(RESET)"
$(CARGO) fmt --all -- --check
@echo "$(GREEN)✅ Formatting OK$(RESET)"
lint:
@echo "$(YELLOW)Running clippy...$(RESET)"
$(CARGO) clippy --workspace -- -D warnings
@echo "$(GREEN)✅ Clippy clean$(RESET)"
install-nextest:
@$(CARGO) install cargo-nextest --version 0.9.114 --locked 2>/dev/null || true
install-llvm-cov:
@$(CARGO) install cargo-llvm-cov --locked 2>/dev/null || true
test: fmt-check lint install-nextest
$(CARGO) nextest run --workspace
@echo "$(GREEN)✅ All tests passed$(RESET)"
ci-format:
$(CARGO) fmt --all -- --check
ci-lint:
$(CARGO) clippy --workspace --all-features -- -D warnings
$(CARGO) clippy --workspace --all-features --all-targets -- -D warnings
ci-lockfile-diff:
@cp Cargo.lock Cargo.lock.committed
@$(CARGO) generate-lockfile
@diff Cargo.lock.committed Cargo.lock || { \
echo ""; \
echo "ERROR: Cargo.lock is out of date. Run: cargo generate-lockfile && git add Cargo.lock"; \
mv Cargo.lock.committed Cargo.lock; exit 1; }
@mv Cargo.lock.committed Cargo.lock
ci-check: ci-format ci-lint
@echo "$(GREEN)✅ All code quality checks passed$(RESET)"
ci-test:
RUSTFLAGS="-D warnings" $(CARGO) nextest run --workspace \
--all-features -E 'not binary(e2e)'
MALLOC_CONF_PROD ?= prof:true,prof_active:false,lg_prof_sample:19
MUSL_PLATFORM ?=
MUSL_PLATFORM_FLAG := $(if $(MUSL_PLATFORM),--platform $(MUSL_PLATFORM),)
ci-heap-probe-musl:
@docker run --rm $(MUSL_PLATFORM_FLAG) -v "$$PWD":/src -w /src \
-v "$$HOME/.cargo/registry":/usr/local/cargo/registry \
-e CARGO_TARGET_DIR=/tmp/muslbuild \
-e MALLOC_CONF_PROD='$(MALLOC_CONF_PROD)' \
rust:alpine sh -ec '\
apk add --no-cache musl-dev gcc make bash perl libunwind-dev libunwind-static binutils xz-dev xz-static zlib-dev zlib-static >/dev/null; \
arch=$$(uname -m); \
echo "==> what libunwind actually provides on musl"; \
ls -1 /usr/lib/libunwind*.a 2>/dev/null || echo " (no libunwind archives)"; \
for f in /usr/lib/libunwind*.a; do \
[ -e "$$f" ] || continue; \
n=$$(nm -g --defined-only "$$f" 2>/dev/null | grep -cw "unw_backtrace" || true); \
u=$$(nm -g --defined-only "$$f" 2>/dev/null | grep -c "_U.*_backtrace" || true); \
echo " $$f: unw_backtrace=$$n _U*_backtrace=$$u"; \
done; \
echo "==> minimal static link test: can ANY link line resolve unw_backtrace?"; \
printf "#define UNW_LOCAL_ONLY\\n#include <libunwind.h>\\nint main(void){void*b[4];return unw_backtrace(b,4);}\\n" > /tmp/t.c; \
if gcc -static -o /tmp/t /tmp/t.c -lunwind -l:liblzma.a -l:libz.a 2>/tmp/t.err; then \
echo " MINIMAL STATIC LINK OK — libunwind can satisfy it; the problem is how rustc composes the line"; \
else \
echo " MINIMAL STATIC LINK FAILED — libunwind on static musl cannot satisfy unw_backtrace:"; \
sed -n "1,6p" /tmp/t.err | sed "s/^/ /"; \
fi; \
echo "==> linking libunwind-$$arch.a as a static archive"; \
echo " unw_backtrace lives in the arch-specific lib, not in libunwind itself,"; \
echo " so tikv-jemalloc-sys emitting -lunwind alone under-links. Naming the"; \
echo " archive with -l: is required too: rustc places extra link args after"; \
echo " -Wl,-Bdynamic, so a plain -lunwind-$$arch resolves to the .so, which a"; \
echo " -static-pie link cannot use — it is skipped and the symbol stays undefined."; \
triple="$$arch-unknown-linux-musl"; \
tenv=$$(echo "$$triple" | tr "a-z-" "A-Z_"); \
echo "==> target-scoped link flags for $$triple"; \
echo " RUSTFLAGS would also apply to host build scripts and break them"; \
echo " (quote/proc-macro2/libc failed to compile that way); CARGO_TARGET_*"; \
echo " with an explicit --target keeps them off host artifacts."; \
echo "==> building via the build script in this crate: no hand-tuned link flags"; \
echo " build.rs extracts and republishes the unwind shim and emits the"; \
echo " link directives itself, so this exercises exactly what a consumer"; \
echo " gets from enabling the feature — nothing here that a service lacks."; \
cargo build --features profiling-memory-probe --bin heap-probe --target "$$triple"; \
echo "==> target: $$(uname -m) static musl"; \
set +e; \
_RJEM_MALLOC_CONF="$$MALLOC_CONF_PROD" /tmp/muslbuild/$$triple/debug/heap-probe; \
rc=$$?; \
set -e; \
if [ $$rc -ge 128 ]; then \
echo ""; \
echo "ERROR: heap profiling killed the process by signal $$((rc - 128)) on static musl."; \
echo " _RJEM_MALLOC_CONF=$$MALLOC_CONF_PROD"; \
echo " This is the target consuming services ship. jemalloc prof walks a"; \
echo " stack per sampled allocation; without libunwind it walks it through"; \
echo " libgcc, which has no working unwind path in a static musl binary."; \
exit $$rc; \
elif [ $$rc -ne 0 ]; then \
echo ""; \
echo "ERROR: heap probe exited $$rc on static musl — profiling did not arm."; \
exit $$rc; \
fi'
ci-build-check:
$(CARGO) check --workspace --all-targets
$(CARGO) check --workspace --all-targets --all-features
$(CARGO) check --workspace --all-targets --no-default-features
$(CARGO) check --workspace --all-targets --no-default-features --features http
$(CARGO) check --workspace --all-targets --no-default-features --features grpc
$(CARGO) check --workspace --all-targets --no-default-features --features grpc-mtls
ci-release-readiness:
@echo "otel-bootstrap: single-crate lib — nothing to validate here"
ci-coverage:
RUSTFLAGS="-D warnings" $(CARGO) llvm-cov nextest --workspace \
--features integration-tests,grpc-mtls \
--show-missing-lines \
--fail-uncovered-lines 110
ci-e2e:
RUSTFLAGS="-D warnings" $(CARGO) nextest run \
--features integration-tests \
--test e2e
ci-audit:
$(CARGO) audit --deny warnings --deny unsound --deny unmaintained --deny yanked $(if $(DB_PATH),--db $(DB_PATH) --no-fetch,)
audit:
$(CARGO) audit
e2e-up:
docker compose up -d
@echo "$(GREEN)OTel Collector running — gRPC on :4317$(RESET)"
e2e-down:
docker compose down
e2e-logs:
docker compose logs -f
e2e-run: e2e-up
$(CARGO) test --features integration-tests --test e2e
@echo "$(GREEN)✅ E2E tests passed$(RESET)"
lockfile:
cargo generate-lockfile
spec-check:
@test -f SPEC.md || { echo "ERROR: SPEC.md missing"; exit 1; }
@grep -q 'wire_surface:' SPEC.md || { echo "ERROR: SPEC.md missing wire_surface field"; exit 1; }
@echo "spec-check: OK"
pre-commit: spec-check ci-format ci-lint ci-lockfile-diff ci-test ci-changelog
clean:
$(CARGO) clean
.PHONY: ci-changelog
ci-changelog:
@bash -lc 'bash <(curl -fsSL https://raw.githubusercontent.com/brefwiz/shared-ci-workflows/main/scripts/check-release-changelog.sh)'