-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathMakefile
325 lines (257 loc) · 10.2 KB
/
Makefile
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
SHELL := /usr/bin/env bash -euo pipefail -c
MAKEFLAGS := --jobs=10
PRODUCT_NAME := actions-go-build
DESTDIR ?= /usr/local/bin
# Set AUTOCLEAR=1 to have the terminal cleared before running builds,
# tests, and installs.
CLEAR := $(AUTOCLEAR)
ifeq ($(CLEAR),1)
CLEAR := clear
else
CLEAR :=
endif
SOURCE_ID := .git/source-id
SOURCE_ID_VALUE := $(shell SOURCE_ID=$(SOURCE_ID) ./dev/update-source-id)
# Uncomment to show the source ID.
# $(info SOURCE_ID=$(SOURCE_ID_VALUE))
default: run
ifeq ($(TMPDIR),)
TMPDIR := $(RUNNER_TEMP)
export TMPDIR
endif
ifeq ($(TMPDIR),)
$(error Neither TMPDIR nor RUNNER_TEMP are set.)
endif
TEST_LOG := $(TMPDIR)/go_tests.log
RUN_TESTS_QUIET := @$(MAKE) test > "$(TEST_LOG)" 2>&1 || { cat "$(TEST_LOG)" ; exit 1; }
# Always just install the git hooks unless in CI (GHA sets CI=true as do many CI providers).
ifeq ($(CI),true)
_ := $(shell mkdir -p .git/hooks && cd .git/hooks && ln -fs ../../dev/git_hooks/* .)
endif
ifneq ($(PRODUCT_VERSION),)
CURR_VERSION := $(PRODUCT_VERSION)
else
CURR_VERSION := $(shell cat dev/VERSION)
endif
CURR_VERSION_CL := dev/changes/v$(CURR_VERSION).md
DIRTY := $(shell git diff --exit-code > /dev/null 2>&1 || echo -n "dirty-")
ifneq ($(PRODUCT_REVISION),)
CURR_REVISION := $(PRODUCT_REVISION)
else
CURR_REVISION := $(shell git rev-parse HEAD)
PRODUCT_REVISION := $(CURR_REVISION)
endif
CURR_REVISION := $(DIRTY)$(CURR_REVISION)
PRODUCT_REVISION ?= $(CURR_REVISION)
CLINAME := $(PRODUCT_NAME)
# Release versions of the CLI are built in three phases:
#
# 1) INITIAL_BUILD - No build metadata.
# 2) INTERMEDIATE_BUILD - Some build metadata.
# 3) BOOTSTRAPPED_BUILD - All build metadata.
#
# See comments below for more explanation.
TMP_BASE := $(TMPDIR)/actions-go-build.builds/$(SOURCE_ID_VALUE)
# INITIAL_BUILD is a build of the CLI done using just `go build ...`. This is used to bootstrap
# compiling the CLI using itself, for dogfooding purposes. The INITIAL_BUILD contains none of the
# automatically generated metadata like the version or revision. It is used to build the
# intermediate build...
INITIAL_BUILD := $(TMP_BASE)/initial/$(CLINAME)
# INTERMEDIATE_BUILD is a build of the CLI done using the INITIAL_BUILD build. Because it used
# INITIAL_BUILD (i.e. the code in this repo) to build itself, it contains automatically generated
# metadata like the version and revision. However, it does not contain the metadata about the
# version of actions-go-build that built it because INITIAL_BUILD doesn't have that metadata
# available to inject.
INTERMEDIATE_BUILD := $(TMP_BASE)/intermediate/$(CLINAME)
# BOOTSTRAPPED_BUILD is the final build of the CLI, done using the INTERMEDIATE_BUILD. Because
# INTERMEDIATE_BUILD contains build metadata (e.g. version and revision), it is able to inject
# that information, into this final build as "tool metadata". Thus we can track the provanance of
# this binary just like we are able to with any product binaries also built using this tool.
BOOTSTRAPPED_BUILD := $(TMP_BASE)/bootstrapped/$(CLINAME)
# HOST_PLATFORM_TARGETS are targets that must always produce output compatible with
# the current host platform. We therefore unset the GOOS and GOARCH variable to allow
# the defaults to shine through.
HOST_PLATFORM_TARGETS := $(INITIAL_BUILD) $(INTERMEDIATE_BUILD) test/go
$(HOST_PLATFORM_TARGETS): export GOOS :=
$(HOST_PLATFORM_TARGETS): export GOARCH :=
HOST_PLATFORM_TARGET_ENV := GOOS= GOARCH= OS= ARCH=
#
# Targets
#
test: test/go
.PHONY: test/go
test/go: compile
@$(HOST_PLATFORM_TARGET_ENV) go test $(GO_TEST_FLAGS) ./...
cover: GO_TEST_FLAGS := -coverprofile=coverage.profile
cover: test/go
@go tool cover -html=coverage.profile && rm coverage.profile
test/update: test/go/update
.PHONY: test/go/update
test/go/update: export UPDATE_TESTDATA := true
test/go/update: test/go
@echo "Test data updated."
.PHONY: compile
compile:
@$(CLEAR)
@go build ./...
.PHONY: env
env:
@echo "ENV:"
@echo " PRODUCT_VERSION=$$PRODUCT_VERSION"
@echo " PRODUCT_REVISION=$$PRODUCT_REVISION"
@echo " PRODUCT_REVISION_TIME=$$PRODUCT_REVISION_TIME"
# The targets below invoke the ./dev/build script. Please see that script for more docs
# on how actions-go-build is built using itself.
$(INITIAL_BUILD): $(SOURCE_ID)
@echo "# Running tests..." 1>&2
@$(RUN_TESTS_QUIET) || exit 1
@BIN_PATH="$@" ./dev/build initial > /dev/null || exit 1
RUN_QUIET = OUT="$$($(1) 2>&1)" || { \
echo "Command Failed: $(notdir $(1))"; echo "$$OUT"; exit 1; \
}
$(INTERMEDIATE_BUILD): $(INITIAL_BUILD)
@BIN_PATH="$@" ./dev/build intermediate "$<" > /dev/null || exit 1
$(BOOTSTRAPPED_BUILD): $(INTERMEDIATE_BUILD)
@BIN_PATH="$@" ./dev/build bootstrapped "$<" > /dev/null || exit 1
cli: $(BOOTSTRAPPED_BUILD)
@echo "Build successful."
$(BOOTSTRAPPED_BUILD) --version
.PHONY: install
# Ensure install always targets the host platform.
install: export GOOS :=
install: export GOARCH :=
ifneq ($(GITHUB_PATH),)
# install for GitHub Actions.
install: $(BOOTSTRAPPED_BUILD)
@echo "$(dir $(BOOTSTRAPPED_BUILD))" >> "$(GITHUB_PATH)"
@echo "Command '$(CLINAME)' installed to GITHUB_PATH ($(GITHUB_PATH))"
@export PATH="$$(cat $(GITHUB_PATH))" && $(CLINAME) --version
else
# install for local use.
install: $(BOOTSTRAPPED_BUILD)
@mv "$<" "$(DESTDIR)"
@V="$$($(CLINAME) version -short)" && \
echo "# $(CLINAME) v$$V installed to $(DESTDIR)"
endif
.PHONY: mod/framework/update
mod/framework/update:
@REF="$$(cd ../composite-action-framework-go && make module/ref/head)" && \
go get "$$REF"
# The run/... targets build and then run the CLI itself
# which is usful for quickly seeing its output whilst developing.
.PHONY: run
run: $(INITIAL_BUILD)
@$${QUIET:-false} || $(CLEAR)
@$${QUIET:-false} || echo "\$$ $(notdir $<) $(RUN)"
@$(INITIAL_BUILD) $(RUN)
.PHONY: docs
docs: readme changelog
.PHONY: readme
readme:
@./dev/docs/readme_update
.PHONY: changelog
changelog:
@./dev/docs/changelog_update
.PHONY: changelog/view
changelog/view:
@echo "Current development version: $(CURR_VERSION)"
@echo
@[[ -s "$(CURR_VERSION_CL)" ]] && cat "$(CURR_VERSION_CL)" || echo ' - changelog empty -'
@echo
@echo "Use 'make changelog/add' to edit this version's changelog."
CL_REMINDERS_COMMENT := RECENT COMMITS TO JOG YOUR MEMORY (DELETE THIS SECTION WHEN DONE)...
# changelog/add appends recent commit logs (since the file was last updated)
# to the changelog, and opens it in the editor.
changelog/add:
@echo "<!-- $(CL_REMINDERS_COMMENT)" >> "$(CURR_VERSION_CL)"
@git log $$(git rev-list -1 HEAD "$(CURR_VERSION_CL)")..HEAD >> "$(CURR_VERSION_CL)"
@echo " END $(CL_REMINDERS_COMMENT) -->" >> "$(CURR_VERSION_CL)"
@$(EDITOR) "$(CURR_VERSION_CL)"
@$(MAKE) changelog
@git add CHANGELOG.md "$(CURR_VERSION_CL)" && git commit -m "update changelog for v$(CURR_VERSION)" && \
echo "==> Changelog updated and committed, thanks for keeping it up-to-date!"
.PHONY: debug/docs
debug/docs: export DEBUG := 1
debug/docs: docs
GH := $(shell command -v gh 2> /dev/null)
ifeq ($(GH),)
GH := echo "Please install the [GitHub CLI](https://github.com/cli/cli\#installation)"; \#
else
GH_AUTHED := $(shell gh auth status > /dev/null 2>&1 && echo true)
ifneq ($(GH_AUTHED),true)
GH := echo "Please ensure 'gh auth status' succeeds and try again."; \#
endif
endif
#
# Release build targets
#
# FINAL_BUILD_TARGETS defines all the targets for platform-specific "final" builds.
# It's a macro so that we get a consistent set of targets for each platform.
define FINAL_BUILD_TARGETS
# Inside this define, $(1) is a platform string, like "linux/amd64" or "darwin/arm64"
# build/<platform> does not require a clean worktree and results in a "Development" build.
build/$(1): $$(BOOTSTRAPPED_BUILD)
@./dev/build dev "$$<" "$1" > /dev/null
DEV_BUILDS := $$(DEV_BUILDS) build/$(1)
dist/$1/actions-go-build \
out/actions-go-build_$(CURR_VERSION)_$(subst /,_,$1).zip \
zip/$(1) \
release/build/$(1): $$(BOOTSTRAPPED_BUILD)
@./dev/build release "$$<" "$1" > /dev/null || exit 1
RELEASE_BUILDS := $$(RELEASE_BUILDS) release/build/$(1)
RELEASE_ZIPS := $$(RELEASE_ZIPS) out/actions-go-build_$(CURR_VERSION)_$(subst /,_,$1).zip
endef
# Get the list of supported platforms defined in the build script.
TARGET_PLATFORMS := $(shell ./dev/build platforms)
# For each supported platform, call FINAL_BUILD_TARGETS to create the needed targets.
$(eval $(foreach P,$(TARGET_PLATFORMS),$(call FINAL_BUILD_TARGETS,$(P))))
.PHONY: $(RELEASE_BUILDS)
.PHONY: $(DEV_BUILDS)
# build builds a dev build for each platform.
build: $(DEV_BUILDS)
# release/build builds a release build for each platform.
release/build: $(RELEASE_BUILDS)
release/zips: $(RELEASE_ZIPS)
@for Z in $^; do echo $$Z; done
# release builds zip-packaged builds for each platform.
release: $(RELEASE_ZIPS)
@./dev/release/create $(RELEASE_ZIPS) || exit 1
version: version/check
@LATEST="$(shell $(GH) release list -L 1 --exclude-drafts | grep Latest | cut -f1)"; \
echo "Working on v$(CURR_VERSION) (Latest public release: $$LATEST)"
.PHONY: version
version/check:
@./dev/release/version_check || { \
echo "Tip: run 'make version/set VERSION=<next version>'"; \
exit 1; \
}
.PHONY: version/check
version/set:
@[[ -z "$(VERSION)" ]] && { \
echo "Usage:" && \
echo " make VERSION=<version> version/set" && \
echo "Current Version:" && \
echo " $(CURR_VERSION)" && \
exit 1; \
}; \
./dev/release/set_version "$(VERSION)" && \
$(MAKE) changelog && \
git add dev/VERSION dev/changes/v$(VERSION).md CHANGELOG.md && \
git commit -m "set development version to v$(VERSION)"
.PHONY: version/set
EXAMPLE1 := .github/workflows/example.yml
EXAMPLE1_CURRENT := .github/workflows/example_currentbranch.yml
EXAMPLE2 := .github/workflows/example-matrix.yml
EXAMPLE2_CURRENT := .github/workflows/example-matrix_currentbranch.yml
REPLACEMENTS := -e 's|hashicorp/actions-go-build@main|./|g'
REPLACEMENTS += -e 's|on: \{ push: \{ branches: main \} \}|on: push|g'
REPLACEMENTS += -e 's|main|current branch|g'
define UPDATE_CURRENT_BRANCH_EXAMPLE
@TARGET="$(1).currentbranch.yml" && \
echo "# GENERATED FILE, DO NOT MODIFY; INSTEAD EDIT $(1) AND RUN 'make examples'" > "$$TARGET" && \
sed -E $(REPLACEMENTS) "$(1)" >> "$$TARGET" && \
echo "Example file updated: $$TARGET"
endef
examples:
$(call UPDATE_CURRENT_BRANCH_EXAMPLE,$(EXAMPLE1))
$(call UPDATE_CURRENT_BRANCH_EXAMPLE,$(EXAMPLE2))