This commit is contained in:
@@ -17,7 +17,7 @@ AUTOMATION_MODE=dry-run
|
||||
ALLOW_DIRECT_MAIN=false
|
||||
|
||||
AGENT_COMMAND=aider
|
||||
AGENT_MODEL=mistral/mistral-medium-latest
|
||||
AGENT_MODEL=mistral/devstral-latest
|
||||
MISTRAL_API_KEY=
|
||||
|
||||
POLL_SECONDS=60
|
||||
|
||||
@@ -88,7 +88,7 @@ die empfohlene Betriebsart.
|
||||
Default:
|
||||
|
||||
```dotenv
|
||||
AGENT_MODEL=mistral/mistral-medium-latest
|
||||
AGENT_MODEL=mistral/devstral-latest
|
||||
```
|
||||
|
||||
Das Chat-Modell und Coding-Modell sind bewusst getrennt. Fuer Coding-Agenten sollte ein
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
GITEA_USERNAME: ${GITEA_USERNAME:-automation-bot}
|
||||
GITEA_TOKEN: ${GITEA_TOKEN:-}
|
||||
AGENT_COMMAND: ${AGENT_COMMAND:-aider}
|
||||
AGENT_MODEL: ${AGENT_MODEL:-mistral/mistral-medium-latest}
|
||||
AGENT_MODEL: ${AGENT_MODEL:-mistral/devstral-latest}
|
||||
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
|
||||
POLL_SECONDS: ${POLL_SECONDS:-60}
|
||||
STALE_CLAIM_MINUTES: ${STALE_CLAIM_MINUTES:-90}
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pwd
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
@@ -18,6 +19,13 @@ class AiderAgent:
|
||||
task_file.chmod(0o600)
|
||||
command = self.command(task_file)
|
||||
coder = pwd.getpwnam("coder")
|
||||
git_config = Path(coder.pw_dir) / ".gitconfig"
|
||||
git_config.write_text(
|
||||
"[user]\n\tname = LAI Automation Coder\n\temail = coder@localhost\n"
|
||||
f"[safe]\n\tdirectory = {workspace.resolve()}\n"
|
||||
)
|
||||
git_config.chmod(0o600)
|
||||
os.chown(git_config, coder.pw_uid, coder.pw_gid)
|
||||
os.chown(task_file, coder.pw_uid, coder.pw_gid)
|
||||
for path in workspace.rglob("*"):
|
||||
if ".git" not in path.parts:
|
||||
@@ -49,6 +57,9 @@ class AiderAgent:
|
||||
timeout=900,
|
||||
)
|
||||
finally:
|
||||
shutil.rmtree(workspace / ".aider.tags.cache.v4", ignore_errors=True)
|
||||
for artifact in (".aider.chat.history.md", ".aider.input.history"):
|
||||
(workspace / artifact).unlink(missing_ok=True)
|
||||
os.chown(workspace, 0, coder.pw_gid, follow_symlinks=False)
|
||||
workspace.chmod(0o775)
|
||||
if result.returncode != 0:
|
||||
@@ -72,4 +83,8 @@ class AiderAgent:
|
||||
"--input-history-file",
|
||||
"/dev/null",
|
||||
"--no-analytics",
|
||||
"--edit-format",
|
||||
"diff",
|
||||
"--timeout",
|
||||
"60",
|
||||
]
|
||||
|
||||
@@ -64,7 +64,7 @@ class Settings:
|
||||
gitea_token=os.getenv("GITEA_TOKEN", ""),
|
||||
gitea_username=os.getenv("GITEA_USERNAME", "automation-bot"),
|
||||
agent_command=os.getenv("AGENT_COMMAND", "aider"),
|
||||
agent_model=os.getenv("AGENT_MODEL", "mistral/mistral-medium-latest"),
|
||||
agent_model=os.getenv("AGENT_MODEL", "mistral/devstral-latest"),
|
||||
test_commands=_commands(
|
||||
os.getenv(
|
||||
"TEST_COMMANDS",
|
||||
|
||||
@@ -15,6 +15,7 @@ _ALWAYS_DENIED = (
|
||||
"docker-compose",
|
||||
".gitea/",
|
||||
".github/",
|
||||
".aider",
|
||||
)
|
||||
|
||||
_DIRECT_MAIN_DENIED = (
|
||||
|
||||
@@ -36,6 +36,7 @@ def build_task(report: FailureReport) -> str:
|
||||
|
||||
Rules:
|
||||
- First add a regression test that reproduces the reported behavior.
|
||||
- The regression test must execute the relevant production behavior; never mock or stub the behavior being tested.
|
||||
- Make the smallest production change that fixes the root cause.
|
||||
- Preserve read-only SQL validation and tenant isolation.
|
||||
- Never read or modify .env files, credentials, deployment secrets, or production data.
|
||||
|
||||
@@ -108,7 +108,8 @@ class AutomationWorker:
|
||||
"stderr": result.stderr[-6000:],
|
||||
})
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"Test command failed: {command}\n{result.stderr[-2000:]}")
|
||||
output = (result.stdout + "\n" + result.stderr)[-4000:]
|
||||
raise RuntimeError(f"Test command failed: {command}\n{output}")
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -12,3 +12,4 @@ def test_aider_command_cannot_mutate_git_configuration_or_send_analytics() -> No
|
||||
assert "--no-analytics" in command
|
||||
assert "--no-auto-commits" in command
|
||||
assert command.count("/dev/null") == 2
|
||||
assert command[-4:] == ["--edit-format", "diff", "--timeout", "60"]
|
||||
@@ -15,9 +15,12 @@ def test_policy_accepts_small_application_change() -> None:
|
||||
|
||||
|
||||
def test_policy_rejects_secrets_and_vendor_paths() -> None:
|
||||
result = evaluate_changes(ChangeSummary((".env", "vendor/foo.php"), 2, 0), settings())
|
||||
result = evaluate_changes(
|
||||
ChangeSummary((".env", "vendor/foo.php", ".aider.tags.cache.v4/cache.db"), 3, 0),
|
||||
settings(),
|
||||
)
|
||||
assert not result.allowed
|
||||
assert len(result.errors) == 2
|
||||
assert len(result.errors) == 3
|
||||
|
||||
|
||||
def test_direct_main_blocks_validator_and_migrations() -> None:
|
||||
|
||||
@@ -30,6 +30,7 @@ def test_task_contains_constraints_and_redacted_report() -> None:
|
||||
assert "secret-value" not in task
|
||||
assert "admin@example.com" not in task
|
||||
assert "Do not commit or push" in task
|
||||
assert "never mock or stub the behavior being tested" in task
|
||||
|
||||
|
||||
def test_redact_removes_extended_credentials() -> None:
|
||||
|
||||
Reference in New Issue
Block a user