fix(workspace): include hidden cwd files in docker layer
test / unit (pull_request) Successful in 32s
test / integration (pull_request) Successful in 42s
test / unit (push) Successful in 33s
test / integration (push) Successful in 40s

This commit was merged in pull request #149.
This commit is contained in:
2026-06-02 13:12:40 -04:00
parent a08829573d
commit 3885e2f5ad
2 changed files with 57 additions and 13 deletions
+21 -11
View File
@@ -7,6 +7,7 @@ from __future__ import annotations
import re import re
import shutil import shutil
import subprocess import subprocess
import tempfile
from typing import Iterable, Iterator from typing import Iterable, Iterator
from ...log import die, info from ...log import die, info
@@ -130,17 +131,26 @@ def build_image_with_cwd(
if not os.path.isdir(cwd): if not os.path.isdir(cwd):
die(f"cwd not found at {cwd}") die(f"cwd not found at {cwd}")
info(f"building image {derived} from {base} with {cwd} -> {workspace.guest_path}") info(f"building image {derived} from {base} with {cwd} -> {workspace.guest_path}")
dockerfile = ( with tempfile.TemporaryDirectory(prefix="bot-bottle-cwd.") as tmp:
f"FROM {base}\n" context_dir = os.path.join(tmp, "context")
f"COPY --chown=node:node . {workspace.guest_path}\n" staged_workspace = os.path.join(context_dir, "workspace")
f"WORKDIR {workspace.workdir}\n" shutil.copytree(
) cwd,
subprocess.run( staged_workspace,
["docker", "build", "-t", derived, "-f", "-", cwd], symlinks=True,
input=dockerfile, ignore=shutil.ignore_patterns(".git"),
text=True, )
check=True, dockerfile = (
) f"FROM {base}\n"
f"COPY --chown=node:node workspace/. {workspace.guest_path}\n"
f"WORKDIR {workspace.workdir}\n"
)
subprocess.run(
["docker", "build", "-t", derived, "-f", "-", context_dir],
input=dockerfile,
text=True,
check=True,
)
def image_id(ref: str) -> str: def image_id(ref: str) -> str:
+36 -2
View File
@@ -85,11 +85,45 @@ class TestBuildImageWithCwd(unittest.TestCase):
argv = run.call_args.args[0] argv = run.call_args.args[0]
dockerfile = run.call_args.kwargs["input"] dockerfile = run.call_args.kwargs["input"]
self.assertEqual(["docker", "build", "-t", "derived:tag", "-f", "-", tmp], argv) self.assertEqual(["docker", "build", "-t", "derived:tag", "-f", "-"], argv[:6])
self.assertTrue(argv[6].endswith("/context"))
self.assertIn("FROM base:tag\n", dockerfile) self.assertIn("FROM base:tag\n", dockerfile)
self.assertIn("COPY --chown=node:node . /guest/home/workspace\n", dockerfile) self.assertIn(
"COPY --chown=node:node workspace/. /guest/home/workspace\n",
dockerfile,
)
self.assertIn("WORKDIR /guest/home/workspace\n", dockerfile) self.assertIn("WORKDIR /guest/home/workspace\n", dockerfile)
def test_staged_context_includes_hidden_files_but_not_git_dir(self):
with tempfile.TemporaryDirectory(prefix="bb-docker-cwd.") as tmp:
root = Path(tmp)
(root / ".gitignore").write_text("*.pyc\n")
(root / ".dockerignore").write_text(".gitignore\n")
(root / ".env.example").write_text("SAFE=1\n")
(root / ".git").mkdir()
(root / ".git" / "config").write_text("[core]\n")
workspace = WorkspacePlan(
enabled=True,
host_path=root,
guest_home="/guest/home",
guest_path="/guest/home/workspace",
workdir="/guest/home/workspace",
)
def inspect_context(*args, **kwargs):
context = Path(args[0][-1])
staged = context / "workspace"
self.assertTrue((staged / ".gitignore").is_file())
self.assertTrue((staged / ".dockerignore").is_file())
self.assertTrue((staged / ".env.example").is_file())
self.assertFalse((staged / ".git").exists())
return _ok()
with patch.object(
docker_mod.subprocess, "run", side_effect=inspect_context,
):
docker_mod.build_image_with_cwd("derived:tag", "base:tag", workspace)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()