- update_mcp_worker.yml: git_status -> git_fetch -> git_pull (all cwd=scrum4me-docker) -> docker_compose_build -> docker_compose_up_recreate -> wait_for_health_worker - commands.yml.example: add docker_compose_up_recreate (--force-recreate) and wait_for_health_worker (polls /var/log/agent/current for 'pre-flight passed') Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
205 lines
7 KiB
Text
205 lines
7 KiB
Text
# Whitelist of allowed commands for ops-agent.
|
|
# Copy to /etc/ops-agent/commands.yml on the host.
|
|
# Restart ops-agent after changes.
|
|
#
|
|
# Schema per command:
|
|
# cmd: required — command + static args as array (no shell, no interpolation)
|
|
# cwd: optional — working directory for the subprocess
|
|
# cwd_pattern: optional — working directory as a glob/pattern (resolved at runtime)
|
|
# args:
|
|
# allowed: optional — whitelist of argument values accepted from the caller
|
|
# If absent or empty, the command takes no extra arguments.
|
|
# description: optional — human-readable description
|
|
|
|
commands:
|
|
docker_ps:
|
|
cmd: ["docker", "ps", "--format", "table"]
|
|
description: "List running Docker containers"
|
|
|
|
git_status:
|
|
cmd: ["git", "status", "--short", "--branch"]
|
|
cwd_pattern: "/srv/"
|
|
description: "Git status with branch info (first arg = repo path, must start with /srv/)"
|
|
|
|
git_log_ahead:
|
|
cmd: ["git", "log", "@{upstream}..HEAD", "--oneline"]
|
|
cwd_pattern: "/srv/"
|
|
description: "Local commits not yet pushed (first arg = repo path)"
|
|
|
|
git_diff:
|
|
cmd: ["git", "diff", "HEAD"]
|
|
cwd_pattern: "/srv/"
|
|
description: "Uncommitted diff against HEAD (first arg = repo path)"
|
|
|
|
git_fetch:
|
|
cmd: ["git", "fetch", "--quiet"]
|
|
cwd_pattern: "/srv/"
|
|
description: "Fetch all remotes silently (first arg = repo path)"
|
|
|
|
systemctl_status:
|
|
cmd: ["systemctl", "status", "--no-pager", "-l"]
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- ops-agent
|
|
- caddy
|
|
- docker
|
|
- nginx
|
|
- postgresql
|
|
description: "Show systemctl status for an allowed service"
|
|
|
|
journalctl_recent:
|
|
cmd: ["journalctl", "--since", "1 hour ago", "-n", "100", "--no-pager", "-u"]
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- ops-agent
|
|
- caddy
|
|
- docker
|
|
- nginx
|
|
- postgresql
|
|
description: "Last 100 journal lines from the past hour for an allowed service"
|
|
|
|
caddy_show_config:
|
|
cmd: ["caddy", "fmt", "/etc/caddy/Caddyfile"]
|
|
description: "Print the formatted Caddy config"
|
|
|
|
caddy_list_certs:
|
|
cmd:
|
|
- sh
|
|
- -c
|
|
- "for f in /data/caddy/certificates/*/*.crt; do [ -f \"$f\" ] || continue; echo \"CERTFILE:$f\"; openssl x509 -noout -subject -issuer -dates -in \"$f\" 2>&1; echo \"CERTEND\"; done"
|
|
description: "List TLS cert info (subject, issuer, validity dates) from Caddy certificate store"
|
|
|
|
# ── Destructive / write commands ──────────────────────────────────────────
|
|
|
|
docker_compose_restart:
|
|
cmd: ["docker", "compose", "restart"]
|
|
cwd: "/srv/scrum4me/compose"
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- worker-idea
|
|
- ops-dashboard
|
|
- caddy
|
|
- postgres
|
|
description: "Restart a docker compose service (ops-agent user must be in the docker group)"
|
|
|
|
docker_compose_stop:
|
|
cmd: ["docker", "compose", "stop"]
|
|
cwd: "/srv/scrum4me/compose"
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- worker-idea
|
|
- ops-dashboard
|
|
- caddy
|
|
- postgres
|
|
description: "Stop a docker compose service"
|
|
|
|
docker_compose_build:
|
|
cmd: ["docker", "compose", "build"]
|
|
cwd: "/srv/scrum4me/compose"
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- worker-idea
|
|
- ops-dashboard
|
|
description: "Build a docker compose service image"
|
|
|
|
docker_compose_up:
|
|
cmd: ["docker", "compose", "up", "-d"]
|
|
cwd: "/srv/scrum4me/compose"
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- worker-idea
|
|
- ops-dashboard
|
|
description: "Start or recreate a docker compose service in detached mode"
|
|
|
|
docker_compose_up_recreate:
|
|
cmd: ["docker", "compose", "up", "-d", "--force-recreate"]
|
|
cwd: "/srv/scrum4me/compose"
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- worker-idea
|
|
- ops-dashboard
|
|
description: "Force-recreate a docker compose service (picks up a rebuilt image)"
|
|
|
|
git_pull:
|
|
cmd: ["git", "pull", "--ff-only"]
|
|
cwd_pattern: "/srv/"
|
|
preconditions:
|
|
- git_status_clean
|
|
description: "Fast-forward pull — refused when working tree is dirty"
|
|
|
|
systemctl_restart:
|
|
# Requires /etc/sudoers.d/ops-agent (see deploy/ops-agent/sudoers).
|
|
cmd: ["sudo", "/usr/bin/systemctl", "restart"]
|
|
args:
|
|
allowed:
|
|
- scrum4me-web
|
|
- ops-agent
|
|
- caddy
|
|
description: "Restart an allowed systemd service via sudo"
|
|
|
|
caddy_validate:
|
|
cmd: ["caddy", "validate", "--config", "/srv/scrum4me/caddy/Caddyfile"]
|
|
description: "Validate /srv/scrum4me/caddy/Caddyfile without reloading"
|
|
|
|
caddy_reload:
|
|
cmd: ["caddy", "reload", "--config", "/srv/scrum4me/caddy/Caddyfile"]
|
|
description: "Reload Caddy with /srv/scrum4me/caddy/Caddyfile"
|
|
|
|
caddy_write_config:
|
|
# Writes stdin to Caddyfile.new first; mv is atomic on the same filesystem.
|
|
# ops-agent user must own /srv/scrum4me/caddy/.
|
|
cmd:
|
|
- sh
|
|
- -c
|
|
- "cat > /srv/scrum4me/caddy/Caddyfile.new && mv /srv/scrum4me/caddy/Caddyfile.new /srv/scrum4me/caddy/Caddyfile"
|
|
stdin_from_body: true
|
|
description: "Atomically replace /srv/scrum4me/caddy/Caddyfile (write stdin to .new, then mv)"
|
|
|
|
# ── Smoke tests / health checks ───────────────────────────────────────────
|
|
|
|
curl_smoke_scrum4me_web:
|
|
cmd: ["curl", "-sf", "--max-time", "10", "https://scrum4me.com"]
|
|
description: "HTTP smoke test — fails (non-zero) if the site is unreachable or returns a non-2xx status"
|
|
|
|
docker_compose_ps_worker:
|
|
cmd: ["docker", "compose", "ps", "--filter", "status=running", "worker-idea"]
|
|
cwd: "/srv/scrum4me/compose"
|
|
description: "Verify worker-idea container is in the running state"
|
|
|
|
wait_for_health_worker:
|
|
cmd:
|
|
- sh
|
|
- -c
|
|
- "timeout 60 sh -c 'until grep -q \"pre-flight passed\" /var/log/agent/current 2>/dev/null; do sleep 3; done && echo \"pre-flight passed\"'"
|
|
description: "Wait up to 60s for MCP worker pre-flight check (/var/log/agent/current)"
|
|
|
|
# ── Scrum4Me web deployment steps ────────────────────────────────────────
|
|
|
|
npm_ci:
|
|
cmd: ["npm", "ci"]
|
|
cwd: "/srv/scrum4me/repos/Scrum4Me"
|
|
description: "Install production dependencies for Scrum4Me web (npm ci)"
|
|
|
|
prisma_migrate_deploy:
|
|
cmd: ["npx", "prisma", "migrate", "deploy"]
|
|
cwd: "/srv/scrum4me/repos/Scrum4Me"
|
|
description: "Apply pending Prisma migrations for Scrum4Me web"
|
|
|
|
npm_run_build:
|
|
cmd: ["npm", "run", "build"]
|
|
cwd: "/srv/scrum4me/repos/Scrum4Me"
|
|
description: "Build the Scrum4Me web application (next build)"
|
|
|
|
curl_smoke_scrum4me_thuis:
|
|
cmd:
|
|
- sh
|
|
- -c
|
|
- "code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 https://thuis.jp-visser.nl/api/products); echo \"HTTP $code\"; [ \"$code\" = \"200\" ] || [ \"$code\" = \"401\" ]"
|
|
description: "Smoke test: /api/products must return 200 or 401"
|