BINARIES := collector aggregator frontend cli MODULE := git.ipng.ch/ipng/nginx-logtail PROTO_DIR := proto PROTO_FILE := $(PROTO_DIR)/logtail.proto GEN_FILES := proto/logtailpb/logtail.pb.go proto/logtailpb/logtail_grpc.pb.go NATIVE_ARCH := $(shell go env GOARCH) VERSION := 0.9.1 COMMIT_HASH := $(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 '$(MODULE)/internal/version.Version=$(VERSION)' \ -X '$(MODULE)/internal/version.Commit=$(COMMIT_HASH)' \ -X '$(MODULE)/internal/version.Date=$(DATE)' # CGO_ENABLED=0 produces fully static binaries: no libc dependency, so the # .deb runs on any Linux-amd64/arm64 host regardless of glibc version. export CGO_ENABLED := 0 IMAGE := git.ipng.ch/ipng/nginx-logtail # GO_VERSION is the floor install-deps-go enforces. go.mod currently requires # 1.24; override on the command line to pull a specific patch release: # make install-deps GO_VERSION=1.25.0 GO_VERSION ?= 1.24.6 # GOLANGCI_LINT_VERSION is the minimum golangci-lint version install-deps-go-tools # accepts. Older releases can't parse recent Go syntax. GOLANGCI_LINT_VERSION ?= 1.64.0 .PHONY: help all build build-amd64 build-arm64 test lint proto pkg-deb docker docker-push clean \ install-deps install-deps-apt install-deps-go install-deps-go-tools # help is the default target. Keep the list aligned with the .PHONY block above. help: @echo "nginx-logtail — make targets (version $(VERSION))" @echo "" @echo " build build all four binaries for the native arch into build/$(NATIVE_ARCH)/" @echo " build-amd64 build all four binaries for linux/amd64 into build/amd64/" @echo " build-arm64 build all four binaries for linux/arm64 into build/arm64/" @echo " test run 'go test ./...'" @echo " lint run 'golangci-lint run ./...'" @echo " proto regenerate proto/logtailpb/*.pb.go from proto/logtail.proto" @echo " pkg-deb build amd64 + arm64 .deb packages (requires dpkg-deb)" @echo " docker buildx --load two tags ($(IMAGE):v$(VERSION), $(IMAGE):latest) for the native arch" @echo " docker-push buildx multi-arch push (amd64+arm64) to $(IMAGE)" @echo " clean remove build/ and generated proto files" @echo "" @echo " install-deps apt + Go toolchain + Go tools (runs the three sub-targets)" @echo " install-deps-apt apt-install protobuf-compiler, git, make, dpkg-dev, curl, tar" @echo " install-deps-go download Go $(GO_VERSION)+ to /usr/local/go if missing" @echo " install-deps-go-tools go install protoc-gen-go, protoc-gen-go-grpc, golangci-lint" @echo "" @echo "Overridable variables:" @echo " VERSION=$(VERSION) GO_VERSION=$(GO_VERSION) GOLANGCI_LINT_VERSION=$(GOLANGCI_LINT_VERSION)" all: build build: $(GEN_FILES) mkdir -p build/$(NATIVE_ARCH) $(foreach b,$(BINARIES),go build -trimpath -ldflags "$(LDFLAGS)" -o build/$(NATIVE_ARCH)/$(b) ./cmd/$(b) &&) true build-amd64: $(GEN_FILES) mkdir -p build/amd64 $(foreach b,$(BINARIES),GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "$(LDFLAGS)" -o build/amd64/$(b) ./cmd/$(b) &&) true build-arm64: $(GEN_FILES) mkdir -p build/arm64 $(foreach b,$(BINARIES),GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "$(LDFLAGS)" -o build/arm64/$(b) ./cmd/$(b) &&) true test: $(GEN_FILES) go test ./... lint: golangci-lint run ./... proto: $(GEN_FILES) # protoc's go_package option places output at the go_package path (not source-relative). # We invoke protoc from the repo root so the resulting proto/logtailpb/*.pb.go tree # lands alongside the .proto file. $(GEN_FILES): $(PROTO_FILE) protoc \ --go_out=. --go_opt=module=$(MODULE) \ --go-grpc_out=. --go-grpc_opt=module=$(MODULE) \ $(PROTO_FILE) # pkg-deb builds one package per arch; both contain all four static binaries under # /usr/local/bin/ with the nginx-logtail- prefix. Each build- target is a hard # prerequisite — the packaging script refuses to run without build// populated. pkg-deb: build-amd64 build-arm64 debian/build-deb.sh amd64 $(VERSION) debian/build-deb.sh arm64 $(VERSION) # docker — build one image for the native host arch and --load it into the local # daemon. Tagged both :v$(VERSION) and :latest in a single build, so bumping # VERSION is the only change needed to cut a release. docker: docker buildx build --load \ --build-arg VERSION=$(VERSION) \ --build-arg COMMIT=$(COMMIT_HASH) \ --build-arg DATE=$(DATE) \ -t $(IMAGE):v$(VERSION) -t $(IMAGE):latest . # docker-push — build a multi-arch (amd64+arm64) manifest and push it. Buildx # won't --load a multi-platform result, so this is the only path that produces # the combined manifest. Assumes the caller is already logged in to the registry. docker-push: docker buildx build --platform linux/amd64,linux/arm64 --push \ --build-arg VERSION=$(VERSION) \ --build-arg COMMIT=$(COMMIT_HASH) \ --build-arg DATE=$(DATE) \ -t $(IMAGE):v$(VERSION) -t $(IMAGE):latest . clean: rm -rf build/ rm -f $(GEN_FILES) # install-deps is an opt-in "set up a fresh developer box" target. Tested on # Debian Trixie; the apt half should also work on Bookworm and recent Ubuntu LTS. install-deps: install-deps-apt install-deps-go install-deps-go-tools @echo "" @echo "==> All build dependencies installed." @echo " Make sure these are on PATH:" @echo " /usr/local/go/bin (Go toolchain)" @echo " \$$(go env GOPATH)/bin (protoc-gen-go, golangci-lint, ...)" install-deps-apt: @set -eu; \ if [ "$$(id -u)" = 0 ]; then SUDO=""; else SUDO="sudo"; fi; \ echo "==> Installing apt packages (protoc, git, make, dpkg-dev, curl, tar)"; \ $$SUDO apt-get update; \ $$SUDO apt-get install -y --no-install-recommends \ protobuf-compiler git make dpkg-dev ca-certificates curl tar # install-deps-go short-circuits when go env GOVERSION already reports a version # >= GO_VERSION. Otherwise it downloads the official upstream tarball and extracts # it to /usr/local/go. install-deps-go: @set -eu; \ if [ "$$(id -u)" = 0 ]; then SUDO=""; else SUDO="sudo"; fi; \ echo "==> Checking Go toolchain (required: $(GO_VERSION)+)"; \ if command -v go >/dev/null 2>&1; then \ CURRENT=$$(go env GOVERSION 2>/dev/null | sed 's/^go//'); \ OLDEST=$$(printf '%s\n%s\n' "$(GO_VERSION)" "$$CURRENT" | sort -V | head -n1); \ if [ "$$OLDEST" = "$(GO_VERSION)" ] && [ -n "$$CURRENT" ]; then \ echo " go$$CURRENT already installed (>= $(GO_VERSION)), skipping."; \ exit 0; \ fi; \ echo " go$$CURRENT is older than $(GO_VERSION), upgrading."; \ else \ echo " no Go toolchain on PATH, installing."; \ fi; \ DEB_ARCH=$$(dpkg --print-architecture); \ case "$$DEB_ARCH" in \ amd64) GOARCH=amd64 ;; \ arm64) GOARCH=arm64 ;; \ armhf) GOARCH=armv6l ;; \ *) echo " unsupported architecture: $$DEB_ARCH" >&2; exit 1 ;; \ esac; \ TARBALL="go$(GO_VERSION).linux-$$GOARCH.tar.gz"; \ URL="https://go.dev/dl/$$TARBALL"; \ echo " downloading $$URL"; \ curl -fsSL -o "/tmp/$$TARBALL" "$$URL"; \ echo " installing to /usr/local/go"; \ $$SUDO rm -rf /usr/local/go; \ $$SUDO tar -C /usr/local -xzf "/tmp/$$TARBALL"; \ rm -f "/tmp/$$TARBALL"; \ echo " installed $$(/usr/local/go/bin/go version)" install-deps-go-tools: @set -eu; \ if ! command -v go >/dev/null 2>&1; then \ export PATH="/usr/local/go/bin:$$PATH"; \ fi; \ echo "==> Installing Go tools via 'go install'"; \ echo " google.golang.org/protobuf/cmd/protoc-gen-go"; \ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest; \ echo " google.golang.org/grpc/cmd/protoc-gen-go-grpc"; \ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest; \ echo " github.com/golangci/golangci-lint/v2/cmd/golangci-lint"; \ go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest; \ GOBIN="$$(go env GOBIN)"; \ if [ -z "$$GOBIN" ]; then GOBIN="$$(go env GOPATH)/bin"; fi; \ echo "==> Asserting golangci-lint version >= $(GOLANGCI_LINT_VERSION)"; \ if ! "$$GOBIN/golangci-lint" version >/dev/null 2>&1; then \ echo " ERROR: $$GOBIN/golangci-lint is not executable" >&2; \ exit 1; \ fi; \ INSTALLED=$$("$$GOBIN/golangci-lint" version 2>&1 | sed -En 's/.*has version v?([0-9][0-9.]*).*/\1/p' | head -n1); \ if [ -z "$$INSTALLED" ]; then \ echo " ERROR: could not parse golangci-lint version output" >&2; \ "$$GOBIN/golangci-lint" version >&2; \ exit 1; \ fi; \ OLDEST=$$(printf '%s\n%s\n' "$(GOLANGCI_LINT_VERSION)" "$$INSTALLED" | sort -V | head -n1); \ if [ "$$OLDEST" != "$(GOLANGCI_LINT_VERSION)" ]; then \ echo " ERROR: golangci-lint $$INSTALLED is older than the required $(GOLANGCI_LINT_VERSION)" >&2; \ exit 1; \ fi; \ echo " golangci-lint $$INSTALLED (>= $(GOLANGCI_LINT_VERSION)) OK"