Harden Aider workspace handoff
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-08-21 23:17:43 +02:00
parent bfa5cdcd3e
commit ed7b88ade4
4 changed files with 51 additions and 21 deletions

View File

@@ -17,6 +17,7 @@ services:
GITEA_REPOSITORY: ${GITEA_REPOSITORY:-lai} GITEA_REPOSITORY: ${GITEA_REPOSITORY:-lai}
GITEA_USERNAME: ${GITEA_USERNAME:-automation-bot} GITEA_USERNAME: ${GITEA_USERNAME:-automation-bot}
GITEA_TOKEN: ${GITEA_TOKEN:-} GITEA_TOKEN: ${GITEA_TOKEN:-}
AGENT_COMMAND: ${AGENT_COMMAND:-aider}
AGENT_MODEL: ${AGENT_MODEL:-mistral/mistral-medium-latest} AGENT_MODEL: ${AGENT_MODEL:-mistral/mistral-medium-latest}
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-} MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
POLL_SECONDS: ${POLL_SECONDS:-60} POLL_SECONDS: ${POLL_SECONDS:-60}

View File

@@ -16,17 +16,7 @@ class AiderAgent:
task_file = workspace / ".git" / "failure-task.txt" task_file = workspace / ".git" / "failure-task.txt"
task_file.write_text(task) task_file.write_text(task)
task_file.chmod(0o600) task_file.chmod(0o600)
command = [ command = self.command(task_file)
self.settings.agent_command,
"--model",
self.settings.agent_model,
"--message-file",
str(task_file),
"--yes",
"--no-auto-commits",
"--no-dirty-commits",
"--no-stream",
]
coder = pwd.getpwnam("coder") coder = pwd.getpwnam("coder")
os.chown(task_file, coder.pw_uid, coder.pw_gid) os.chown(task_file, coder.pw_uid, coder.pw_gid)
for path in workspace.rglob("*"): for path in workspace.rglob("*"):
@@ -47,16 +37,39 @@ class AiderAgent:
os.setgid(coder.pw_gid) os.setgid(coder.pw_gid)
os.setuid(coder.pw_uid) os.setuid(coder.pw_uid)
result = subprocess.run( try:
command, result = subprocess.run(
cwd=workspace, command,
env=environment, cwd=workspace,
text=True, env=environment,
capture_output=True, text=True,
check=False, capture_output=True,
preexec_fn=drop_privileges, check=False,
timeout=3600, preexec_fn=drop_privileges,
) timeout=900,
)
finally:
os.chown(workspace, 0, coder.pw_gid, follow_symlinks=False)
workspace.chmod(0o775)
if result.returncode != 0: if result.returncode != 0:
raise RuntimeError(f"Aider failed ({result.returncode}): {result.stderr[-4000:]}") raise RuntimeError(f"Aider failed ({result.returncode}): {result.stderr[-4000:]}")
return result.stdout[-12_000:] return result.stdout[-12_000:]
def command(self, task_file: Path) -> list[str]:
return [
self.settings.agent_command,
"--model",
self.settings.agent_model,
"--message-file",
str(task_file),
"--yes",
"--no-auto-commits",
"--no-dirty-commits",
"--no-stream",
"--no-gitignore",
"--chat-history-file",
"/dev/null",
"--input-history-file",
"/dev/null",
"--no-analytics",
]

View File

@@ -37,6 +37,8 @@ class GitWorkspace:
shutil.rmtree(self.path, ignore_errors=True) shutil.rmtree(self.path, ignore_errors=True)
self.path.parent.mkdir(parents=True, exist_ok=True) self.path.parent.mkdir(parents=True, exist_ok=True)
self._git("clone", "--branch", self.settings.source_branch, "--single-branch", self.settings.source_repository, str(self.path), cwd=self.path.parent) self._git("clone", "--branch", self.settings.source_branch, "--single-branch", self.settings.source_repository, str(self.path), cwd=self.path.parent)
self._git("config", "user.name", "LAI Automation")
self._git("config", "user.email", "automation@localhost")
self._git("checkout", "-b", self.branch) self._git("checkout", "-b", self.branch)
def run( def run(

14
tests/test_agent.py Normal file
View File

@@ -0,0 +1,14 @@
from pathlib import Path
from test_config import settings
from lai_automation.agent import AiderAgent
def test_aider_command_cannot_mutate_git_configuration_or_send_analytics() -> None:
command = AiderAgent(settings()).command(Path("/repo/.git/task.txt"))
assert "--no-gitignore" in command
assert "--no-analytics" in command
assert "--no-auto-commits" in command
assert command.count("/dev/null") == 2