diff --git a/Makefile b/Makefile index 2ec404ee..69156267 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .DEFAULT_GOAL := verify -.PHONY: build build-controller build-garm-derivative fmt-check garm-derivative-script test test-race vet verify +.PHONY: build build-controller controller-release build-garm-derivative fmt-check garm-derivative-script test test-controller-release-manifest test-race vet verify # The controller, observer, cache broker and pressure observer that ship to # the hosts. Stamped, because an unstamped binary reports `version: dev, @@ -31,9 +31,19 @@ build-controller: CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags "$(CONTROLLER_LDFLAGS)" -o dist/gha-fleet-observer ./cmd/gha-fleet-observer CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags "$(CONTROLLER_LDFLAGS)" -o dist/gha-cache-broker ./cmd/gha-cache-broker CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags "$(CONTROLLER_LDFLAGS)" -o dist/gha-pressure-observer ./cmd/gha-pressure-observer + CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags "$(CONTROLLER_LDFLAGS)" -o dist/gha-diagnostic-exporter ./cmd/gha-diagnostic-exporter @./dist/gha-fleet version + @./dist/gha-fleet-observer --version @./dist/gha-cache-broker -version @./dist/gha-pressure-observer --version + @./dist/gha-diagnostic-exporter --version + +# The only supported deployable controller build. The manifest binds every +# artifact to the exact source tree and build inputs consumed by the estate. +controller-release: build-controller + @./scripts/write-controller-release-manifest.sh \ + "$(CONTROLLER_VERSION)" "$(CONTROLLER_COMMIT)" dist/controller-release.json dist/gha-fleet \ + dist/gha-fleet-observer dist/gha-cache-broker dist/gha-pressure-observer dist/gha-diagnostic-exporter garm-derivative-script: go run ./cmd/gha-fleet render-garm-build @@ -50,10 +60,13 @@ fmt-check: test: go test ./... +test-controller-release-manifest: + ./scripts/test-controller-release-manifest.sh + test-race: go test -race ./... vet: go vet ./... -verify: fmt-check vet test-race build +verify: fmt-check vet test-race test-controller-release-manifest build diff --git a/scripts/test-controller-release-manifest.sh b/scripts/test-controller-release-manifest.sh new file mode 100755 index 00000000..cf43870e --- /dev/null +++ b/scripts/test-controller-release-manifest.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +repo_root=$(git rev-parse --show-toplevel) +scratch=$(mktemp -d) +trap 'rm -rf -- "${scratch}"' EXIT + +printf '#!/usr/bin/env bash\nexit 0\n' >"${scratch}/one" +printf '#!/usr/bin/env bash\nexit 0\n# two\n' >"${scratch}/two" +chmod +x "${scratch}/one" "${scratch}/two" + +"${repo_root}/scripts/write-controller-release-manifest.sh" \ + v1.2.3 0123456789abcdef0123456789abcdef01234567 "${scratch}/manifest.json" \ + "${scratch}/one" "${scratch}/two" + +python3 - "${scratch}/manifest.json" "${scratch}/one" "${scratch}/two" <<'PY' +import hashlib +import json +from pathlib import Path +import sys + +manifest = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) +assert manifest["schema_version"] == 1 +assert manifest["version"] == "v1.2.3" +assert manifest["source_commit"] == "0123456789abcdef0123456789abcdef01234567" +assert manifest["build"]["cgo_enabled"] is False +assert manifest["build"]["trimpath"] is True +assert manifest["build"]["buildvcs"] is False +for path_text in sys.argv[2:]: + path = Path(path_text) + digest = hashlib.sha256(path.read_bytes()).hexdigest() + assert manifest["binaries"][path.name] == f"sha256:{digest}" +PY diff --git a/scripts/write-controller-release-manifest.sh b/scripts/write-controller-release-manifest.sh new file mode 100755 index 00000000..32a87a5d --- /dev/null +++ b/scripts/write-controller-release-manifest.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +version=${1:?usage: write-controller-release-manifest.sh ...} +commit=${2:?usage: write-controller-release-manifest.sh ...} +output=${3:?usage: write-controller-release-manifest.sh ...} +shift 3 + +(( $# > 0 )) || { echo "controller release has no binaries" >&2; exit 1; } +[[ "${commit}" =~ ^[0-9a-f]{40}$ ]] || { echo "invalid controller source commit: ${commit}" >&2; exit 1; } + +for binary in "$@"; do + [[ -x "${binary}" ]] || { echo "controller binary is absent or not executable: ${binary}" >&2; exit 1; } +done + +temporary="${output}.tmp" +trap 'rm -f -- "${temporary}"' EXIT + +{ + printf '{\n' + printf ' "schema_version": 1,\n' + printf ' "version": "%s",\n' "${version}" + printf ' "source_commit": "%s",\n' "${commit}" + printf ' "build": {\n' + printf ' "go_version": "%s",\n' "$(go env GOVERSION)" + printf ' "goos": "%s",\n' "$(go env GOOS)" + printf ' "goarch": "%s",\n' "$(go env GOARCH)" + printf ' "cgo_enabled": false,\n' + printf ' "trimpath": true,\n' + printf ' "buildvcs": false,\n' + printf ' "build_id": ""\n' + printf ' },\n' + printf ' "binaries": {\n' + index=0 + count=$# + for binary in "$@"; do + index=$((index + 1)) + separator=, + if (( index == count )); then separator=; fi + printf ' "%s": "sha256:%s"%s\n' "$(basename -- "${binary}")" "$(sha256sum "${binary}" | awk '{print $1}')" "${separator}" + done + printf ' }\n' + printf '}\n' +} >"${temporary}" + +mv -- "${temporary}" "${output}" +trap - EXIT