Replace the legacy Go implementation as the production path, quarantine the reference tree, add deterministic release packaging and version metadata, and run verification through GitLab CI.
169 lines
7.7 KiB
Makefile
169 lines
7.7 KiB
Makefile
SHELL := /usr/bin/env bash
|
|
.SHELLFLAGS := -eu -o pipefail -c
|
|
.DEFAULT_GOAL := help
|
|
.DELETE_ON_ERROR:
|
|
|
|
PROJECT := latchd
|
|
BINARY := latchd
|
|
PREFIX ?= /usr
|
|
DESTDIR ?=
|
|
BINDIR := $(DESTDIR)$(PREFIX)/bin
|
|
CONFDIR := $(DESTDIR)/etc/$(PROJECT)
|
|
SYSTEMD_DIR := $(DESTDIR)/usr/lib/systemd/system
|
|
BUILD_DIR ?= build
|
|
DIST_DIR ?= dist
|
|
CARGO_TARGET_DIR ?= target
|
|
CARGO ?= cargo
|
|
GO ?= go
|
|
TARGET ?=
|
|
TAG ?=
|
|
|
|
VERSION ?= $(shell sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)
|
|
GIT_COMMIT ?= $(shell git rev-parse --short=12 HEAD 2>/dev/null || printf unknown)
|
|
SOURCE_DATE_EPOCH ?= $(shell git log -1 --format=%ct 2>/dev/null || date +%s)
|
|
TARGET_TRIPLE ?= $(shell rustc -vV 2>/dev/null | sed -n 's/^host: //p' || printf unknown)
|
|
|
|
TARGET_ARGS := $(if $(TARGET),--target $(TARGET),)
|
|
RELEASE_DIR := $(CARGO_TARGET_DIR)/$(if $(TARGET),$(TARGET)/)release
|
|
PACKAGE_NAME := $(PROJECT)-$(VERSION)-$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))
|
|
PACKAGE_FILE := $(DIST_DIR)/$(PACKAGE_NAME).tar.gz
|
|
PACKAGE_STAGE := $(DIST_DIR)/stage/$(PACKAGE_NAME)
|
|
CARGO_ENV := LATCHD_VERSION=$(VERSION) LATCHD_COMMIT=$(GIT_COMMIT) LATCHD_BUILD_TIME=$(SOURCE_DATE_EPOCH) SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)
|
|
|
|
COLOR ?= 1
|
|
ifeq ($(COLOR),1)
|
|
RESET := \033[0m
|
|
DIM := \033[2m
|
|
CYAN := \033[36m
|
|
GREEN := \033[32m
|
|
YELLOW := \033[33m
|
|
RED := \033[31m
|
|
else
|
|
RESET :=
|
|
DIM :=
|
|
CYAN :=
|
|
GREEN :=
|
|
YELLOW :=
|
|
RED :=
|
|
endif
|
|
|
|
.PHONY: all deps build release-build test check fmt lint doc audit docker-test \
|
|
package release release-check version install uninstall clean run-preview \
|
|
legacy-build legacy-check enable update help
|
|
|
|
all: release-build ## Build the optimized release binary
|
|
|
|
version: ## Print the package, commit, and reproducibility metadata
|
|
@printf 'version=%s\ncommit=%s\nepoch=%s\ntarget=%s\n' \
|
|
'$(VERSION)' '$(GIT_COMMIT)' '$(SOURCE_DATE_EPOCH)' '$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))'
|
|
|
|
deps: ## Check native dependencies needed for builds and tests
|
|
@command -v $(CARGO) >/dev/null || { echo 'cargo (Rust 1.88+) is required'; exit 1; }
|
|
@command -v rustfmt >/dev/null || { echo 'rustfmt is required'; exit 1; }
|
|
@command -v pkg-config >/dev/null || { echo 'pkg-config is required'; exit 1; }
|
|
@pkg-config --exists pam || { echo 'libpam development files are required'; exit 1; }
|
|
@printf '$(GREEN)dependencies ok$(RESET)\n'
|
|
|
|
build: release-check ## Build and copy the optimized binary to build/
|
|
@mkdir -p '$(BUILD_DIR)'
|
|
@printf '$(CYAN)building$(RESET) %s %s\n' '$(VERSION)' '$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))'
|
|
@env $(CARGO_ENV) $(CARGO) build --release --locked $(TARGET_ARGS)
|
|
@install -m755 '$(RELEASE_DIR)/$(BINARY)' '$(BUILD_DIR)/$(BINARY)'
|
|
@'$(BUILD_DIR)/$(BINARY)' --version
|
|
|
|
release-build: build ## Alias for the optimized release build
|
|
|
|
fmt: ## Check Rust formatting
|
|
@$(CARGO) fmt --all -- --check
|
|
|
|
test: ## Run all Rust tests
|
|
@$(CARGO) test --locked --workspace --all-targets
|
|
|
|
lint: ## Run Clippy with warnings denied
|
|
@$(CARGO) clippy --locked --workspace --all-targets --all-features -- -D warnings
|
|
|
|
doc: ## Build rustdoc with warnings denied
|
|
@RUSTDOCFLAGS='-D warnings' $(CARGO) doc --locked --workspace --no-deps
|
|
|
|
audit: ## Run cargo-audit when installed
|
|
@command -v cargo-audit >/dev/null || { echo 'cargo-audit is required; install with cargo install cargo-audit --locked'; exit 1; }
|
|
@cargo audit
|
|
|
|
check: fmt test lint doc ## Run the complete local verification gate
|
|
|
|
docker-test: ## Run the pinned Docker verification suite
|
|
@./scripts/test-docker.sh
|
|
|
|
release-check: ## Validate a release version and optional tag
|
|
@[[ '$(VERSION)' =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$$ ]] || { echo 'invalid VERSION: $(VERSION)'; exit 1; }
|
|
@cargo_version="$$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)"; \
|
|
[[ "$$cargo_version" == '$(VERSION)' ]] || { echo "Cargo version $$cargo_version does not match $(VERSION)"; exit 1; }
|
|
@if [[ -n '$(TAG)' ]]; then \
|
|
tag_version="$$(printf %s '$(TAG)' | sed 's/^v//')"; \
|
|
[[ "$$tag_version" == '$(VERSION)' ]] || { echo "tag $(TAG) does not match $(VERSION)"; exit 1; }; \
|
|
fi
|
|
|
|
package: release-check build ## Create a deterministic install archive and checksum
|
|
@rm -rf '$(PACKAGE_STAGE)'
|
|
@mkdir -p '$(PACKAGE_STAGE)/usr/bin' '$(PACKAGE_STAGE)/usr/lib/systemd/system' \
|
|
'$(PACKAGE_STAGE)/etc/pam.d' '$(PACKAGE_STAGE)/etc/$(PROJECT)/wms' '$(PACKAGE_STAGE)/etc/$(PROJECT)/wayland'
|
|
@install -m755 '$(BUILD_DIR)/$(BINARY)' '$(PACKAGE_STAGE)/usr/bin/$(BINARY)'
|
|
@install -m644 extra/config.toml '$(PACKAGE_STAGE)/etc/$(PROJECT)/config.toml'
|
|
@install -m644 extra/latchd.pam '$(PACKAGE_STAGE)/etc/pam.d/$(PROJECT)'
|
|
@install -m644 extra/latchd.service '$(PACKAGE_STAGE)/usr/lib/systemd/system/$(PROJECT).service'
|
|
@install -m644 README.md LICENSE CHANGELOG.md '$(PACKAGE_STAGE)/'
|
|
@printf 'version=%s\ncommit=%s\nepoch=%s\ntarget=%s\n' \
|
|
'$(VERSION)' '$(GIT_COMMIT)' '$(SOURCE_DATE_EPOCH)' '$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))' \
|
|
> '$(PACKAGE_STAGE)/BUILD-METADATA'
|
|
@mkdir -p '$(DIST_DIR)'
|
|
@tar --sort=name --numeric-owner --owner=0 --group=0 --mtime='@$(SOURCE_DATE_EPOCH)' \
|
|
-C '$(DIST_DIR)/stage' -czf '$(PACKAGE_FILE)' '$(PACKAGE_NAME)'
|
|
@(cd '$(DIST_DIR)' && sha256sum '$(notdir $(PACKAGE_FILE))' > '$(notdir $(PACKAGE_FILE)).sha256')
|
|
@(cd '$(DIST_DIR)' && sha256sum --check '$(notdir $(PACKAGE_FILE)).sha256')
|
|
@checksum="$$(sha256sum '$(PACKAGE_FILE)' | cut -d' ' -f1)"; \
|
|
printf 'package=%s\nsha256=%s\nversion=%s\n' \
|
|
'$(PACKAGE_FILE)' "$$checksum" '$(VERSION)' > '$(DIST_DIR)/release.env'
|
|
@printf '$(GREEN)created$(RESET) %s\n' '$(PACKAGE_FILE)'
|
|
|
|
release: check audit docker-test package ## Run all gates and produce release artifacts
|
|
|
|
install: build ## Install the binary, config, PAM policy, and systemd unit
|
|
@install -Dm755 '$(BUILD_DIR)/$(BINARY)' '$(BINDIR)/$(BINARY)'
|
|
@install -Dm644 extra/config.toml '$(CONFDIR)/config.toml'
|
|
@install -Dm644 extra/latchd.pam '$(DESTDIR)/etc/pam.d/$(PROJECT)'
|
|
@install -Dm644 extra/latchd.service '$(SYSTEMD_DIR)/$(PROJECT).service'
|
|
@install -d '$(CONFDIR)/wms' '$(CONFDIR)/wayland'
|
|
|
|
uninstall: ## Remove files installed by make install
|
|
@rm -f '$(BINDIR)/$(BINARY)' '$(DESTDIR)/etc/pam.d/$(PROJECT)' '$(SYSTEMD_DIR)/$(PROJECT).service'
|
|
@printf '$(YELLOW)configuration at %s was preserved; remove it manually if no longer needed$(RESET)\n' '$(CONFDIR)'
|
|
|
|
clean: ## Remove generated build, release, and Cargo artifacts
|
|
@rm -rf '$(BUILD_DIR)' '$(DIST_DIR)'
|
|
@$(CARGO) clean
|
|
|
|
run-preview: build ## Run the safe non-authenticating preview UI
|
|
@'$(BUILD_DIR)/$(BINARY)' --preview --no-log
|
|
|
|
legacy-build: ## Build the quarantined Go reference only when explicitly requested
|
|
@printf '$(YELLOW)legacy Go is reference-only and excluded from release builds$(RESET)\n'
|
|
@command -v $(GO) >/dev/null || { echo 'go is required for legacy-build'; exit 1; }
|
|
@mkdir -p '$(BUILD_DIR)'
|
|
@cd legacy/go && $(GO) build -trimpath -o '../../$(BUILD_DIR)/$(BINARY)-go' .
|
|
|
|
legacy-check: ## Format-check and test the quarantined Go reference
|
|
@command -v $(GO) >/dev/null || { echo 'go is required for legacy-check'; exit 1; }
|
|
@command -v gofmt >/dev/null || { echo 'gofmt is required for legacy-check'; exit 1; }
|
|
@test -z "$$(find legacy/go -type f -name '*.go' -print0 | xargs -0 gofmt -l)" || { echo 'legacy Go files need gofmt'; exit 1; }
|
|
@cd legacy/go && $(GO) test ./...
|
|
|
|
enable: ## Enable the systemd service after host validation
|
|
@sudo systemctl enable $(PROJECT).service
|
|
|
|
update: ## Rebuild and reinstall the current checkout without changing git state
|
|
@sudo $(MAKE) install
|
|
|
|
help: ## Show available targets
|
|
@printf '$(CYAN)$(PROJECT)$(RESET) $(VERSION) — production build targets\n\n'
|
|
@awk -F ':|##' '/^[a-zA-Z0-9_-]+:.*##/ { printf " %-18s %s\n", $$1, $$NF }' '$(MAKEFILE_LIST)' | sort
|