diff --git a/Dockerfile b/Dockerfile index 26a9592..4ca4ac3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,9 +8,6 @@ RUN go mod download COPY . . -# Touch .pb.go so make does not try to regenerate via protoc (file is committed) -RUN touch mfer/mf.pb.go - # Go half of fmt-check only: this image has no node, so no prettier. The # markdown half runs in the mdfmt stage below. RUN make fmt-check-go @@ -46,9 +43,6 @@ RUN go mod download COPY . . -# Touch .pb.go so make does not try to regenerate via protoc (file is committed) -RUN touch mfer/mf.pb.go - RUN make test # A build context sent as a tar archive, as upaas sends it, keeps its files' diff --git a/Makefile b/Makefile index 38d19df..7c15d57 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ GOLDFLAGS += -X main.Version=$(VERSION) GOLDFLAGS += -X main.Gitrev=$(GITREV_BUILD) GOFLAGS := -ldflags "$(GOLDFLAGS)" -.PHONY: bootstrap setup docker default run ci test fuzz check lint fmt fmt-check fmt-check-go fmt-check-md hooks fixme +.PHONY: bootstrap setup docker default run ci test fuzz check lint fmt fmt-check fmt-check-go fmt-check-md hooks fixme generate default: fmt test @@ -58,15 +58,14 @@ fmt-check-md: hooks: @script/install-precommit -mfer/mf.pb.go: mfer/mf.proto - cd mfer && go generate . +generate: + @script/generate -bin/mfer: $(SOURCEFILES) mfer/mf.pb.go - protoc --version +bin/mfer: $(SOURCEFILES) cd cmd/mfer && go build -tags urfave_cli_no_docs -o ../../bin/mfer $(GOFLAGS) . clean: - rm -rfv mfer/*.pb.go bin/mfer cmd/mfer/mfer *.dockerimage + rm -rfv bin/mfer cmd/mfer/mfer *.dockerimage fmt: @script/fmt diff --git a/README.md b/README.md index 7234ed5..97956d0 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,12 @@ provide: `script/bootstrap`, then `script/install-precommit` - `script/projectname` — output the project name (`mfer`); used by other scripts such as `script/docker` -- `script/test` — run the test suite (`go test`), regenerating the protobuf code - first if it is stale +- `script/test` — run the test suite (`go test`); one test fails when + `mfer/mf.proto` no longer matches the hash `script/generate` recorded +- `script/generate` — regenerate `mfer/mf.pb.go` from `mfer/mf.proto` and record + the hash of that `mfer/mf.proto` in `mfer/mf.proto.sha256`; needs `protoc` and + `protoc-gen-go`, and is the only thing that regenerates the committed + `mfer/mf.pb.go` - `script/fuzz` — fuzz the manifest parser for one minute; run by hand (`make fuzz`), never by CI, while `script/test` runs its committed seed corpus as ordinary tests diff --git a/mfer/mf.proto.sha256 b/mfer/mf.proto.sha256 new file mode 100644 index 0000000..619c842 --- /dev/null +++ b/mfer/mf.proto.sha256 @@ -0,0 +1 @@ +103901c42b94396aa7ae128fd503ef693a4b7a03b2169481f25fda3d2c254e00 mf.proto diff --git a/mfer/mf_test.go b/mfer/mf_test.go new file mode 100644 index 0000000..f481176 --- /dev/null +++ b/mfer/mf_test.go @@ -0,0 +1,29 @@ +package mfer_test + +import ( + "crypto/sha256" + "encoding/hex" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +// mf.pb.go is generated from mf.proto and committed. `make generate` +// records the hash of the mf.proto it generated from in mf.proto.sha256. +func TestGeneratedCodeMatchesProto(t *testing.T) { + t.Parallel() + + proto, err := os.ReadFile("mf.proto") + require.NoError(t, err) + + recorded, err := os.ReadFile("mf.proto.sha256") + require.NoError(t, err) + + recordedHash, _, _ := strings.Cut(string(recorded), " ") + sum := sha256.Sum256(proto) + require.Equal(t, recordedHash, hex.EncodeToString(sum[:]), + "mfer/mf.proto has changed since mfer/mf.pb.go was generated "+ + "from it: run `make generate` and commit the result") +} diff --git a/script/fmt b/script/fmt index b9aa31d..fd81efa 100755 --- a/script/fmt +++ b/script/fmt @@ -5,19 +5,8 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" -# Regenerate mfer/mf.pb.go from mfer/mf.proto if it is missing or stale -# (mirrors the old Makefile prerequisite; the generated file is -# committed, so this is normally a no-op). -ensure_pb() { - if [ ! -f mfer/mf.pb.go ] || - [ -n "$(find mfer/mf.proto -newer mfer/mf.pb.go 2>/dev/null)" ]; then - (cd mfer && go generate .) - fi -} - main() { cd "$ROOT" - ensure_pb gofumpt -l -w mfer internal cmd # Markdown and JSON, over the same file set script/fmt-check verifies. "$SCRIPT_DIR/prettier" --write diff --git a/script/fmt-check-go b/script/fmt-check-go index c079f59..e0248d4 100755 --- a/script/fmt-check-go +++ b/script/fmt-check-go @@ -6,19 +6,8 @@ set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" -# Regenerate mfer/mf.pb.go from mfer/mf.proto if it is missing or stale -# (mirrors the old Makefile prerequisite; the generated file is -# committed, so this is normally a no-op). -ensure_pb() { - if [ ! -f mfer/mf.pb.go ] || - [ -n "$(find mfer/mf.proto -newer mfer/mf.pb.go 2>/dev/null)" ]; then - (cd mfer && go generate .) - fi -} - main() { cd "$ROOT" - ensure_pb if [ -n "$(gofmt -l .)" ]; then echo "gofmt: files need formatting:" >&2 gofmt -l . >&2 diff --git a/script/generate b/script/generate new file mode 100755 index 0000000..5d1d0c8 --- /dev/null +++ b/script/generate @@ -0,0 +1,17 @@ +#!/bin/sh +# script/generate: regenerate mfer/mf.pb.go from mfer/mf.proto, and record +# the hash of that mf.proto in mfer/mf.proto.sha256. Nothing else +# regenerates mf.pb.go: it is committed, so building and checking need no +# protoc. Needs protoc and protoc-gen-go on PATH. A test fails while +# mf.proto no longer matches the recorded hash. +set -eu + +ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" + +main() { + cd "$ROOT/mfer" + go generate . + shasum -a 256 mf.proto >mf.proto.sha256 +} + +main "$@" diff --git a/script/test b/script/test index a165bc4..2d63e2d 100755 --- a/script/test +++ b/script/test @@ -4,19 +4,8 @@ set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" -# Regenerate mfer/mf.pb.go from mfer/mf.proto if it is missing or stale -# (mirrors the old Makefile prerequisite; the generated file is -# committed, so this is normally a no-op). -ensure_pb() { - if [ ! -f mfer/mf.pb.go ] || - [ -n "$(find mfer/mf.proto -newer mfer/mf.pb.go 2>/dev/null)" ]; then - (cd mfer && go generate .) - fi -} - main() { cd "$ROOT" - ensure_pb go test -timeout 30s -race -cover ./... || { echo "--- Rerunning with -v for details ---"