SHELL := bash
.SHELLFLAGS := -euo pipefail -c
.ONESHELL:
.DEFAULT_GOAL := help
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables --no-builtin-rules
BINARY := timebomb
RELEASE_BINARY := target/release/$(BINARY)
CARGO := cargo
DOCKER ?= docker
DOCKERFILE ?= Dockerfile
VERSION ?= $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n 1)
IMAGE ?= pwbsladek/timebomb
IMAGE_TAG ?= $(VERSION)
IMAGE_REF := $(IMAGE):$(IMAGE_TAG)
IMAGE_LATEST ?= $(IMAGE):latest
PLATFORMS ?= linux/amd64,linux/arm64
DHI_RUST_IMAGE ?= dhi.io/rust:1-debian13-dev
DHI_STATIC_IMAGE ?= dhi.io/static:20250419-glibc-debian13
SMOKE_DIR ?= /tmp/timebomb-smoke
export CARGO_TERM_COLOR := always
export RUST_BACKTRACE := 1
write-fixture = mkdir -p "$(SMOKE_DIR)/$(1)" && \
printf '%s\n' '$(2)' > "$(SMOKE_DIR)/$(1)/$(3)"
.PHONY: help \
build build-release build-dist \
docker-build docker-run docker-smoke docker-push \
test-unit test-integration test test-nocapture bench bench-no-run \
fmt fmt-check clippy lint \
smoke smoke-empty smoke-list smoke-expired smoke-json smoke-github smoke-clean \
check ci self-check self-list run \
install install-dist uninstall \
clean clean-smoke clean-bench
help:
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-22s\033[0m %s\n", $$1, $$2 } \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' \
$(MAKEFILE_LIST)
build:
$(CARGO) build
build-release:
$(CARGO) build --release
build-dist:
$(CARGO) build --profile dist
docker-build:
$(DOCKER) build \
--file $(DOCKERFILE) \
--build-arg RUST_IMAGE=$(DHI_RUST_IMAGE) \
--build-arg STATIC_IMAGE=$(DHI_STATIC_IMAGE) \
--tag $(IMAGE_REF) \
--tag $(IMAGE_LATEST) \
.
docker-run: docker-build ## Run the local image: make docker-run ARGS="--help"
$(DOCKER) run --rm $(IMAGE_REF) $(ARGS)
docker-smoke: docker-build
$(DOCKER) run --rm $(IMAGE_REF) --version
docker-push:
$(DOCKER) buildx build \
--platform $(PLATFORMS) \
--file $(DOCKERFILE) \
--build-arg RUST_IMAGE=$(DHI_RUST_IMAGE) \
--build-arg STATIC_IMAGE=$(DHI_STATIC_IMAGE) \
--tag $(IMAGE_REF) \
--tag $(IMAGE_LATEST) \
--push \
.
test-unit:
$(CARGO) test --lib --bins --verbose
test-integration:
$(CARGO) test --tests --verbose
test: test-unit test-integration
test-nocapture:
$(CARGO) test -- --nocapture
bench: ## Run criterion benchmarks and print a formatted summary table. Pass TIME=<secs> to change measurement time per bench (default: 5)
@./benches/bench.sh $(if $(TIME),--time $(TIME),)
bench-no-run:
@./benches/bench.sh --no-run
fmt:
$(CARGO) fmt
fmt-check:
$(CARGO) fmt --all -- --check
clippy:
$(CARGO) clippy --all-targets --all-features -- -D warnings
lint: fmt-check clippy
smoke-empty: build-release
@mkdir -p "$(SMOKE_DIR)/empty"
printf ' %-40s' 'empty dir (exits 0) ...'
$(RELEASE_BINARY) sweep "$(SMOKE_DIR)/empty" > /dev/null 2>&1
printf '\033[32m✓ pass\033[0m\n'
smoke-list: build-release
@$(call write-fixture,list,// TODO[2020-01-01]: expired annotation,test.rs)
printf ' %-40s' 'manifest with detonated (exits 0) ...'
$(RELEASE_BINARY) manifest "$(SMOKE_DIR)/list" > /dev/null 2>&1
printf '\033[32m✓ pass\033[0m\n'
smoke-expired: build-release
@$(call write-fixture,expired,// TODO[2020-01-01]: this is expired,main.rs)
printf ' %-40s' 'detonated fuse (exits 1) ...'
$(RELEASE_BINARY) sweep "$(SMOKE_DIR)/expired" > /dev/null 2>&1 && { printf '\033[31m✗ FAIL\033[0m (expected exit 1, got 0)\n' >&2; exit 1; } || true
printf '\033[32m✓ pass\033[0m\n'
smoke-json: build-release
@$(call write-fixture,json,// FIXME[2020-01-01]: old,lib.rs)
printf ' %-40s' '--format json (valid JSON output) ...'
{ $(RELEASE_BINARY) sweep "$(SMOKE_DIR)/json" --format json || true; } \
| python3 -m json.tool > /dev/null
printf '\033[32m✓ pass\033[0m\n'
smoke-github: build-release
@$(call write-fixture,github,// TODO[2020-01-01]: expired annotation,main.rs)
printf ' %-40s' '--format github (::error present) ...'
($(RELEASE_BINARY) sweep "$(SMOKE_DIR)/github" --format github 2>&1 || true) | grep -q "::error" || { printf '\033[31m✗ FAIL\033[0m (::error not found)\n' >&2; exit 1; }
printf '\033[32m✓ pass\033[0m\n'
smoke: build-release smoke-empty smoke-list smoke-expired smoke-json smoke-github
@printf '\n\033[1;32m✓ All smoke tests passed\033[0m\n'
smoke-clean:
@rm -rf "$(SMOKE_DIR)"
check: fmt-check clippy test smoke
@printf '\n\033[1;32m✓ All checks passed\033[0m\n'
ci: check
self-check: build-release
$(RELEASE_BINARY) sweep ./src --format github || true
self-list: build-release
$(RELEASE_BINARY) manifest ./src || true
run: ## Run the dev binary: make run ARGS="check ./src"
$(CARGO) run -- $(ARGS)
install:
$(CARGO) install --path .
install-dist:
$(CARGO) install --path . --profile dist
uninstall:
$(CARGO) uninstall $(BINARY)
clean:
$(CARGO) clean
clean-smoke: smoke-clean
clean-bench:
rm -rf target/criterion