.DEFAULT_GOAL := help
SHELL := /usr/bin/env bash
CARGO ?= cargo
BIN := notiflow
MSRV := $(shell awk -F'"' '/^rust-version/ {print $$2}' Cargo.toml)
TARGET ?=
DEBUG_BIN := target/debug/$(BIN)
GEN_DIR := docs/src/generated
GEN_FILES := $(GEN_DIR)/exit-codes.md $(GEN_DIR)/placeholders.md $(GEN_DIR)/cli.md \
$(GEN_DIR)/action-inputs.md $(GEN_DIR)/action-outputs.md
.PHONY: help
help:
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-16s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
.PHONY: build
build:
$(CARGO) build
.PHONY: release
release:
$(CARGO) build --release --locked
.PHONY: run
run: ## Run the binary (make run ARGS="send -m hi")
$(CARGO) run -- $(ARGS)
.PHONY: install
install:
$(CARGO) install --path . --locked
.PHONY: fmt
fmt:
$(CARGO) fmt
.PHONY: fmt-check
fmt-check:
$(CARGO) fmt --check
.PHONY: clippy
clippy:
$(CARGO) clippy --all-targets --all-features -- -D warnings
.PHONY: shellcheck
shellcheck:
@command -v shellcheck >/dev/null || { echo "shellcheck not found; run: make install-tools"; exit 1; }
shellcheck -x scripts/install.sh scripts/action/*.sh tests/parity/v1-driver.sh
@if command -v shfmt >/dev/null; then shfmt -d -i 2 -ci scripts tests/parity/v1-driver.sh; fi
.PHONY: actionlint
actionlint:
@if command -v actionlint >/dev/null; then actionlint; \
else echo "actionlint not found — skipping (run: make install-tools)"; fi
.PHONY: lint
lint: fmt-check clippy shellcheck actionlint
.PHONY: check
check:
$(CARGO) check --all-targets
.PHONY: test
test:
$(CARGO) test --all-features
.PHONY: test-v1
test-v1:
@command -v bats >/dev/null || { echo "bats not found; run: make install-tools"; exit 1; }
bats tests/parity/bats
.PHONY: test-action
test-action: build
@command -v bats >/dev/null || { echo "bats not found; run: make install-tools"; exit 1; }
bats tests/action
.PHONY: action-smoke
action-smoke: build
NF_BIN=$(CURDIR)/$(DEBUG_BIN) bash scripts/action/smoke.sh
.PHONY: parity
parity: build
python3 tests/parity/run.py
.PHONY: parity-bless
parity-bless: build
python3 tests/parity/run.py --bless
.PHONY: msrv
msrv:
@command -v rustup >/dev/null || { echo "rustup is required for the MSRV check"; exit 1; }
rustup toolchain install $(MSRV) --profile minimal --no-self-update
rustup run $(MSRV) cargo check --all-targets --locked
.PHONY: audit
audit:
@command -v cargo-audit >/dev/null || $(CARGO) install cargo-audit --locked
$(CARGO) audit
.PHONY: deny
deny:
@command -v cargo-deny >/dev/null || $(CARGO) install cargo-deny --locked
$(CARGO) deny check
.PHONY: gen
gen: build
@mkdir -p $(GEN_DIR)
$(DEBUG_BIN) docs exit-codes > $(GEN_DIR)/exit-codes.md
$(DEBUG_BIN) docs placeholders > $(GEN_DIR)/placeholders.md
$(DEBUG_BIN) docs cli > $(GEN_DIR)/cli.md
python3 scripts/gen-action-tables.py
@echo "regenerated: $(GEN_FILES)"
.PHONY: gen-check
gen-check:
@$(MAKE) --no-print-directory gen
@if ! git diff --quiet -- $(GEN_DIR); then \
echo "generated docs are out of date; run 'make gen' and commit the result" >&2; \
git --no-pager diff -- $(GEN_DIR); \
exit 1; \
fi
.PHONY: docs-install
docs-install:
cd docs && npm ci
.PHONY: docs-dev
docs-dev:
cd docs && npm run dev
.PHONY: docs-build
docs-build: gen
cd docs && npm run build
.PHONY: docs-preview
docs-preview:
cd docs && npm run preview
.PHONY: dist
dist: ## Cross-build one release archive (make dist TARGET=aarch64-apple-darwin)
@test -n "$(TARGET)" || { echo "usage: make dist TARGET=<rust-target>"; exit 1; }
$(CARGO) build --release --locked --target $(TARGET)
@mkdir -p dist
@ @ @if [[ "$(TARGET)" == *windows* ]]; then \
src="target/$(TARGET)/release/$(BIN).exe"; out="$(CURDIR)/dist/$(BIN)-$(TARGET).zip"; \
rm -f "$$out"; \
if command -v zip >/dev/null; then \
(cd "target/$(TARGET)/release" && zip -q -X "$$out" "$(BIN).exe"); \
elif command -v 7z >/dev/null; then \
(cd "target/$(TARGET)/release" && 7z a -tzip -bso0 -bsp0 "$$out" "$(BIN).exe"); \
elif command -v powershell >/dev/null; then \
powershell -NoProfile -Command "Compress-Archive -Path '$$src' -DestinationPath '$$out' -Force"; \
else \
echo "no zip, 7z or powershell available to build $$out" >&2; exit 1; \
fi; \
else \
tar -czf dist/$(BIN)-$(TARGET).tar.gz -C target/$(TARGET)/release $(BIN); \
fi
@echo "dist/$(BIN)-$(TARGET).*"
.PHONY: dist-all
dist-all:
@for t in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu \
x86_64-unknown-linux-musl aarch64-unknown-linux-musl \
x86_64-apple-darwin aarch64-apple-darwin x86_64-pc-windows-msvc; do \
$(MAKE) --no-print-directory dist TARGET=$$t || echo "skipped $$t"; \
done
.PHONY: version-check
version-check: ## Verify Cargo.toml and VERSION agree with a tag (make version-check TAG=v2.0.0)
@test -n "$(TAG)" || { echo "usage: make version-check TAG=vX.Y.Z"; exit 1; }
@want="$(TAG)"; want="$${want#v}"; \
cargo_version="$$(awk -F'"' '/^version = / {print $$2; exit}' Cargo.toml)"; \
file_version="$$(tr -d '[:space:]' < VERSION)"; file_version="$${file_version#v}"; \
fail=0; \
if [ "$$cargo_version" != "$$want" ]; then \
echo "Cargo.toml version is $$cargo_version, tag says $$want" >&2; fail=1; fi; \
if [ "$$file_version" != "$$want" ]; then \
echo "VERSION is $$file_version, tag says $$want" >&2; fail=1; fi; \
if [ $$fail -ne 0 ]; then \
echo "run 'make release-prep VERSION=$$want' before tagging" >&2; exit 1; fi; \
echo "version $$want is consistent across Cargo.toml, VERSION and the tag"
.PHONY: release-prep
release-prep: ## Stamp a version into Cargo.toml and VERSION (make release-prep VERSION=2.0.0)
@test -n "$(VERSION)" || { echo "usage: make release-prep VERSION=X.Y.Z"; exit 1; }
@ @ @ @v="$(VERSION)"; v="$${v#v}"; \
python3 -c 'import re,sys,pathlib; p=pathlib.Path("Cargo.toml"); s=p.read_text(); \
s2,n=re.subn(r"^version = \"[^\"]*\"", "version = \"%s\"" % sys.argv[1], s, count=1, flags=re.M); \
sys.exit("Cargo.toml: no version line found") if n != 1 else p.write_text(s2)' "$$v"; \
printf 'v%s\n' "$$v" > VERSION; \
$(CARGO) update --workspace --quiet; \
$(MAKE) --no-print-directory version-check TAG="v$$v"; \
echo "now update CHANGELOG.md, commit, and tag v$$v"
.PHONY: checksums
checksums:
@cd dist && { command -v sha256sum >/dev/null && sha256sum $(BIN)-* || shasum -a 256 $(BIN)-*; } > checksums.txt
@cat dist/checksums.txt
.PHONY: ci
ci: lint test gen-check parity
.PHONY: install-tools
install-tools:
@if [ "$$(uname)" = "Darwin" ]; then \
brew install bats-core shellcheck shfmt actionlint jq; \
elif command -v apt-get >/dev/null; then \
sudo apt-get update && sudo apt-get install -y bats shellcheck jq; \
curl -sSfL -o /tmp/shfmt https://github.com/mvdan/sh/releases/download/v3.7.0/shfmt_v3.7.0_linux_amd64; \
sudo install -m 0755 /tmp/shfmt /usr/local/bin/shfmt; \
bash <(curl -sSfL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash); \
sudo install -m 0755 ./actionlint /usr/local/bin/actionlint; \
else \
echo "unsupported OS — install bats-core, shellcheck, shfmt, actionlint and jq by hand"; exit 1; \
fi
$(CARGO) install cargo-deny cargo-audit --locked
.PHONY: clean
clean:
$(CARGO) clean
rm -rf dist docs/dist docs/.astro