a73x

Makefile

Ref:   Size: 4.7 KiB   History

CARGO      := cargo
PROFILE    ?= debug
BIN        := git-collab
SERVER_BIN := git-collab-server
# A personal forge is installed by one person onto their own machine, so the
# default has to be a directory that person can write to. `~/.local` is already
# on most PATHs via `~/.local/bin` and is where a per-user install belongs;
# `make install PREFIX=/usr/local` under sudo remains the system-wide install.
PREFIX     ?= $(HOME)/.local

# --- Building blocks ---
.PHONY: fmt check clippy test build install clean

fmt:
	$(CARGO) fmt

check:
	$(CARGO) check

clippy:
	$(CARGO) clippy -- -D warnings

test:
	$(CARGO) test

build:
	$(CARGO) build $(if $(filter release,$(PROFILE)),--release,)

# --- Composites ---
.PHONY: lint ci

lint: fmt check clippy

ci: lint test build

# --- Install ---
.PHONY: uninstall

# The binaries go in first, and the man pages are attempted afterwards without
# the power to fail the install. Man pages are a convenience; the binaries are
# the thing being installed. Someone whose `$(MAN_INSTALL_DIR)` is not writable
# still wants `git-collab` on their PATH, and the old ordering — man pages as a
# prerequisite — meant the convenience could abort the install before a single
# binary had been written. `make install-man` on its own stays strict: asking
# for man pages explicitly and silently not getting them would be worse.
install: PROFILE := release
install: build man
	install -Dm755 target/release/$(BIN) $(PREFIX)/bin/$(BIN)
	install -Dm755 target/release/$(SERVER_BIN) $(PREFIX)/bin/$(SERVER_BIN)
	@install -d $(MAN_INSTALL_DIR) 2>/dev/null && \
		install -m644 $(MAN_DIR)/*.1 $(MAN_INSTALL_DIR)/ 2>/dev/null && \
		echo "installed man pages to $(MAN_INSTALL_DIR)" || \
		echo "warning: could not write $(MAN_INSTALL_DIR) — binaries are installed, man pages are not"

# Removes exactly what `install` creates. The man pages are matched by name
# rather than regenerated: they are `git-collab.1` plus one page per subcommand,
# all named `git-collab-*.1`, so the two patterns are the whole set — including
# pages for subcommands that an older version installed and this one no longer
# generates, which are precisely the litter worth clearing.
uninstall:
	rm -f $(PREFIX)/bin/$(BIN) $(PREFIX)/bin/$(SERVER_BIN)
	rm -f $(MAN_INSTALL_DIR)/$(BIN).1 $(MAN_INSTALL_DIR)/$(BIN)-*.1

clean: clean-man
	$(CARGO) clean

# --- Man pages ---
MAN_DIR         := man/man1
MAN_INSTALL_DIR := $(PREFIX)/share/man/man1

.PHONY: man install-man clean-man

man:
	MAN_OUT_DIR=$(MAN_DIR) $(CARGO) build
	@echo "Man pages written to $(MAN_DIR)/"

install-man: man
	install -d $(MAN_INSTALL_DIR)
	install -m644 $(MAN_DIR)/*.1 $(MAN_INSTALL_DIR)/

clean-man:
	rm -rf man/

# --- Docker ---
REGISTRY   := registry.a73x.sh
IMAGE      := $(REGISTRY)/git-collab-server

# `.dockerignore` excludes `.git/`, correctly — the image needs the source, not
# the repository — so the build inside the container has nothing to read the
# commit from and `--version` there would report the crate version alone. A
# deployed container is the artifact whose identity is hardest to establish
# after the fact: you cannot `git log` it, and an image tagged by commit is only
# as good as whoever typed the tag. So the commit is passed in, and the binary
# carries it. Tag and stamp come from the same `git rev-parse` for that reason.
GIT_COMMIT ?= $(shell git rev-parse HEAD 2>/dev/null)
# `1` when tracked files differ from HEAD, so an image built from a
# work-in-progress tree says `-dirty` instead of claiming to be the commit.
# The `test -n` rather than the porcelain output itself: `$(if ...)` strips
# whitespace from its condition, and porcelain lines start with a space.
GIT_DIRTY  ?= $(shell test -n "$$(git status --porcelain --untracked-files=no 2>/dev/null)" && echo 1)
TAG        ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo 0.0.1)

.PHONY: docker docker-push

docker:
	docker build \
		$(if $(GIT_COMMIT),--build-arg GIT_COMMIT=$(GIT_COMMIT),) \
		$(if $(GIT_DIRTY),--build-arg GIT_DIRTY=1,) \
		-t $(IMAGE):$(TAG) .

docker-push: docker
	docker push $(IMAGE):$(TAG)

# --- Dev helpers ---
.PHONY: run serve dev dashboard base-check

run:
	$(CARGO) run -- $(ARGS)

serve:
	$(CARGO) run --bin $(SERVER_BIN) -- $(ARGS)

dev: fmt
	$(CARGO) run -- $(ARGS)

dashboard: fmt
	$(CARGO) run -- dashboard

# Test another revision without moving this worktree's HEAD or index.
base-check:
	@test -n "$(strip $(REF))" || { echo "usage: make base-check REF=<ref>" >&2; exit 2; }
	@set -eu; \
		root=$$(mktemp -d "$${TMPDIR:-/tmp}/git-collab-base-check.XXXXXX"); \
		checkout="$$root/checkout"; \
		cleanup() { \
			git worktree remove --force "$$checkout" >/dev/null 2>&1 || true; \
			rm -rf "$$root"; \
		}; \
		trap cleanup 0; \
		git worktree add --detach "$$checkout" "$(REF)"; \
		$(MAKE) -C "$$checkout" test