8afda7cd8c
CI / Test (store) (pull_request) Successful in 5s
CI / Test (rest) (pull_request) Successful in 9s
CI / Build (windows/amd64) (pull_request) Successful in 7s
CI / Test (server-http) (pull_request) Successful in 17s
CI / Build (linux/amd64) (pull_request) Successful in 7s
CI / Lint (pull_request) Successful in 19s
CI / Build (linux/arm64) (pull_request) Successful in 14s
e2e / Playwright vs docker-compose (pull_request) Successful in 1m27s
The Dockerfile only set `-X main.version=...`, so docker-built binaries
left `internal/version.Version` at its default "dev". The update logic
(host_update.go:61, hosts.go:94, fleet_update.go:101 et al.) compares
against `internal/version.Version`, so a v1.0.0 host always looked
out-of-date to a v1.0.0 server, the chip never cleared, and pressing
"update" re-downloaded the same bundled binary on a loop.
Collapse the two version sources: drop the `var version/commit/date`
locals in cmd/{server,agent}/main.go, route everything through
internal/version (now also carrying Date), and have both the Dockerfile
and the Makefile set the same single set of -X flags. Verified
end-to-end: make build and docker build both emit binaries whose
--version reflects the build VERSION.
188 lines
8.5 KiB
Makefile
188 lines
8.5 KiB
Makefile
# restic-manager — common dev targets
|
|
|
|
SHELL := /bin/bash
|
|
BIN_DIR := bin
|
|
SERVER_BIN := $(BIN_DIR)/restic-manager-server
|
|
AGENT_BIN := $(BIN_DIR)/restic-manager-agent
|
|
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
|
COMMIT ?= $(shell git rev-parse HEAD 2>/dev/null || echo none)
|
|
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
VERSION_PKG := gitea.dcglab.co.uk/steve/restic-manager/internal/version
|
|
LDFLAGS := -s -w \
|
|
-X $(VERSION_PKG).Version=$(VERSION) \
|
|
-X $(VERSION_PKG).Commit=$(COMMIT) \
|
|
-X $(VERSION_PKG).Date=$(DATE)
|
|
GOFLAGS := -trimpath
|
|
DOCKER_IMAGE ?= gitea.dcglab.co.uk/steve/restic-manager
|
|
DOCKER_TAG ?= dev
|
|
|
|
# Tailwind standalone CLI — single binary, no Node toolchain.
|
|
# See spec.md §4.1 / tasks.md P1-28 for why.
|
|
TAILWIND_VERSION ?= v3.4.17
|
|
TAILWIND_OS := $(shell uname -s | tr A-Z a-z)
|
|
TAILWIND_ARCH := $(shell uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/')
|
|
TAILWIND_BIN := $(BIN_DIR)/tailwindcss
|
|
TAILWIND_URL := https://github.com/tailwindlabs/tailwindcss/releases/download/$(TAILWIND_VERSION)/tailwindcss-$(TAILWIND_OS)-$(TAILWIND_ARCH)
|
|
TAILWIND_INPUT := web/styles/input.css
|
|
TAILWIND_OUTPUT := web/static/css/styles.css
|
|
|
|
# mdBook for the docs site (P5-01). Single static binary, no
|
|
# Rust toolchain — same pattern as Tailwind.
|
|
MDBOOK_VERSION ?= v0.4.51
|
|
MDBOOK_OS := $(shell uname -s | tr A-Z a-z)
|
|
MDBOOK_TRIPLE := $(shell uname -m)-unknown-$(if $(filter darwin,$(MDBOOK_OS)),apple-darwin,linux-gnu)
|
|
MDBOOK_BIN := $(BIN_DIR)/mdbook
|
|
MDBOOK_TARBALL := mdbook-$(MDBOOK_VERSION)-$(MDBOOK_TRIPLE).tar.gz
|
|
MDBOOK_URL := https://github.com/rust-lang/mdBook/releases/download/$(MDBOOK_VERSION)/$(MDBOOK_TARBALL)
|
|
DOCS_BOOK_DIR := docs/book
|
|
DOCS_BOOK_OUT := $(DOCS_BOOK_DIR)/book
|
|
|
|
.PHONY: help build server agent test test-race lint fmt tidy clean run-server run-agent docker release tailwind tailwind-watch docs docs-watch setup hooks smoke-restart smoke-stop smoke-status smoke-logs smoke-deploy
|
|
|
|
# ---- smoke-env tooling -------------------------------------------------
|
|
# The smoke server runs as a transient user-systemd unit so it survives
|
|
# bash-tool boundaries and reboots-of-the-shell. Use `make smoke-restart`
|
|
# any time you've rebuilt the server. `make smoke-deploy` is the full
|
|
# rebuild + restage + restart workflow described in CLAUDE.md.
|
|
SMOKE_UNIT := restic-manager-smoke
|
|
SMOKE_DATA_DIR := $(HOME)/smoke/data
|
|
SMOKE_LOG_FILE := $(HOME)/smoke/server.log
|
|
SMOKE_BASE_URL := http://127.0.0.1:8080
|
|
SMOKE_LISTEN := :8080
|
|
|
|
help:
|
|
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN{FS=":.*?## "};{printf " \033[36m%-14s\033[0m %s\n",$$1,$$2}'
|
|
|
|
build: tailwind server agent ## Build server + agent into ./bin (incl. Tailwind CSS)
|
|
|
|
server: ## Build the server binary
|
|
@mkdir -p $(BIN_DIR)
|
|
CGO_ENABLED=0 go build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(SERVER_BIN) ./cmd/server
|
|
|
|
$(TAILWIND_BIN):
|
|
@mkdir -p $(BIN_DIR)
|
|
@echo "==> downloading tailwindcss $(TAILWIND_VERSION) ($(TAILWIND_OS)/$(TAILWIND_ARCH))"
|
|
curl -fsSL -o $@ "$(TAILWIND_URL)"
|
|
chmod +x $@
|
|
|
|
tailwind: $(TAILWIND_BIN) ## Build the CSS bundle from web/styles/input.css
|
|
@mkdir -p $$(dirname $(TAILWIND_OUTPUT))
|
|
$(TAILWIND_BIN) -c tailwind.config.js -i $(TAILWIND_INPUT) -o $(TAILWIND_OUTPUT) --minify
|
|
|
|
tailwind-watch: $(TAILWIND_BIN) ## Watch and rebuild on every save
|
|
@mkdir -p $$(dirname $(TAILWIND_OUTPUT))
|
|
$(TAILWIND_BIN) -c tailwind.config.js -i $(TAILWIND_INPUT) -o $(TAILWIND_OUTPUT) --watch
|
|
|
|
$(MDBOOK_BIN):
|
|
@mkdir -p $(BIN_DIR)
|
|
@echo "==> downloading mdbook $(MDBOOK_VERSION) ($(MDBOOK_TRIPLE))"
|
|
curl -fsSL "$(MDBOOK_URL)" | tar -xz -C $(BIN_DIR) mdbook
|
|
@chmod +x $@
|
|
|
|
docs: $(MDBOOK_BIN) ## Build the docs/book/ mdBook site into docs/book/book/
|
|
$(MDBOOK_BIN) build $(DOCS_BOOK_DIR)
|
|
|
|
docs-watch: $(MDBOOK_BIN) ## Serve the docs site at http://127.0.0.1:3000 with live reload
|
|
$(MDBOOK_BIN) serve $(DOCS_BOOK_DIR) -n 127.0.0.1 -p 3000
|
|
|
|
agent: ## Build the agent binary
|
|
@mkdir -p $(BIN_DIR)
|
|
CGO_ENABLED=0 go build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(AGENT_BIN) ./cmd/agent
|
|
|
|
test: ## Run tests
|
|
go test ./...
|
|
|
|
test-race: ## Run tests with the race detector
|
|
go test -race -coverprofile=coverage.out ./...
|
|
|
|
lint: ## Run golangci-lint
|
|
golangci-lint run ./...
|
|
|
|
setup: hooks ## One-time per-clone setup (Go tools + git hooks)
|
|
@command -v gofumpt >/dev/null 2>&1 || go install mvdan.cc/gofumpt@latest
|
|
@command -v golangci-lint >/dev/null 2>&1 || go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0
|
|
@echo "==> setup complete: gofumpt, golangci-lint, pre-commit hooks installed"
|
|
|
|
hooks: ## Install the pre-commit hooks defined in .pre-commit-config.yaml
|
|
@command -v pre-commit >/dev/null 2>&1 || { echo "pre-commit not found — install with 'pip install pre-commit' or 'brew install pre-commit'" >&2; exit 1; }
|
|
pre-commit install
|
|
|
|
fmt: ## Format with gofumpt + goimports
|
|
gofumpt -w .
|
|
goimports -local gitea.dcglab.co.uk/steve/restic-manager -w .
|
|
|
|
tidy: ## go mod tidy
|
|
go mod tidy
|
|
|
|
clean: ## Remove build artifacts
|
|
rm -rf $(BIN_DIR) coverage.out coverage.html $(TAILWIND_OUTPUT) $(DOCS_BOOK_OUT)
|
|
|
|
run-server: server ## Build and run the server
|
|
$(SERVER_BIN)
|
|
|
|
run-agent: agent ## Build and run the agent
|
|
$(AGENT_BIN)
|
|
|
|
docker: ## Build the server Docker image
|
|
docker build -f deploy/Dockerfile.server \
|
|
--build-arg VERSION=$(VERSION) \
|
|
--build-arg COMMIT=$(COMMIT) \
|
|
--build-arg DATE=$(DATE) \
|
|
-t $(DOCKER_IMAGE):$(DOCKER_TAG) .
|
|
|
|
smoke-restart: server ## (Re)start the smoke server as a transient user-systemd unit
|
|
@systemctl --user reset-failed $(SMOKE_UNIT) >/dev/null 2>&1 || true
|
|
@systemctl --user stop $(SMOKE_UNIT) >/dev/null 2>&1 || true
|
|
@echo "==> launching $(SMOKE_UNIT)"
|
|
systemd-run --user --unit=$(SMOKE_UNIT) \
|
|
--setenv=RM_LISTEN=$(SMOKE_LISTEN) \
|
|
--setenv=RM_DATA_DIR=$(SMOKE_DATA_DIR) \
|
|
--setenv=RM_BASE_URL=$(SMOKE_BASE_URL) \
|
|
--setenv=RM_SECRET_KEY_FILE=$(SMOKE_DATA_DIR)/secret.key \
|
|
--setenv=RM_COOKIE_SECURE=false \
|
|
--property=StandardOutput=append:$(SMOKE_LOG_FILE) \
|
|
--property=StandardError=append:$(SMOKE_LOG_FILE) \
|
|
--property=Restart=on-failure \
|
|
$(PWD)/$(SERVER_BIN)
|
|
@for i in 1 2 3 4 5; do \
|
|
curl -fsS -o /dev/null $(SMOKE_BASE_URL)/api/version 2>/dev/null && \
|
|
{ echo "==> smoke server up: $$(curl -s $(SMOKE_BASE_URL)/api/version)"; exit 0; }; \
|
|
sleep 1; \
|
|
done; \
|
|
echo "!! smoke server did not respond on $(SMOKE_BASE_URL) — check $(SMOKE_LOG_FILE)" >&2; \
|
|
systemctl --user status --no-pager $(SMOKE_UNIT) || true; \
|
|
exit 1
|
|
|
|
smoke-stop: ## Stop the smoke server
|
|
systemctl --user stop $(SMOKE_UNIT) || true
|
|
@systemctl --user reset-failed $(SMOKE_UNIT) >/dev/null 2>&1 || true
|
|
|
|
smoke-status: ## Show status of the smoke server
|
|
@systemctl --user status --no-pager $(SMOKE_UNIT) 2>&1 | head -20 || true
|
|
|
|
smoke-logs: ## Tail the smoke server log
|
|
tail -50 $(SMOKE_LOG_FILE)
|
|
|
|
smoke-deploy: build smoke-restart ## Rebuild + restage agent into smoke + restart server (full per-CLAUDE.md cycle)
|
|
@echo "==> restaging agent + install assets into $(SMOKE_DATA_DIR)"
|
|
cp $(AGENT_BIN) $(SMOKE_DATA_DIR)/agent-binaries/restic-manager-agent-linux-amd64
|
|
cp deploy/install/install.sh $(SMOKE_DATA_DIR)/install/install.sh
|
|
cp deploy/install/install.ps1 $(SMOKE_DATA_DIR)/install/install.ps1
|
|
cp deploy/install/restic-manager-agent.service $(SMOKE_DATA_DIR)/install/restic-manager-agent.service
|
|
@echo "==> NOTE: this dev box's installed agent at /usr/local/bin/restic-manager-agent is NOT updated by this target."
|
|
@echo " Run the agent restage block in CLAUDE.md if your change touches agent code or the unit file."
|
|
|
|
release: ## Cross-compile for all supported platforms
|
|
@mkdir -p $(BIN_DIR)
|
|
@for target in linux/amd64 linux/arm64 windows/amd64; do \
|
|
goos=$${target%/*}; goarch=$${target#*/}; \
|
|
ext=""; if [ "$$goos" = "windows" ]; then ext=".exe"; fi; \
|
|
echo "==> $$goos/$$goarch"; \
|
|
GOOS=$$goos GOARCH=$$goarch CGO_ENABLED=0 \
|
|
go build $(GOFLAGS) -ldflags "$(LDFLAGS)" \
|
|
-o $(BIN_DIR)/restic-manager-server-$$goos-$$goarch$$ext ./cmd/server; \
|
|
GOOS=$$goos GOARCH=$$goarch CGO_ENABLED=0 \
|
|
go build $(GOFLAGS) -ldflags "$(LDFLAGS)" \
|
|
-o $(BIN_DIR)/restic-manager-agent-$$goos-$$goarch$$ext ./cmd/agent; \
|
|
done
|