Greenfield Go multi-tenant IPFS Pinning Service wire-compatible with the
IPFS Pinning Services API spec. Paired 1:1 with Kubo over localhost RPC,
clustered via embedded NATS JetStream, Postgres source-of-truth with
RLS-enforced tenancy, Fiber + huma v2 for the HTTP surface, Authentik
OIDC for session login with kid-rotated HS256 JWT API tokens.
Feature-complete against the 22-milestone build plan, including the
ship-it v1.0 gap items:
* admin CLIs: drain/uncordon, maintenance, mint-token, rotate-key,
prune-denylist, rebalance --dry-run, cache-stats, cluster-presences
* TTL leader election via NATS KV, fence tokens, JetStream dedup
* rebalancer (plan/apply split), reconciler, requeue sweeper
* ristretto caches with NATS-backed cross-node invalidation
(placements live-nodes + token denylist)
* maintenance watchdog for stuck cluster-pause flag
* Prometheus /metrics with CIDR ACL, HTTP/pin/scheduler/cache gauges
* rate limiting: session (10/min) + anonymous global (120/min)
* integration tests: rebalance, refcount multi-org, RLS belt
* goreleaser (tar + deb/rpm/apk + Alpine Docker) targeting Gitea
Stack: Cobra/Viper, Fiber v2 + huma v2, embedded NATS JetStream,
pgx/sqlc/golang-migrate, ristretto, TypeID, prometheus/client_golang,
testcontainers-go.
103 lines
3.2 KiB
Makefile
103 lines
3.2 KiB
Makefile
# anchorage — developer-facing Makefile.
|
|
#
|
|
# Targets follow the Go standards doc: fmt + vet + lint + test must be
|
|
# clean on every PR. `make check` is the local equivalent of CI.
|
|
|
|
SHELL := /usr/bin/env bash
|
|
PKG := anchorage
|
|
BIN := anchorage
|
|
BIN_DIR := bin
|
|
CMD_DIR := ./cmd/anchorage
|
|
|
|
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
|
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
|
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
LDFLAGS := -s -w \
|
|
-X $(PKG)/internal/cmd.version=$(VERSION) \
|
|
-X $(PKG)/internal/cmd.commit=$(COMMIT) \
|
|
-X $(PKG)/internal/cmd.date=$(DATE)
|
|
|
|
GO ?= go
|
|
GOFMT ?= gofmt
|
|
GOIMPORTS ?= goimports
|
|
STATICCHK ?= staticcheck
|
|
GOVULNCHK ?= govulncheck
|
|
GORELEASER ?= goreleaser
|
|
|
|
.PHONY: help
|
|
help: ## Show this help
|
|
@awk 'BEGIN{FS=":.*##"} /^[a-zA-Z_-]+:.*##/ {printf " \033[36m%-14s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
|
|
|
.PHONY: build
|
|
build: ## Compile the anchorage binary into ./bin
|
|
@mkdir -p $(BIN_DIR)
|
|
$(GO) build -trimpath -ldflags "$(LDFLAGS)" -o $(BIN_DIR)/$(BIN) $(CMD_DIR)
|
|
|
|
.PHONY: run
|
|
run: ## Run `anchorage version` against the local source tree
|
|
$(GO) run $(CMD_DIR) version
|
|
|
|
.PHONY: fmt
|
|
fmt: ## gofmt + goimports across the module
|
|
$(GOFMT) -w -s .
|
|
@command -v $(GOIMPORTS) >/dev/null && $(GOIMPORTS) -w -local $(PKG) . || echo "goimports not installed; skipping"
|
|
|
|
.PHONY: vet
|
|
vet: ## go vet ./...
|
|
$(GO) vet ./...
|
|
|
|
.PHONY: lint
|
|
lint: ## staticcheck ./...
|
|
@command -v $(STATICCHK) >/dev/null && $(STATICCHK) ./... || echo "staticcheck not installed; skipping"
|
|
|
|
.PHONY: test
|
|
test: ## go test -race -count=1 ./...
|
|
$(GO) test -race -count=1 ./...
|
|
|
|
.PHONY: vuln
|
|
vuln: ## govulncheck ./...
|
|
@command -v $(GOVULNCHK) >/dev/null && $(GOVULNCHK) ./... || echo "govulncheck not installed; skipping"
|
|
|
|
.PHONY: tidy
|
|
tidy: ## go mod tidy
|
|
$(GO) mod tidy
|
|
|
|
.PHONY: check
|
|
check: fmt vet lint test vuln ## Full pre-PR verification
|
|
|
|
.PHONY: clean
|
|
clean: ## Remove build artifacts
|
|
rm -rf $(BIN_DIR) coverage.* dist/
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Release pipeline — GoReleaser
|
|
# ---------------------------------------------------------------------------
|
|
|
|
.PHONY: snapshot
|
|
snapshot: ## Build a local snapshot (tar + deb + rpm + docker) into ./dist
|
|
$(GORELEASER) release --snapshot --clean
|
|
|
|
.PHONY: release-check
|
|
release-check: ## Dry-run the release config without building
|
|
$(GORELEASER) check
|
|
|
|
.PHONY: release
|
|
release: ## Tagged release (requires GITEA_TOKEN in env; pushes to Gitea + package repo)
|
|
$(GORELEASER) release --clean
|
|
|
|
.PHONY: docker-local
|
|
docker-local: build ## Build the container image from the current tree (no GoReleaser)
|
|
docker build -f build/package/Dockerfile -t anchorage:dev $(BIN_DIR)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Swarm stack helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
.PHONY: stack-deploy
|
|
stack-deploy: ## Deploy the production Swarm stack (requires docker swarm init + secrets)
|
|
docker stack deploy -c deploy/docker-compose.yml anchorage
|
|
|
|
.PHONY: stack-rm
|
|
stack-rm: ## Remove the deployed Swarm stack (volumes retained)
|
|
docker stack rm anchorage
|