Repository navigation
Expand file tree
/
Copy pathmakefile
More file actions
178 lines (146 loc) · 9.31 KB
/
Copy pathmakefile
File metadata and controls
178 lines (146 loc) · 9.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# plain `make` is fmt + build: without this the first tool rule below would be the default
.DEFAULT_GOAL := default
# recipes use bash for pipefail support (ubuntu's default sh is dash)
SHELL := /bin/bash
GIT_COMMIT=$(shell git describe --always --long --dirty 2>/dev/null || echo none)
# the version every katbyte tool reports: the tag, then +commits@ghash from git describe (v0.5.0-17-gc26e2f3 -> v0.5.0+17@gc26e2f3),
# -dirty when the tree is; an untagged repo counts from v0.0.0 in the same shape rather than falling back to go's pseudo-version
GIT_VERSION=$(shell git describe --tags --dirty 2>/dev/null | sed 's/-\([0-9]*\)-g/+\1@g/' | grep . || \
echo "v0.0.0+$$(git rev-list --count HEAD 2>/dev/null || echo 0)@g$$(git rev-parse --short HEAD 2>/dev/null || echo none)$$(git diff --quiet 2>/dev/null || echo -dirty)")
TEST_TIMEOUT?=15m
LDFLAGS=-X github.com/katbyte/go-kt/version.GitCommit=${GIT_COMMIT} -X github.com/katbyte/go-kt/version.Version=${GIT_VERSION}
# dev tool binaries are built into .tools/bin (gitignored) from the versions pinned in
# .tools/go.mod - the single source of truth for make and CI; dependabot keeps them updated
TOOLS_BIN=.tools/bin
ACTIONLINT=$(TOOLS_BIN)/actionlint
GOFUMPT=$(TOOLS_BIN)/gofumpt
GOLANGCI_LINT=$(TOOLS_BIN)/golangci-lint
# the tools module is not vendored (prawn's own dependencies are): GOFLAGS is cleared for it, so a
# machine with GOFLAGS=-mod=vendor set still builds them rather than looking for .tools/vendor
TOOLS_ENV=GOFLAGS=
# non-Go tools also live in .tools/bin at pinned versions, but the pins are here (dependabot
# cannot bump them): shellcheck, typos and zizmor are static binaries downloaded from their github releases,
# yamllint is python installed into a repo-local venv. all rebuild when this makefile changes.
# github release downloads occasionally 5xx; retry rather than fail the run on the first hiccup
CURL=curl -sSfL --retry 5 --retry-delay 2 --retry-all-errors
SHELLCHECK_VERSION=v0.11.0
TYPOS_VERSION=v1.50.1
YAMLLINT_VERSION=1.38.0
ZIZMOR_VERSION=v1.30.1
SHELLCHECK=$(TOOLS_BIN)/shellcheck
TYPOS=$(TOOLS_BIN)/typos
YAMLLINT=$(TOOLS_BIN)/yamllint
ZIZMOR=$(TOOLS_BIN)/zizmor
# golangci-lint with the azproviderlint module plugin compiled in (.tools/.custom-gcl.yml);
# lint runs use this binary, the plain go.mod one exists to bootstrap `golangci-lint custom`
GOLANGCI_LINT_MODULES=$(TOOLS_BIN)/golangci-with-modules
# one rule builds any Go tool: the import path comes from the tool directives in .tools/go.mod
# (via go list tool), so the makefile never repeats it - add a tool there and a variable above
$(TOOLS_BIN)/%: .tools/go.mod .tools/go.sum
@echo "==> building $* (version pinned in .tools/go.mod)..."
@cd .tools && $(TOOLS_ENV) go build -o bin/$* $$($(TOOLS_ENV) go list tool | grep "/$*$$")
# explicit rules take precedence over the pattern rule above for the non-Go tools
$(GOLANGCI_LINT_MODULES): .tools/.custom-gcl.yml $(GOLANGCI_LINT)
@echo "==> building golangci-lint with plugins (versions pinned in .tools/.custom-gcl.yml)..."
@cd .tools && $(TOOLS_ENV) bin/golangci-lint custom
$(SHELLCHECK): makefile
@echo "==> downloading shellcheck $(SHELLCHECK_VERSION)..."
@mkdir -p $(TOOLS_BIN)
@os=$$(uname | tr 'A-Z' 'a-z'); arch=$$(uname -m); [ "$$arch" = "arm64" ] && arch=aarch64; \
$(CURL) "https://github.com/koalaman/shellcheck/releases/download/$(SHELLCHECK_VERSION)/shellcheck-$(SHELLCHECK_VERSION).$$os.$$arch.tar.xz" \
| tar -xJ -O shellcheck-$(SHELLCHECK_VERSION)/shellcheck > $@ && chmod +x $@
$(TYPOS): makefile
@echo "==> downloading typos $(TYPOS_VERSION)..."
@mkdir -p $(TOOLS_BIN)
@case "$$(uname)" in Darwin) target=apple-darwin;; *) target=unknown-linux-musl;; esac; \
arch=$$(uname -m); [ "$$arch" = "arm64" ] && arch=aarch64; \
$(CURL) "https://github.com/crate-ci/typos/releases/download/$(TYPOS_VERSION)/typos-$(TYPOS_VERSION)-$$arch-$$target.tar.gz" \
| tar -xz -O ./typos > $@ && chmod +x $@
$(YAMLLINT): makefile
@command -v python3 >/dev/null || (echo "python3 is required to install yamllint (macOS: xcode CLT; Debian/Ubuntu: apt install python3-venv)" && exit 1)
@echo "==> installing yamllint $(YAMLLINT_VERSION) into .tools/venv..."
@mkdir -p $(TOOLS_BIN)
@python3 -m venv .tools/venv && .tools/venv/bin/pip install -q yamllint==$(YAMLLINT_VERSION) && ln -sf ../venv/bin/yamllint $@
$(ZIZMOR): makefile
@echo "==> downloading zizmor $(ZIZMOR_VERSION)..."
@mkdir -p $(TOOLS_BIN)
@case "$$(uname)" in Darwin) target=apple-darwin;; *) target=unknown-linux-gnu;; esac; \
arch=$$(uname -m); [ "$$arch" = "arm64" ] && arch=aarch64; \
curl -sSfL "https://github.com/zizmorcore/zizmor/releases/download/$(ZIZMOR_VERSION)/zizmor-$$arch-$$target.tar.gz" \
| tar -xz -O zizmor > $@ && chmod +x $@
# a download that fails part way must not leave a truncated binary that later looks up to date
.DELETE_ON_ERROR:
default: fmt build
all: fmt build
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m<target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-24s\033[0m%s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
##@ Build
build: ## Compile prawn with version info from git
@echo "==> building..."
go build -o prawn -ldflags "${LDFLAGS}"
docker: ## Build the prawn docker image, tagged as the published name so `docker compose up` runs it
docker build --tag prawn --tag ghcr.io/katbyte/prawn:latest --build-arg GO_VERSION=$(shell cat .go-version) \
--build-arg VERSION=${GIT_VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT} .
install: ## Install prawn into GOPATH/bin with version info from git
@echo "==> installing..."
go install -ldflags "${LDFLAGS}" .
tools: $(ACTIONLINT) $(GOFUMPT) $(GOLANGCI_LINT) $(GOLANGCI_LINT_MODULES) $(SHELLCHECK) $(TYPOS) $(YAMLLINT) $(ZIZMOR) ## Install all pinned dev tools into .tools/bin
##@ Formatting
fmt: $(GOFUMPT) $(GOLANGCI_LINT) ## Fix Go formatting (gofmt, gofumpt, goimports)
@echo "==> Fixing source code with gofmt..."
find . -name '*.go' | grep -v vendor | xargs gofmt -s -w
@echo "==> Fixing source code with gofumpt..."
find . -name '*.go' | grep -v vendor | xargs $(GOFUMPT) -w
@echo "==> Fixing imports with golangci-lint (goimports)..."
$(GOLANGCI_LINT) fmt -E goimports ./...
goimports: $(GOLANGCI_LINT) ## Fix imports with golangci-lint (goimports)
@echo "==> Fixing imports with golangci-lint (goimports)..."
$(GOLANGCI_LINT) fmt -E goimports ./...
##@ Linting & Dependencies
lint: $(GOLANGCI_LINT_MODULES) ## Check source code with the golangci linters (incl. azproviderlint)
@echo "==> Checking source code against linters..."
$(GOLANGCI_LINT_MODULES) run ./...
actionlint: $(ACTIONLINT) $(SHELLCHECK) ## Check GitHub workflows with actionlint (incl. shellcheck on run blocks)
@echo "==> Checking workflows with actionlint..."
@$(ACTIONLINT) -shellcheck=$(SHELLCHECK)
lint-fix: $(GOLANGCI_LINT_MODULES) ## Fix source code with all golangci linters
@echo "==> Checking source code against linters (applying autofixes)..."
$(GOLANGCI_LINT_MODULES) run --fix ./...
yamllint: $(YAMLLINT) ## Check YAML files with yamllint (config in .yamllint.yml)
@echo "==> Checking YAML files with yamllint..."
@$(YAMLLINT) -s .
shellcheck: $(SHELLCHECK) ## Check shell scripts with shellcheck
@echo "==> Checking shell scripts with shellcheck..."
@$(SHELLCHECK) scripts/*.sh
typos: $(TYPOS) ## Check all files for spelling mistakes with typos (config in .typos.toml)
@echo "==> Checking for typos..."
@$(TYPOS)
typos-fix: $(TYPOS) ## Fix spelling mistakes found by typos
@echo "==> Fixing typos..."
@$(TYPOS) --write-changes
zizmor: $(ZIZMOR) ## Audit GitHub workflows for security issues with zizmor
@echo "==> Auditing workflows with zizmor..."
@$(ZIZMOR) .
depscheck: ## Check that go.mod/go.sum and vendor/ are in sync
@echo "==> Checking source code with go mod tidy..."
@go mod tidy
@git diff --exit-code -- go.mod go.sum || \
(echo; echo "Unexpected difference in go.mod/go.sum files. Run 'go mod tidy' command or revert any go.mod/go.sum changes and commit."; exit 1)
@echo "==> Checking source code with go mod vendor..."
@go mod vendor
@git diff --compact-summary --exit-code -- vendor || \
(echo; echo "Unexpected difference in vendor/ directory. Run 'go mod vendor' command or revert any go.mod/go.sum/vendor changes and commit."; exit 1)
@echo "==> Checking .tools/go.mod with go mod tidy..."
@cd .tools && $(TOOLS_ENV) go mod tidy
@git diff --exit-code -- .tools/go.mod .tools/go.sum || \
(echo; echo "Unexpected difference in .tools/go.mod/go.sum. Run 'cd .tools && go mod tidy' and commit."; exit 1)
@echo "==> Checking .tools/.custom-gcl.yml golangci-lint version matches .tools/go.mod..."
@modv=$$(cd .tools && $(TOOLS_ENV) go list -m -f '{{.Version}}' github.com/golangci/golangci-lint/v2); \
gclv=$$(grep '^version:' .tools/.custom-gcl.yml | awk '{print $$2}'); \
[ "$$modv" = "$$gclv" ] || \
(echo; echo "golangci-lint version mismatch: .tools/go.mod has $$modv but .tools/.custom-gcl.yml has $$gclv - update .custom-gcl.yml to match."; exit 1)
##@ Testing
test: build ## Run the tests under the race detector
go test -race $$(go list ./... | grep -v vendor) -timeout ${TEST_TIMEOUT}
check-all: build test lint actionlint yamllint shellcheck typos depscheck ## Run build + test + all linters + depscheck
.PHONY: default all help fmt goimports build docker lint lint-fix actionlint yamllint shellcheck typos typos-fix zizmor depscheck check-all install tools test