This commit is contained in:
@@ -17,7 +17,7 @@ AUTOMATION_MODE=dry-run
|
|||||||
ALLOW_DIRECT_MAIN=false
|
ALLOW_DIRECT_MAIN=false
|
||||||
|
|
||||||
AGENT_COMMAND=aider
|
AGENT_COMMAND=aider
|
||||||
AGENT_MODEL=mistral/mistral-medium-latest
|
AGENT_MODEL=mistral/devstral-latest
|
||||||
MISTRAL_API_KEY=
|
MISTRAL_API_KEY=
|
||||||
|
|
||||||
POLL_SECONDS=60
|
POLL_SECONDS=60
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ die empfohlene Betriebsart.
|
|||||||
Default:
|
Default:
|
||||||
|
|
||||||
```dotenv
|
```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
|
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_USERNAME: ${GITEA_USERNAME:-automation-bot}
|
||||||
GITEA_TOKEN: ${GITEA_TOKEN:-}
|
GITEA_TOKEN: ${GITEA_TOKEN:-}
|
||||||
AGENT_COMMAND: ${AGENT_COMMAND:-aider}
|
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:-}
|
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
|
||||||
POLL_SECONDS: ${POLL_SECONDS:-60}
|
POLL_SECONDS: ${POLL_SECONDS:-60}
|
||||||
STALE_CLAIM_MINUTES: ${STALE_CLAIM_MINUTES:-90}
|
STALE_CLAIM_MINUTES: ${STALE_CLAIM_MINUTES:-90}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -18,6 +19,13 @@ class AiderAgent:
|
|||||||
task_file.chmod(0o600)
|
task_file.chmod(0o600)
|
||||||
command = self.command(task_file)
|
command = self.command(task_file)
|
||||||
coder = pwd.getpwnam("coder")
|
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)
|
os.chown(task_file, coder.pw_uid, coder.pw_gid)
|
||||||
for path in workspace.rglob("*"):
|
for path in workspace.rglob("*"):
|
||||||
if ".git" not in path.parts:
|
if ".git" not in path.parts:
|
||||||
@@ -49,6 +57,9 @@ class AiderAgent:
|
|||||||
timeout=900,
|
timeout=900,
|
||||||
)
|
)
|
||||||
finally:
|
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)
|
os.chown(workspace, 0, coder.pw_gid, follow_symlinks=False)
|
||||||
workspace.chmod(0o775)
|
workspace.chmod(0o775)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
@@ -72,4 +83,8 @@ class AiderAgent:
|
|||||||
"--input-history-file",
|
"--input-history-file",
|
||||||
"/dev/null",
|
"/dev/null",
|
||||||
"--no-analytics",
|
"--no-analytics",
|
||||||
|
"--edit-format",
|
||||||
|
"diff",
|
||||||
|
"--timeout",
|
||||||
|
"60",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Settings:
|
|||||||
gitea_token=os.getenv("GITEA_TOKEN", ""),
|
gitea_token=os.getenv("GITEA_TOKEN", ""),
|
||||||
gitea_username=os.getenv("GITEA_USERNAME", "automation-bot"),
|
gitea_username=os.getenv("GITEA_USERNAME", "automation-bot"),
|
||||||
agent_command=os.getenv("AGENT_COMMAND", "aider"),
|
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(
|
test_commands=_commands(
|
||||||
os.getenv(
|
os.getenv(
|
||||||
"TEST_COMMANDS",
|
"TEST_COMMANDS",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ _ALWAYS_DENIED = (
|
|||||||
"docker-compose",
|
"docker-compose",
|
||||||
".gitea/",
|
".gitea/",
|
||||||
".github/",
|
".github/",
|
||||||
|
".aider",
|
||||||
)
|
)
|
||||||
|
|
||||||
_DIRECT_MAIN_DENIED = (
|
_DIRECT_MAIN_DENIED = (
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ def build_task(report: FailureReport) -> str:
|
|||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
- First add a regression test that reproduces the reported behavior.
|
- 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.
|
- Make the smallest production change that fixes the root cause.
|
||||||
- Preserve read-only SQL validation and tenant isolation.
|
- Preserve read-only SQL validation and tenant isolation.
|
||||||
- Never read or modify .env files, credentials, deployment secrets, or production data.
|
- Never read or modify .env files, credentials, deployment secrets, or production data.
|
||||||
|
|||||||
@@ -108,7 +108,8 @@ class AutomationWorker:
|
|||||||
"stderr": result.stderr[-6000:],
|
"stderr": result.stderr[-6000:],
|
||||||
})
|
})
|
||||||
if result.returncode != 0:
|
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
|
return results
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ def test_aider_command_cannot_mutate_git_configuration_or_send_analytics() -> No
|
|||||||
assert "--no-analytics" in command
|
assert "--no-analytics" in command
|
||||||
assert "--no-auto-commits" in command
|
assert "--no-auto-commits" in command
|
||||||
assert command.count("/dev/null") == 2
|
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:
|
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 not result.allowed
|
||||||
assert len(result.errors) == 2
|
assert len(result.errors) == 3
|
||||||
|
|
||||||
|
|
||||||
def test_direct_main_blocks_validator_and_migrations() -> None:
|
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 "secret-value" not in task
|
||||||
assert "admin@example.com" not in task
|
assert "admin@example.com" not in task
|
||||||
assert "Do not commit or push" 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:
|
def test_redact_removes_extended_credentials() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user