From d965f00041591ae5cc2e97b9f33a0d875416c077 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Thu, 5 Nov 2026 12:58:26 +0000 Subject: [PATCH] fix(build): keep nested agent worktrees out of the image build context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A quality gate in the main checkout ran compose-up and sent a 35.86 GB build context, because .dockerignore did not exclude the agent worktrees under .claude/worktrees/ (36 GB). Git ignores them through the nested .claude/.gitignore, which the .dockerignore copy never sees. The build cache grew from 28 to 86 GB until / was full. .gitignore, and with it the generated .dockerignore, now lists .claude/worktrees/ and .worktrees/. compose-up regenerates the .dockerignore first, because its build path never ran fix-dockerignore. The new lint pins the copy, both entries and the compose-up prerequisite; on main it fails three times. | 🎯 Fix | 💥 Break | 🌱 Efficiency | ⚡ Speed | 🛡️ Security | 🏆 Score | | --- | --- | --- | --- | --- | --- | | 95 % | 2 % | – | – | ~100 % | 93 | ``` /home/kevinveenbirkenbach/.local/share/worktrees/github.com/infinito-nexus/core/fix/dockerignore-agent-worktrees ``` Co-Authored-By: Claude Opus 5.5 --- .dockerignore | 2 ++ .gitignore | 2 ++ Makefile | 2 +- tests/lint/repository/test_dockerignore.py | 31 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/lint/repository/test_dockerignore.py diff --git a/.dockerignore b/.dockerignore index d2098ebad7..5cbbce7aee 100644 --- a/.dockerignore +++ b/.dockerignore @@ -20,6 +20,8 @@ build/* .ruff_cache .agents .claude/skills/ +.claude/worktrees/ +.worktrees/ .claude-* collections/ .env diff --git a/.gitignore b/.gitignore index d2098ebad7..5cbbce7aee 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ build/* .ruff_cache .agents .claude/skills/ +.claude/worktrees/ +.worktrees/ .claude-* collections/ .env diff --git a/Makefile b/Makefile index e08a2a1888..b400a0d679 100644 --- a/Makefile +++ b/Makefile @@ -260,7 +260,7 @@ compose-system-purge: compose-entity-purge .PHONY: compose-up # Start the development stack. -compose-up: install +compose-up: install fix-dockerignore @"$${PYTHON}" -m cli.administration.deploy.development up .PHONY: console diff --git a/tests/lint/repository/test_dockerignore.py b/tests/lint/repository/test_dockerignore.py new file mode 100644 index 0000000000..81df725641 --- /dev/null +++ b/tests/lint/repository/test_dockerignore.py @@ -0,0 +1,31 @@ +import re +import unittest +from pathlib import Path + +PROJECT_ROOT = Path(__file__).resolve().parents[3] +NESTED_CHECKOUTS = (".claude/worktrees/", ".worktrees/") + + +class TestDockerignore(unittest.TestCase): + def test_dockerignore_is_the_gitignore(self) -> None: + self.assertEqual( + (PROJECT_ROOT / ".gitignore").read_text(), + (PROJECT_ROOT / ".dockerignore").read_text(), + "Run 'make fix-dockerignore': .dockerignore must be a copy of .gitignore.", + ) + + def test_nested_checkouts_stay_out_of_the_build_context(self) -> None: + lines = (PROJECT_ROOT / ".dockerignore").read_text().splitlines() + for entry in NESTED_CHECKOUTS: + with self.subTest(entry=entry): + self.assertIn(entry, lines) + + def test_compose_up_regenerates_the_dockerignore(self) -> None: + makefile = (PROJECT_ROOT / "Makefile").read_text() + match = re.search(r"^compose-up:(.*)$", makefile, re.MULTILINE) + self.assertIsNotNone(match, "Makefile has no compose-up target") + self.assertIn("fix-dockerignore", match.group(1).split()) + + +if __name__ == "__main__": + unittest.main()