Files
ai-agent/backend/test_deploycmd.py
Gabriel Vidal 8e6cef1cd4 feat(backend): compose and rsync deploys, read off the archive
Mined what ran just before a `notify-done --type deploy` without any of the
classic deploy commands (159 of 725): mostly `docker compose … up --build`.
Now also a deploy: compose `up --build` / `--force-recreate` (and via
docker-compose-lab.sh), a plain `compose up` after a `compose build` in the
same command, and rsync into zipgo's domains/ tree. A bare `up -d`, a
lone build and docker build/run test containers stay out. Deploy notifies
with no detected deploy: 159 → 99.

The widget judges failure on the output's last lines — build logs quote
"error" in passing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:47:16 +02:00

96 lines
5.1 KiB
Python

"""conversations.deploycmd: a Bash call is a deploy by what it *runs*.
The cases are lifted from real transcripts (the archive had ~750 calls that
only *mentioned* a deploy script). The extra
shapes (compose, rsync) are what the archive ran just before a
`notify-done --type deploy` without any of the classic deploy commands.
"""
import pytest
from conversations.deploycmd import deploy_invocation
RUNS = [
("./deploy.sh", ("deploy.sh", "./deploy.sh")),
("cd ~/homelab && ./services/ai-agent/deploy.sh 2>&1 | tail -40",
("deploy.sh", "./services/ai-agent/deploy.sh")),
("setsid nohup ./services/ai-agent/deploy.sh > /tmp/ai-agent-deploy.log 2>&1 < /dev/null & disown",
("deploy.sh", "./services/ai-agent/deploy.sh")),
("timeout 900 ./deploy.sh 2>&1 | tail -5", ("deploy.sh", "./deploy.sh")),
("EVOX2_SSH=gabrielvidal@100.93.171.39 ./deploy.sh 2>&1 | tail -15",
("deploy.sh", "./deploy.sh")),
("bash scripts/deploy.sh > log 2>&1; echo EXIT=$?", ("deploy.sh", "scripts/deploy.sh")),
("chmod +x scripts/deploy.sh && bash scripts/deploy.sh 2>&1 | tail -15",
("deploy.sh", "scripts/deploy.sh")),
('echo "=== deploy ==="; ./deploy.sh 2>&1 | tail -40', ("deploy.sh", "./deploy.sh")),
("flock /tmp/cf.lock ./deploy.sh 2>&1 | tail", ("deploy.sh", "./deploy.sh")),
("flock -n /tmp/cf.lock npm run deploy", ("npm run deploy", "")),
('setsid nohup bash -c "./services/ai-agent/deploy.sh; echo RC=$?" > log 2>&1 &',
("deploy.sh", "./services/ai-agent/deploy.sh")),
("for i in $(seq 1 30); do flock -n /tmp/l true && break; sleep 5; done; ./deploy.sh",
("deploy.sh", "./deploy.sh")),
("scripts/deploy-test-site.sh dist feat.app", ("deploy.sh", "scripts/deploy-test-site.sh")),
("cd ~/projects/manuals && rtk npm run deploy 2>&1 | tail -12", ("npm run deploy", "")),
("npm run deploy:demo", ("npm run deploy", "")),
("pnpm --filter web run deploy", ("npm run deploy", "")),
("zipgo deploy dist/ -d foo.dev.gabvdl.xyz", ("zipgo deploy", "dist/")),
("make deploy-pages 2>&1 | tail -20", ("make deploy", "deploy-pages")),
("deploy-html page.html demo", ("deploy-html", "page.html")),
("ssh raspy2 'cd svc && ./deploy.sh'", ("deploy.sh", "./deploy.sh")),
("cat > $S/deploy.sh <<'EOF'\n#!/bin/bash\necho hi\nEOF\nchmod +x $S/deploy.sh && $S/deploy.sh",
("deploy.sh", "$S/deploy.sh")),
("docker compose --profile phone up -d --build phone 2>&1 | tail -30",
("docker compose", "phone")),
("cd ~/homelab && docker compose -f services/x/docker-compose.yml --profile ai up --build -d 3d-gen brain",
("docker compose", "3d-gen brain")),
("docker compose --profile mail up -d --force-recreate mail 2>&1 | tail -3",
("docker compose", "mail")),
("docker compose build ai-agent 2>&1 | tail -20 && docker compose up -d ai-agent",
("docker compose", "ai-agent")),
("./scripts/docker-compose-lab.sh --profile dev up -d --build browserless",
("docker compose", "browserless")),
("docker-compose up -d --build web", ("docker compose", "web")),
("rsync -az --delete dist/ raspy2:/home/gabrielvidal/services/domains/gabvdl.xyz/www./",
("rsync", "raspy2:/home/gabrielvidal/services/domains/gabvdl.xyz/www./")),
]
MENTIONS = [
"cat services/ai-agent/deploy.sh",
"rtk proxy sed -n '180,214p' services/ai-agent/deploy.sh",
"grep -n 'phase' deploy.sh | head",
"grep -E 'REMOTE_DEST=|zipgo deploy|rsync' scripts/deploy.sh",
"bash -n services/ai-agent/deploy.sh && echo syntax OK",
"git add services/ai-agent/deploy.sh && git commit -m 'fix: deploy.sh; npm run deploy'",
'git commit -q -m "feat(deploy): run ./deploy.sh from make deploy"',
"python3 - <<'EOF'\np = 'deploy.sh'\nprint(open(p).read())\nEOF",
"until ! pgrep -f 'services/ai-agent/deploy.sh' >/dev/null; do sleep 5; done; tail -6 /tmp/ai-agent-deploy.log",
'until ! kill -0 $(pgrep -f "ai-agent/deploy.sh" | head -1) 2>/dev/null; do sleep 2; done',
"for d in */scripts/deploy.sh; do cat \"$d\"; done",
"ls ~/projects/backrooms/ && echo '=== deploy script ===' && cat deploy.sh | head -60",
"chmod +x deploy.sh",
"notify-done 'Deployed' 'deploy.sh now syncs images' --type deploy",
"tail -20 /tmp/rig-deploy.log; pgrep -af 'deploy.sh|docker build'",
"# run ./deploy.sh later\necho ok",
"cat <<<\"./deploy.sh\"",
"",
# a bare `up -d` is a bring-up, a build alone ships nothing, test containers
"docker compose --profile dev up -d browserless",
"docker compose build ai-agent 2>&1 | tail -20",
"docker build -t encombrants:test services/encombrants && docker run -d --name encombrants-test encombrants:test",
"docker compose ps browser-agent",
"docker compose --profile media config | grep -A40 stash",
"grep -n 'compose up --build' README.md",
"rsync -a dist/ /tmp/preview/",
"rsync -a raspy2:/home/gabrielvidal/services/domains/x/ ./backup/",
]
@pytest.mark.parametrize("cmd,want", RUNS)
def test_runs_are_deploys(cmd, want):
assert deploy_invocation(cmd) == want
@pytest.mark.parametrize("cmd", MENTIONS)
def test_mentions_are_not(cmd):
assert deploy_invocation(cmd) is None