From 68c4d037cfbc997f10ae64cdba57ed42e80146bb Mon Sep 17 00:00:00 2001 From: Janpeter Visser <30029041+madhura68@users.noreply.github.com> Date: Thu, 14 May 2026 12:05:39 +0200 Subject: [PATCH] feat(flows): redeploy_all flow + fix MCP-worker cache-bust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legt de volledige stack-redeploy vast als één flow: scrum4me-web (pull/migrate/build/restart) gevolgd door de MCP-worker. Onderweg een echte bug gevonden en gefixt: update_mcp_worker.yml deed `docker_compose_build worker-idea` zónder cache-bust. De worker-idea Dockerfile clonet scrum4me-mcp van GitHub in een aparte laag; zolang MCP_GIT_REF gelijk blijft ('main') hergebruikt Docker die laag, dus nieuwe MCP-commits werden NIET opgepikt. Een schijnbaar geslaagde rebuild draaide stilletjes op oude MCP-code. Wijzigingen: - commands.yml.example: nieuw command docker_compose_build_worker_fresh dat via `sh -c` MCP_CACHE_BUST=$(date +%s) meegeeft — invalideert de clone-laag zodat de laatste MCP-code wordt gepulld - update_mcp_worker.yml: gebruikt nu de fresh-build; pullt ook scrum4me-mcp lokaal (on_failure: continue, sync-only) - redeploy_all.yml: nieuwe gecombineerde flow (16 stappen, web → worker) - app/flows/redeploy-all/: UI-pagina + panel, zelfde patroon als de bestaande flow-pagina's - app/flows/page.tsx: Redeploy All bovenaan de flows-lijst Co-Authored-By: Claude Opus 4.7 (1M context) --- app/flows/page.tsx | 5 + .../redeploy-all/_components/flow-panel.tsx | 135 ++++++++++++++++++ app/flows/redeploy-all/page.tsx | 27 ++++ ops-agent/commands.yml.example | 14 ++ ops-agent/flows.example/redeploy_all.yml | 86 +++++++++++ ops-agent/flows.example/update_mcp_worker.yml | 27 ++-- 6 files changed, 285 insertions(+), 9 deletions(-) create mode 100644 app/flows/redeploy-all/_components/flow-panel.tsx create mode 100644 app/flows/redeploy-all/page.tsx create mode 100644 ops-agent/flows.example/redeploy_all.yml diff --git a/app/flows/page.tsx b/app/flows/page.tsx index 05848fe..ee16447 100644 --- a/app/flows/page.tsx +++ b/app/flows/page.tsx @@ -5,6 +5,11 @@ import { getCurrentUser } from '@/lib/session' export const dynamic = 'force-dynamic' const FLOWS = [ + { + href: '/flows/redeploy-all', + title: 'Redeploy All', + desc: 'Volledige stack-redeploy: scrum4me-web + MCP-worker (cache-busted)', + }, { href: '/flows/update-scrum4me-web', title: 'Update Scrum4Me website', diff --git a/app/flows/redeploy-all/_components/flow-panel.tsx b/app/flows/redeploy-all/_components/flow-panel.tsx new file mode 100644 index 0000000..d81ea26 --- /dev/null +++ b/app/flows/redeploy-all/_components/flow-panel.tsx @@ -0,0 +1,135 @@ +'use client' + +import { useState, useCallback } from 'react' +import Link from 'next/link' +import { useFlowRun } from '@/hooks/useFlowRun' +import StreamingTerminal from '@/components/StreamingTerminal' +import ConfirmDialog from '@/components/ConfirmDialog' + +const FLOW_KEY = 'redeploy_all' + +const STEPS = [ + 'git status Scrum4Me (show current state)', + 'git fetch Scrum4Me (fetch remote refs)', + 'git log (commits ahead of upstream)', + 'git pull --ff-only Scrum4Me (aborts if dirty)', + 'npm ci (install dependencies)', + 'prisma migrate deploy (apply migrations)', + 'npm run build (build application)', + 'systemctl restart scrum4me-web', + 'smoke test: curl /api/products (expect 200 or 401)', + 'git status scrum4me-docker (show current state)', + 'git fetch scrum4me-docker (fetch remote refs)', + 'git pull --ff-only scrum4me-docker (aborts if dirty)', + 'git pull --ff-only scrum4me-mcp (lokale sync)', + 'rebuild worker image — cache-busted MCP clone', + 'docker compose up -d --force-recreate worker-idea', + 'wait for worker pre-flight to pass', +] + +export default function FlowPanel() { + const [pendingDryRun, setPendingDryRun] = useState(null) + const [completedFlowRunId, setCompletedFlowRunId] = useState(null) + + const handleComplete = useCallback((flowRunId: string) => { + setCompletedFlowRunId(flowRunId) + }, []) + + const flowRun = useFlowRun(handleComplete) + + const handleConfirm = useCallback(() => { + if (pendingDryRun === null) return + const dryRun = pendingDryRun + setPendingDryRun(null) + setCompletedFlowRunId(null) + flowRun.startFlow(FLOW_KEY, dryRun) + }, [pendingDryRun, flowRun]) + + const handleReset = useCallback(() => { + flowRun.reset() + setCompletedFlowRunId(null) + }, [flowRun]) + + return ( +
+
+
+

+ Volledige stack-redeploy: eerst de hoofd-app (scrum4me-web — pull, + migrate, build, restart), dan de MCP-worker (cache-busted image + rebuild zodat de nieuwe scrum4me-mcp code wordt opgepikt). +

+

+ repos: Scrum4Me · scrum4me-docker · scrum4me-mcp +

+
+
    + {STEPS.map((step, i) => ( +
  1. + {i + 1}. + {step} +
  2. + ))} +
+
+ +
+ + + {flowRun.status !== 'idle' && flowRun.status !== 'running' && ( + + )} +
+ + {flowRun.status !== 'idle' && ( +
+
+ Output + {completedFlowRunId && ( + + View in audit log → + + )} +
+ +
+ )} + + ` ${i + 1}. ${s}`).join('\n')}` + } + onConfirm={handleConfirm} + onCancel={() => setPendingDryRun(null)} + /> +
+ ) +} diff --git a/app/flows/redeploy-all/page.tsx b/app/flows/redeploy-all/page.tsx new file mode 100644 index 0000000..8971cbb --- /dev/null +++ b/app/flows/redeploy-all/page.tsx @@ -0,0 +1,27 @@ +import Link from 'next/link' +import { redirect } from 'next/navigation' +import { getCurrentUser } from '@/lib/session' +import FlowPanel from './_components/flow-panel' + +export const dynamic = 'force-dynamic' + +export default async function RedeployAllPage() { + const user = await getCurrentUser() + if (!user) redirect('/login') + + return ( +
+
+
+ + ← Home + + / +

Redeploy All

+
+ + +
+
+ ) +} diff --git a/ops-agent/commands.yml.example b/ops-agent/commands.yml.example index 4c7cd2f..87c17dc 100644 --- a/ops-agent/commands.yml.example +++ b/ops-agent/commands.yml.example @@ -107,6 +107,20 @@ commands: - ops-dashboard description: "Build a docker compose service image" + docker_compose_build_worker_fresh: + # De worker-idea Dockerfile clonet scrum4me-mcp van GitHub in een aparte + # laag. Een gewone docker compose build hergebruikt die laag zolang + # MCP_GIT_REF gelijk blijft (= altijd 'main'), dus nieuwe MCP-commits worden + # NIET opgepikt. MCP_CACHE_BUST met een verse timestamp invalideert de + # clone-laag. sh -c is nodig om $(date) te evalueren (geen shell-injectie: + # vaste string, geen externe input). + cmd: + - sh + - -c + - "docker compose build --build-arg MCP_CACHE_BUST=$(date +%s) worker-idea" + cwd: "/srv/scrum4me/compose" + description: "Rebuild worker-idea image, busting the scrum4me-mcp clone cache so the latest MCP code is pulled" + docker_compose_up: cmd: ["docker", "compose", "up", "-d"] cwd: "/srv/scrum4me/compose" diff --git a/ops-agent/flows.example/redeploy_all.yml b/ops-agent/flows.example/redeploy_all.yml new file mode 100644 index 0000000..4a3ee3e --- /dev/null +++ b/ops-agent/flows.example/redeploy_all.yml @@ -0,0 +1,86 @@ +# Volledige redeploy van de Scrum4Me-stack — alle drie de repos in één flow. +# Copy to /etc/ops-agent/flows/redeploy_all.yml on the host. +# +# Dit is de gecombineerde werkwijze: eerst de hoofd-app (scrum4me-web), +# dan de worker (scrum4me-docker image met verse scrum4me-mcp clone). +# Equivalent aan update_scrum4me_web.yml gevolgd door update_mcp_worker.yml, +# maar als één atomaire flow met audit-trail. +# +# Volgorde-redenering: +# - Web eerst: de DB-migratie (stap 6) is additief en niet-breaking, dus +# veilig terwijl de oude worker nog draait. +# - Worker daarna: de nieuwe MCP-code kan afhankelijk zijn van de nieuwe +# DB-kolommen/enums uit de web-migratie. +# +# Steps: +# 1-9. scrum4me-web: status, fetch, log-ahead, pull, npm ci, migrate, +# build, restart service, smoke-test +# 10-16. worker: status + fetch + pull scrum4me-docker, pull scrum4me-mcp, +# cache-busted image rebuild, container recreate, health-wait +# +# Let op: de worker-rebuild MOET docker_compose_build_worker_fresh gebruiken, +# niet docker_compose_build — anders blijft de scrum4me-mcp clone-laag +# gecached en wordt nieuwe MCP-code gemist. + +name: Redeploy All +description: Volledige stack-redeploy — scrum4me-web (pull/migrate/build/restart) gevolgd door de MCP-worker (cache-busted image rebuild) +steps: + # --- scrum4me-web ------------------------------------------------------- + - command_key: git_status + args: ["/srv/scrum4me/repos/Scrum4Me"] + on_failure: continue + + - command_key: git_fetch + args: ["/srv/scrum4me/repos/Scrum4Me"] + on_failure: abort + + - command_key: git_log_ahead + args: ["/srv/scrum4me/repos/Scrum4Me"] + on_failure: continue + + - command_key: git_pull + args: ["/srv/scrum4me/repos/Scrum4Me"] + on_failure: abort + + - command_key: npm_ci + on_failure: abort + + - command_key: prisma_migrate_deploy + on_failure: abort + + - command_key: npm_run_build + on_failure: abort + + - command_key: systemctl_restart + args: ["scrum4me-web"] + on_failure: abort + + - command_key: curl_smoke_scrum4me_thuis + on_failure: continue + + # --- MCP-worker --------------------------------------------------------- + - command_key: git_status + args: ["/srv/scrum4me/repos/scrum4me-docker"] + on_failure: continue + + - command_key: git_fetch + args: ["/srv/scrum4me/repos/scrum4me-docker"] + on_failure: abort + + - command_key: git_pull + args: ["/srv/scrum4me/repos/scrum4me-docker"] + on_failure: abort + + - command_key: git_pull + args: ["/srv/scrum4me/repos/scrum4me-mcp"] + on_failure: continue + + - command_key: docker_compose_build_worker_fresh + on_failure: abort + + - command_key: docker_compose_up_recreate + args: ["worker-idea"] + on_failure: abort + + - command_key: wait_for_health_worker + on_failure: continue diff --git a/ops-agent/flows.example/update_mcp_worker.yml b/ops-agent/flows.example/update_mcp_worker.yml index 56fdb82..ef8784d 100644 --- a/ops-agent/flows.example/update_mcp_worker.yml +++ b/ops-agent/flows.example/update_mcp_worker.yml @@ -2,15 +2,21 @@ # Copy to /etc/ops-agent/flows/update_mcp_worker.yml on the host. # # Steps: -# 1. Show current git status (informational) -# 2. Fetch remote refs -# 3. Fast-forward pull (aborts if working tree is dirty) -# 4. Rebuild the Docker image -# 5. Recreate the container in detached mode (force-recreate picks up new image) -# 6. Wait for worker pre-flight to pass (checks /var/log/agent/current) +# 1. Show current git status of scrum4me-docker (informational) +# 2. Fetch remote refs for scrum4me-docker +# 3. Fast-forward pull scrum4me-docker (aborts if working tree is dirty) +# 4. Fast-forward pull scrum4me-mcp — sync van de lokale repo. De image +# cloned MCP zelf van GitHub, dus dit is alleen lokale referentie; +# on_failure: continue zodat een dirty mcp-tree de deploy niet blokkeert. +# 5. Rebuild the worker image MET cache-bust. Een gewone build hergebruikt +# de scrum4me-mcp clone-laag (MCP_GIT_REF blijft 'main'), dus nieuwe +# MCP-commits worden gemist. docker_compose_build_worker_fresh forceert +# een verse clone via MCP_CACHE_BUST. +# 6. Recreate the container (force-recreate picks up the new image) +# 7. Wait for worker pre-flight to pass (checks /var/log/agent/current) name: Update MCP Worker -description: Pull latest code, rebuild Docker image, and restart the MCP worker service +description: Pull latest code, rebuild the worker image with a fresh scrum4me-mcp clone, and recreate the worker container steps: - command_key: git_status args: ["/srv/scrum4me/repos/scrum4me-docker"] @@ -24,8 +30,11 @@ steps: args: ["/srv/scrum4me/repos/scrum4me-docker"] on_failure: abort - - command_key: docker_compose_build - args: ["worker-idea"] + - command_key: git_pull + args: ["/srv/scrum4me/repos/scrum4me-mcp"] + on_failure: continue + + - command_key: docker_compose_build_worker_fresh on_failure: abort - command_key: docker_compose_up_recreate