From 1d925172ecea4e089ee3882c62a40ac1af957595 Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 21 Jul 2026 18:09:27 +0000 Subject: [PATCH] fix(firecracker): repair runtime Git config ownership --- bot_bottle/agent_provider.py | 15 +++++++++++++++ tests/unit/test_docker_provision_git_user.py | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/bot_bottle/agent_provider.py b/bot_bottle/agent_provider.py index 2299b93..2391673 100644 --- a/bot_bottle/agent_provider.py +++ b/bot_bottle/agent_provider.py @@ -263,6 +263,21 @@ class AgentProvider(ABC): BottleBackend.provision_workspace against the running bottle.""" from .log import info + # Firecracker exports image rootfs files through an unprivileged host + # tar extraction, so image-time ownership of XDG directories is not + # preserved. Git consults ~/.config/git even when the actual config + # is ~/.gitconfig; an unreadable directory there can prevent the + # git-gate insteadOf rules below from taking effect. Repair this at + # runtime, after every backend's copy/export path has completed. + git_xdg_dir = f"{plan.guest_home}/.config/git" + bottle.exec( + f"mkdir -p {shlex.quote(git_xdg_dir)} && " + f"chown -R node:node {shlex.quote(f'{plan.guest_home}/.config')} && " + f"chmod 755 {shlex.quote(f'{plan.guest_home}/.config')} " + f"{shlex.quote(git_xdg_dir)}", + user="root", + ) + manifest_bottle = plan.manifest.bottle if manifest_bottle.git: from .git_gate import GIT_GATE_HOSTNAME, git_gate_render_gitconfig diff --git a/tests/unit/test_docker_provision_git_user.py b/tests/unit/test_docker_provision_git_user.py index 081bfce..852822b 100644 --- a/tests/unit/test_docker_provision_git_user.py +++ b/tests/unit/test_docker_provision_git_user.py @@ -125,6 +125,20 @@ class TestProvisionGitUser(unittest.TestCase): _PROVIDER.provision_git(bottle, _plan(stage_dir=self.stage)) self.assertEqual([], _git_config_exec_calls(bottle)) + def test_repairs_git_xdg_directory_for_runtime_user(self): + bottle = _make_bottle() + _PROVIDER.provision_git(bottle, _plan(stage_dir=self.stage)) + + script, user = next( + (call.args[0], call.kwargs.get("user", "node")) + for call in bottle.exec.call_args_list + if "/home/node/.config/git" in call.args[0] + ) + self.assertEqual("root", user) + self.assertIn("mkdir -p /home/node/.config/git", script) + self.assertIn("chown -R node:node /home/node/.config", script) + self.assertIn("chmod 755 /home/node/.config /home/node/.config/git", script) + def test_sets_name_and_email(self): plan = _plan( git_user={"name": "Eric Bauerfeld", "email": "eric@dideric.is"},