Repository navigation
Expand file tree
/
Copy pathcommon.mk
More file actions
444 lines (389 loc) · 20.1 KB
/
Copy pathcommon.mk
File metadata and controls
444 lines (389 loc) · 20.1 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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
# Capture the path to this file before any other includes can shift MAKEFILE_LIST.
_COMMON_MK_PATH := $(lastword $(MAKEFILE_LIST))
# Set MAKEFLAGS to suppress entering/leaving directory messages
MAKEFLAGS += --no-print-directory
BUILD_PATH ?= $(shell pwd)
# DEV_KIT_VERSION must be set by the including Makefile before -include common.mk.
# This fallback is for environments where common.mk is used standalone.
# Must precede ENVTEST_SIDELOAD, which expands it in a target name at parse time.
ifndef DEV_KIT_VERSION
$(warning DEV_KIT_VERSION was not set, using default value "main". Please consider pinning the version to avoid unexpected upgrades.)
DEV_KIT_VERSION := main
endif
LOCALBIN ?= $(BUILD_PATH)/bin
$(LOCALBIN):
mkdir -p $(LOCALBIN)
LOCALGOBIN := $(LOCALBIN)/go
$(LOCALGOBIN): $(LOCALBIN)
mkdir -p $(LOCALGOBIN)
# Binaries provided by flake.nix
FLUX ?= flux
GO ?= go
HELM ?= helm
JQ ?= jq
KIND ?= kind
KUBECTL ?= kubectl
SHELLCHECK ?= shellcheck
TREEFMT ?= treefmt
YQ ?= yq
# External prerequisites (not managed by flake.nix or tools.lock)
GH ?= gh
OS := $(or $(shell $(GO) env GOOS 2>/dev/null), \
$(shell uname -s | tr '[:upper:]' '[:lower:]'))
ARCH := $(or $(shell $(GO) env GOARCH 2>/dev/null), \
$(shell uname -m | sed -E 's/x86_64/amd64/;s/i386|i686/386/;s/aarch64|arm64/arm64/;s/armv7l/arm/'))
# Binaries provided by go install / tools.lock
ADDLICENSE ?= $(LOCALGOBIN)/addlicense
CONTROLLER_GEN ?= $(LOCALGOBIN)/controller-gen
COSIGN ?= $(LOCALGOBIN)/cosign
CRD_REF_DOCS ?= $(LOCALGOBIN)/crd-ref-docs
GINKGO ?= $(LOCALGOBIN)/ginkgo
GOLANGCI_LINT ?= $(LOCALGOBIN)/golangci-lint
HELM_DOCS ?= $(LOCALGOBIN)/helm-docs
OCM ?= $(LOCALGOBIN)/ocm
OPENAPI_GEN ?= $(LOCALGOBIN)/openapi-gen
OSV_SCANNER ?= $(LOCALGOBIN)/osv-scanner
SETUP_ENVTEST ?= $(LOCALGOBIN)/setup-envtest
##@ Repository
# repo-settings — repository configuration, configurable per repository.
# Branch protection: the default branch is always protected; REPO_RULESET_BRANCHES
# adds further branch patterns (a JSON array, e.g. '["release/*"]'; short names are
# normalized to refs/heads/...). REPO_STATUS_CHECKS is a JSON array of status-check
# contexts that must pass (each job name is used as-is, so contexts with spaces work).
# REPO_ALLOW_MERGE_COMMIT/SQUASH_MERGE/REBASE_MERGE configure the merge strategy and
# the ruleset's allowed merge methods; at least one must be `true` (repo-settings fails otherwise).
# REPO_REQUIRE_LAST_PUSH_APPROVAL requires the most recent push to be approved before merging.
# Booleans must be `true` or `false`. Example:
# make repo-settings REPO_ADMIN_BYPASS=false REPO_STATUS_CHECKS='["CI","Check action pins"]' REPO_RULESET_BRANCHES='["release/*"]'
REPO_ADMIN_BYPASS ?= true
REPO_REQUIRED_APPROVING_REVIEW_COUNT ?= 1
REPO_REQUIRE_CODE_OWNER_REVIEW ?= false
REPO_REQUIRE_BRANCH_UP_TO_DATE ?= false
REPO_STATUS_CHECKS ?= []
REPO_RULESET_BRANCHES ?= []
REPO_ALLOW_MERGE_COMMIT ?= true
REPO_ALLOW_SQUASH_MERGE ?= false
REPO_ALLOW_REBASE_MERGE ?= false
REPO_REQUIRE_LAST_PUSH_APPROVAL ?= false
.PHONY: repo-settings
repo-settings: ## Reconcile GitHub repository settings (labels, merge strategy, branch protection, security)
@curl --fail -sSL \
"https://raw.githubusercontent.com/opendefensecloud/dev-kit/$(DEV_KIT_VERSION)/scripts/repo-settings.sh" | \
REPO_ADMIN_BYPASS='$(REPO_ADMIN_BYPASS)' \
REPO_ALLOW_MERGE_COMMIT='$(REPO_ALLOW_MERGE_COMMIT)' \
REPO_ALLOW_REBASE_MERGE='$(REPO_ALLOW_REBASE_MERGE)' \
REPO_ALLOW_SQUASH_MERGE='$(REPO_ALLOW_SQUASH_MERGE)' \
REPO_REQUIRE_BRANCH_UP_TO_DATE='$(REPO_REQUIRE_BRANCH_UP_TO_DATE)' \
REPO_REQUIRE_CODE_OWNER_REVIEW='$(REPO_REQUIRE_CODE_OWNER_REVIEW)' \
REPO_REQUIRED_APPROVING_REVIEW_COUNT='$(REPO_REQUIRED_APPROVING_REVIEW_COUNT)' \
REPO_REQUIRE_LAST_PUSH_APPROVAL='$(REPO_REQUIRE_LAST_PUSH_APPROVAL)' \
REPO_RULESET_BRANCHES='$(REPO_RULESET_BRANCHES)' \
REPO_STATUS_CHECKS='$(REPO_STATUS_CHECKS)' \
GH='$(GH)' JQ='$(JQ)' \
bash
.PHONY: update-action-pins
update-action-pins: ## Update GitHub Action pins to their latest commit SHA
@$(GH) auth status >/dev/null 2>&1 || { echo "error: gh is not authenticated; run 'gh auth login'"; exit 1; }; \
GITHUB_TOKEN=$$(gh auth token) update-action-pins .github/workflows/
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
.PHONY: clean
clean:
-chmod -R u+w $(LOCALBIN)
rm -rf $(LOCALBIN)
.PHONY: shellcheck
shellcheck: ## run shellcheck
@git ls-files -z '*.sh' | xargs -0r $(SHELLCHECK)
OSV_SCANNER_CONFIG ?= ./.osv-scanner.toml
.PHONY: scan
scan: $(OSV_SCANNER) ## scan for vulnerabilities
$(OSV_SCANNER) scan --config $(OSV_SCANNER_CONFIG) -r .
# Files subject to a license header: everything matching `pattern`, minus
# generated files.
addlicense_files = git ls-files '$(pattern)' | (xargs -r grep -L -E '^(//|\#) Code generated .* DO NOT EDIT\.$$' || true)
# The SPDX identifier expected in the header, and what addlicense writes for
# `license=apache`. Override `spdx` should a repository ever use another one.
spdx ?= Apache-2.0
# addlicense with -check, only ever sees a *missing* header, never an outdated
# one, and it never inspects the SPDX line at all;
# `|| true` because grep -L exits 1 when it selects nothing, which
# .SHELLFLAGS = -ec would make fatal.
addlicense_mismatch = $$({ $(addlicense_files) | xargs -r grep -L -F 'Copyright $(comment)' || true; \
$(addlicense_files) | xargs -r grep -L -F 'SPDX-License-Identifier: $(spdx)' || true; } | sort -u)
.PHONY: addlicense
addlicense: $(ADDLICENSE) ## Add License headers containing of `license` and `comment` to files matched by `pattern`.
@test -n "$(license)" && test -n "$(comment)" && test -n "$(pattern)" && \
$(addlicense_files) | xargs -r $(ADDLICENSE) -c '$(comment)' -l '$(license)' -s=only -y '' $(extraargs)
.PHONY: addlicense-list
addlicense-list: ## List files matched by `pattern` whose header does not carry `Copyright $(comment)` and `SPDX-License-Identifier: $(spdx)`.
@test -n "$(comment)" && test -n "$(pattern)"
@files=$(addlicense_mismatch); \
if [ -n "$$files" ]; then \
echo "Unexpected license header, expected 'Copyright $(comment)' and 'SPDX-License-Identifier: $(spdx)':"; \
echo "$$files" | sed 's/^/ /'; \
fi
.PHONY: addlicense-check
addlicense-check:
$(MAKE) addlicense extraargs='-check'
@$(MAKE) addlicense-list
@test -z "$(addlicense_mismatch)"
# Local dev environment
.PHONY: setup-local-cluster
setup-local-cluster: ## Set up a Kind cluster for local development if it does not exist
@command -v $(KIND) >/dev/null 2>&1 || { \
echo "Kind is not installed. Please install Kind manually."; \
exit 1; \
}
@case "$$($(KIND) get clusters)" in \
*"$(KIND_CLUSTER)"*) \
echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
*) \
echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
$(KIND) create cluster --name $(KIND_CLUSTER) $(if $(KIND_CONFIG),--config $(KIND_CONFIG)) ;; \
esac
##@ Formatting and linting
# Shared configuration shipped from dev-kit, fetched only when absent.
# The list is static and each file's decision is made inside the recipe, when
# the rule actually runs.
DEV_KIT_CONFIGS ?= .editorconfig .golangci.yml treefmt.toml
# golangci-lint needs only its own config, so that `lint` with formatting off
# never pulls .editorconfig or treefmt.toml in as untracked files. Empty when
# the repo opted out of fetching it, since no rule then exists to make it.
DEV_KIT_GOLANGCI_CONFIG := $(filter .golangci.yml,$(DEV_KIT_CONFIGS))
# Records what was fetched, so a version bump only ever removes files this
# mechanism created itself, never a hand-written override not yet committed.
DEV_KIT_CONFIGS_MARKER := .common.mk-configs
# A repository overrides a file by committing its own copy, including under the
# alternate spellings the org already uses: treefmt reads treefmt.toml before
# .treefmt.toml, and an untracked .golangci.yml alongside a committed
# .golangci.yaml would trip diff-check.
$(DEV_KIT_CONFIGS):
@case '$@' in \
.golangci.yml) \
if [ -e .golangci.yaml ] || [ ! -e go.mod ]; then exit 0; fi ;; \
treefmt.toml) \
if [ -e .treefmt.toml ]; then exit 0; fi ;; \
esac; \
curl --fail -sSL \
'https://raw.githubusercontent.com/opendefensecloud/dev-kit/$(DEV_KIT_VERSION)/$@' \
-o '$@.dev-kit-download'; \
mv '$@.dev-kit-download' '$@'; \
if ! grep -qxF '$@' $(DEV_KIT_CONFIGS_MARKER) 2>/dev/null; then \
echo '$@' >> $(DEV_KIT_CONFIGS_MARKER); \
fi; \
echo "fetched $@ from dev-kit $(DEV_KIT_VERSION)" >&2
# treefmt drives gofmt (via golangci-lint), yamlfmt, shfmt and mdformat from one
# config. LOCALGOBIN goes on PATH so it picks up the pinned golangci-lint from
# tools.lock rather than whatever happens to be installed.
# A repository with no go.mod has no golangci-lint to install.
TREEFMT_ARGS ?= $(if $(wildcard go.mod),,--allow-missing-formatter)
_TREEFMT_GO_TOOL := $(if $(wildcard go.mod),$(GOLANGCI_LINT))
# The formatters come from the dev-kit flake, which consumers pin separately
# from DEV_KIT_VERSION. Bumping the Makefile without bumping flake.lock is a
# common mistake, and the bare "command not found" does not say so.
.PHONY: _require-formatters
_require-formatters:
@missing=; \
for tool in $(TREEFMT) yamlfmt shfmt mdformat; do \
command -v "$$tool" >/dev/null 2>&1 || missing="$$missing $$tool"; \
done; \
if [ -n "$$missing" ]; then \
echo "error: missing formatter(s):$$missing" >&2; \
echo " they ship with the dev-kit flake. If flake.nix pins a dev-kit" >&2; \
echo " tag, bump it to match DEV_KIT_VERSION ($(DEV_KIT_VERSION)); otherwise run" >&2; \
echo " 'nix flake update dev-kit'. Commit flake.lock, then 'direnv allow'." >&2; \
exit 1; \
fi
# Order-only prerequisites (after the |) fetch the configs without making them
# part of the up-to-date check.
.PHONY: fmt-all
fmt-all: _require-formatters $(_TREEFMT_GO_TOOL) | $(DEV_KIT_CONFIGS) ## Format every file in the repository
@PATH="$(LOCALGOBIN):$$PATH" $(TREEFMT) $(TREEFMT_ARGS)
.PHONY: lint-all
lint-all: _require-formatters $(_TREEFMT_GO_TOOL) | $(DEV_KIT_CONFIGS) ## Fail if any file is not formatted
@PATH="$(LOCALGOBIN):$$PATH" $(TREEFMT) --ci $(TREEFMT_ARGS)
# Formatting is opt-in. A repository that still defines its own `fmt:`/`lint:`
# recipe would otherwise have fmt-all/lint-all merged in as prerequisites
# silently on the next DEV_KIT_VERSION bump, and start failing on every YAML,
# Markdown and shell file it has never formatted. Set this in the project
# Makefile *before* `-include common.mk`, next to DEV_KIT_VERSION, after running
# the one-time `make fmt-all` commit:
#
# DEV_KIT_FORMATTING := on
#
# With it off, `lint` behaves exactly as it did before this mechanism existed.
DEV_KIT_FORMATTING ?= off
ifeq ($(filter on off,$(DEV_KIT_FORMATTING)),)
$(error DEV_KIT_FORMATTING must be 'on' or 'off', got '$(DEV_KIT_FORMATTING)')
endif
_fmt_enabled = $(filter on,$(DEV_KIT_FORMATTING))
# Public entry points. These names are what the flake git-hooks and every
# workflow call, so they stay stable. A repository extends them by adding
# prerequisites rather than redefining them.
.PHONY: fmt
fmt: $(if $(_fmt_enabled),fmt-all) ## Format code
.PHONY: lint
lint: $(if $(_fmt_enabled),lint-all) shellcheck $(if $(wildcard go.mod),golangci-lint) ## Check formatting and run all linters
##@ Common golang targets
.PHONY: mod
mod: ## run go mod tidy, download, verify
@$(GO) mod tidy
@$(GO) mod download
@$(GO) mod verify
# Order-only on the config so a direct `make golangci-lint` on a fresh clone, or
# a parallel `make -j lint`, cannot run before .golangci.yml is there and
# silently lint with golangci-lint's own defaults.
.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) | $(DEV_KIT_GOLANGCI_CONFIG) ## run golangci-lint
$(GOLANGCI_LINT) run -v
# Cached rather than piped to bash like repo-settings: this is a `make test`
# prerequisite and must not need the network once the envtest cache is warm.
# subst: DEV_KIT_VERSION may be a ref (refs/heads/main); slashes would become
# directory separators. `~` is illegal in a git ref, so the mapping cannot
# collide. tmp+mv: curl --fail leaves partial files on a dropped
# transfer and there is no .DELETE_ON_ERROR. Order-only: $(LOCALBIN)'s mtime is
# bumped by this very rename, so a normal prerequisite re-downloads every run.
# ponytail: floating DEV_KIT_VERSION pins the script until `make clean`; pin a tag.
ENVTEST_SIDELOAD := $(LOCALBIN)/envtest-sideload-$(subst /,~,$(DEV_KIT_VERSION)).sh
$(ENVTEST_SIDELOAD): | $(LOCALBIN)
@curl --fail -sSL \
"https://raw.githubusercontent.com/opendefensecloud/dev-kit/$(DEV_KIT_VERSION)/scripts/envtest-sideload.sh" \
-o $@.download
@mv $@.download $@
# ENVTEST_K8S_VERSION is deliberately not defaulted here: consumers set it with
# ?= *after* `-include common.mk`, so a default here would silently win.
.PHONY: envtest-binaries-sideload
envtest-binaries-sideload: $(SETUP_ENVTEST) $(ENVTEST_SIDELOAD) ## Populate the envtest cache for ENVTEST_K8S_VERSION from upstream K8s/etcd releases when controller-tools hasn't packaged it. No-op if already cached.
@test -n "$(ENVTEST_K8S_VERSION)" || { echo "error: ENVTEST_K8S_VERSION is not set" >&2; exit 1; }
@SETUP_ENVTEST=$(SETUP_ENVTEST) BIN_DIR=$(LOCALBIN) YQ=$(YQ) \
bash $(ENVTEST_SIDELOAD) $(ENVTEST_K8S_VERSION)
# Cached rather than piped to bash like repo-settings: this runs on the lint
# path, so it must not reach the network on every invocation.
CHECK_GO_VERSION := $(LOCALBIN)/check-go-version-$(subst /,~,$(DEV_KIT_VERSION)).sh
$(CHECK_GO_VERSION): | $(LOCALBIN)
@curl --fail -sSL \
"https://raw.githubusercontent.com/opendefensecloud/dev-kit/$(DEV_KIT_VERSION)/scripts/check-go-version.sh" \
-o $@.download
@mv $@.download $@
# Overridable so a repository with a non-default layout can point at its own
# files; an absent file is skipped rather than failing.
GO_MOD ?= $(BUILD_PATH)/go.mod
FLAKE_NIX ?= $(BUILD_PATH)/flake.nix
# Empty by default: the script then reads every tracked Dockerfile. Set this to
# a space-separated list to override.
DOCKERFILE ?=
.PHONY: check-go-version
check-go-version: $(CHECK_GO_VERSION) ## Assert go.mod, flake.nix and the Dockerfile pin the same Go version
@GO_MOD='$(GO_MOD)' FLAKE_NIX='$(FLAKE_NIX)' DOCKERFILE='$(DOCKERFILE)' \
bash $(CHECK_GO_VERSION)
CHECK_DEV_KIT_PINS := $(LOCALBIN)/check-dev-kit-pins-$(subst /,~,$(DEV_KIT_VERSION)).sh
$(CHECK_DEV_KIT_PINS): | $(LOCALBIN)
@curl --fail -sSL \
"https://raw.githubusercontent.com/opendefensecloud/dev-kit/$(DEV_KIT_VERSION)/scripts/check-dev-kit-pins.sh" \
-o $@.download
@mv $@.download $@
WORKFLOWS ?= $(BUILD_PATH)/.github/workflows
FLAKE_LOCK ?= $(BUILD_PATH)/flake.lock
# Named DEV_KIT_MAKEFILE rather than MAKEFILE, which make treats specially.
DEV_KIT_MAKEFILE ?= $(BUILD_PATH)/Makefile
.PHONY: check-dev-kit-pins
check-dev-kit-pins: $(CHECK_DEV_KIT_PINS) ## Assert every dev-kit reference in this repository resolves to the same release
@WORKFLOWS='$(WORKFLOWS)' DEV_KIT_MAKEFILE='$(DEV_KIT_MAKEFILE)' \
FLAKE_NIX='$(FLAKE_NIX)' FLAKE_LOCK='$(FLAKE_LOCK)' \
bash $(CHECK_DEV_KIT_PINS)
# Install local tools
TOOL_LOCK := $(BUILD_PATH)/tools.lock
.PHONY: $(filter $(LOCALGOBIN)/%,$(MAKECMDGOALS))
$(LOCALGOBIN)/%: $(LOCALGOBIN) $(TOOL_LOCK)
@toolname=$(notdir $@); \
module=$$(awk "/^$$toolname / {print \$$2}" $(TOOL_LOCK)); \
version=$$(cut -d@ -f2 <<< $$module); \
test -s $(LOCALGOBIN)/$$toolname && grep -q "$$version" $(LOCALGOBIN)/.$$toolname-version 2>/dev/null || \
(GOBIN=$(LOCALGOBIN) $(GO) install $$module && echo $$version > $(LOCALGOBIN)/.$$toolname-version)
# ocm cli (sdk v1) cannot be installed with go install because of replace directives in go.mod
$(LOCALGOBIN)/ocm: $(LOCALGOBIN) $(TOOL_LOCK)
@module=$$(awk "/^ocm / {print \$$2}" $(TOOL_LOCK)); \
version=$$(cut -d@ -f2 <<< $$module); \
test -s $@ && grep -q "$$version" $(LOCALGOBIN)/.ocm-version 2>/dev/null || \
curl -s https://ocm.software/install.sh | VERSION_OCM=$$version bash -s -- $(LOCALGOBIN) && echo $$version > $(LOCALGOBIN)/.ocm-version
# Rewrites the common.mk: rule in the calling project's Makefile to the current bootstrap format.
# Run once per repository to migrate from any older recipe shape.
.PHONY: update-common-mk-bootstrap
update-common-mk-bootstrap: ## Rewrite the common.mk: rule in Makefile to the current bootstrap format
@tmp=Makefile.tmp; \
awk ' \
BEGIN { found = 0 } \
/^common\.mk:$$/ { \
found = 1; \
print; \
print "\t@[ -f .common.mk-download ] || \\"; \
print "\t\tcurl --fail -sSL https://raw.githubusercontent.com/opendefensecloud/dev-kit/$$(DEV_KIT_VERSION)/common.mk \\"; \
print "\t\t -o .common.mk-download"; \
print "\tmv .common.mk-download $$@"; \
print "\tprintf \047%s\047 \047$$(DEV_KIT_VERSION)\047 > .common.mk-version"; \
print "\ttouch .common.mk-checked"; \
skip = 1; next \
} \
skip && /^\t/ { next } \
{ skip = 0; print } \
END { exit(found ? 0 : 1) } \
' Makefile > "$$tmp" && mv "$$tmp" Makefile || { rm -f "$$tmp"; echo "error: common.mk: rule not found in Makefile" >&2; exit 1; }
@echo "Updated common.mk: bootstrap in Makefile"
# ── Self-update ────────────────────────────────────────────────────────────────
# The DEV_KIT_VERSION fallback lives near the top of this file, because targets
# that embed it in a *target name* (see ENVTEST_SIDELOAD) need it at parse time.
# Performs a content-based staleness check at most once per hour.
# If the remote content differs from the local file, deletes this file so that
# Make's include-file-remake mechanism triggers the project's common.mk: rule on
# its next restart — picking up the pre-downloaded .common.mk-download file.
# Skipped when common.mk is tracked by git: that means this repository is either
# dev-kit itself, where common.mk is the source file the check would delete, or a
# consumer that deliberately committed a pinned copy.
_COMMON_MK_SELF_UPDATE := $(shell \
if git ls-files --error-unmatch '$(_COMMON_MK_PATH)' >/dev/null 2>&1; then exit 0; fi; \
hash_cmd=$$(command -v sha256sum >/dev/null 2>&1 && echo "sha256sum" || echo "shasum -a 256"); \
stored=$$(cat .common.mk-version 2>/dev/null); \
if [ "$$stored" != "$(DEV_KIT_VERSION)" ]; then \
rm -f .common.mk-checked .common.mk-download; \
if [ -f $(DEV_KIT_CONFIGS_MARKER) ]; then \
while read -r f; do \
[ -n "$$f" ] || continue; \
git ls-files --error-unmatch "$$f" >/dev/null 2>&1 || rm -f "$$f"; \
done < $(DEV_KIT_CONFIGS_MARKER); \
rm -f $(DEV_KIT_CONFIGS_MARKER); \
fi; \
elif find .common.mk-checked -mmin -60 2>/dev/null | grep -q .; then \
exit 0; \
fi; \
if curl --fail -sSL \
'https://raw.githubusercontent.com/opendefensecloud/dev-kit/$(DEV_KIT_VERSION)/common.mk' \
-o .common.mk-download 2>/dev/null; then \
remote=$$($$hash_cmd .common.mk-download | cut -d' ' -f1); \
local_hash=$$($$hash_cmd '$(_COMMON_MK_PATH)' 2>/dev/null | cut -d' ' -f1); \
printf '%s' '$(DEV_KIT_VERSION)' > .common.mk-version; \
if [ "$$remote" = "$$local_hash" ]; then \
rm -f .common.mk-download; \
touch .common.mk-checked; \
else \
rm -f '$(_COMMON_MK_PATH)'; \
fi; \
else \
echo >&2 'warning: could not fetch common.mk update, using cached version'; \
touch .common.mk-checked; \
fi)