From 2c9ff3b014fa3f1463e5768143e981a4573125cd Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Mon, 12 Oct 2026 17:35:58 +0200 Subject: [PATCH] Test that the shipped data stays under 100 MiB and that publish_release.py stops on a tag at another commit, both in the gate --- .github/workflows/test.yml | 2 +- Dockerfile | 6 ++ release-tooling/publish_release_test.py | 78 +++++++++++++++++++++++++ shipped_data_test.go | 22 +++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 release-tooling/publish_release_test.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 530b8ea..0523889 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: else base='${{ github.event.before }}' fi - if git diff --quiet "$base"...HEAD -- '*.go' go.mod go.sum Dockerfile .dockerignore .github data testdata README.md; then + if git diff --quiet "$base"...HEAD -- '*.go' go.mod go.sum Dockerfile .dockerignore .github data release-tooling testdata README.md; then echo "code=false" >> "$GITHUB_OUTPUT" else echo "code=true" >> "$GITHUB_OUTPUT" diff --git a/Dockerfile b/Dockerfile index 2950459..ace9eea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,12 @@ RUN go vet ./... && \ go run github.com/fzipp/gocyclo/cmd/gocyclo@v0.6.0 -over 14 -ignore _test . && \ go test -race ./... +FROM python:3.14.8-slim AS release-tooling +COPY release-tooling/ /release-tooling/ +RUN python3 -m unittest discover -s /release-tooling -p '*_test.py' && touch /tested + FROM portable +# BuildKit skips a stage nothing copies from. +COPY --from=release-tooling /tested /tmp/release-tooling-tested RUN unformatted="$(gofmt -l .)"; test -z "$unformatted" || \ { echo "unformatted files:"; echo "$unformatted"; exit 1; } diff --git a/release-tooling/publish_release_test.py b/release-tooling/publish_release_test.py new file mode 100644 index 0000000..eb06b3c --- /dev/null +++ b/release-tooling/publish_release_test.py @@ -0,0 +1,78 @@ +import io +import os +import tempfile +import unittest +import urllib.error +from contextlib import redirect_stderr, redirect_stdout +from unittest import mock + +import publish_release + +SHA = "a" * 40 +OTHER = "b" * 40 + + +def not_found(path): + return urllib.error.HTTPError(path, 404, "Not Found", None, None) + + +class TagGuard(unittest.TestCase): + def setUp(self): + cwd = os.getcwd() + dir = tempfile.TemporaryDirectory() + self.addCleanup(dir.cleanup) + os.chdir(dir.name) + self.addCleanup(os.chdir, cwd) + with open("CHANGELOG.md", "w", encoding="utf-8") as f: + f.write("# Changelog\n\n## [1.2.3]\n\n- A change.\n") + env = mock.patch.dict(os.environ, {"SHA": SHA}) + env.start() + self.addCleanup(env.stop) + + def publish(self, responses): + created = [] + + def request(path, data=None): + if data is not None: + created.append(data) + return {"html_url": "https://forge.example/release"} + response = responses.get(path) + if response is None: + raise not_found(path) + return response + + with mock.patch.object(publish_release, "request", request), \ + redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()): + code = publish_release.main() + return code, created + + def test_a_tag_at_another_commit_publishes_nothing(self): + code, created = self.publish({"git/ref/tags/v1.2.3": {"object": {"sha": OTHER, "type": "commit"}}}) + self.assertEqual((code, created), (1, [])) + + def test_an_annotated_tag_at_another_commit_publishes_nothing(self): + code, created = self.publish({ + "git/ref/tags/v1.2.3": {"object": {"sha": "c" * 40, "type": "tag"}}, + "git/tags/" + "c" * 40: {"object": {"sha": OTHER, "type": "commit"}}, + }) + self.assertEqual((code, created), (1, [])) + + def test_an_annotated_tag_at_this_commit_publishes(self): + code, created = self.publish({ + "git/ref/tags/v1.2.3": {"object": {"sha": "c" * 40, "type": "tag"}}, + "git/tags/" + "c" * 40: {"object": {"sha": SHA, "type": "commit"}}, + }) + self.assertEqual((code, [c["tag_name"] for c in created]), (0, ["v1.2.3"])) + + def test_no_tag_publishes_one_at_this_commit(self): + code, created = self.publish({}) + self.assertEqual(code, 0) + self.assertEqual(created, [{"body": "- A change.", "name": "v1.2.3", "tag_name": "v1.2.3", "target_commitish": SHA}]) + + def test_a_published_release_publishes_nothing(self): + code, created = self.publish({"releases/tags/v1.2.3": {"html_url": "https://forge.example/release"}}) + self.assertEqual((code, created), (0, [])) + + +if __name__ == "__main__": + unittest.main() diff --git a/shipped_data_test.go b/shipped_data_test.go index dfd89e7..3eef3bf 100644 --- a/shipped_data_test.go +++ b/shipped_data_test.go @@ -2,6 +2,7 @@ package fejkdata import ( "io/fs" + "os" "reflect" "slices" "strings" @@ -18,6 +19,27 @@ func TestABareNewFailsNamingWithDataFS(t *testing.T) { } } +func TestShippedDataStaysUnder100MiB(t *testing.T) { + var size int64 + err := fs.WalkDir(os.DirFS("data"), ".", func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return err + } + info, err := d.Info() + if err != nil { + return err + } + size += info.Size() + return nil + }) + if err != nil { + t.Fatal(err) + } + if size > 100<<20 { + t.Errorf("data/ holds %d bytes, over 100 MiB: carry out todo.md item 193, moving the largest packages into nested Go modules", size) + } +} + func TestEveryShippedModuleLoads(t *testing.T) { f, err := New(withShipped(), WithSeed(1)) if err != nil {