Merge pull request #9 from madhura68/feat/redeploy-all-flow

feat(flows): redeploy_all flow + fix MCP-worker cache-bust bug
This commit is contained in:
Janpeter Visser 2026-05-14 10:36:24 +00:00 committed by GitHub
commit 5c1f047259
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 285 additions and 9 deletions

View file

@ -5,6 +5,11 @@ import { getCurrentUser } from '@/lib/session'
export const dynamic = 'force-dynamic' export const dynamic = 'force-dynamic'
const FLOWS = [ const FLOWS = [
{
href: '/flows/redeploy-all',
title: 'Redeploy All',
desc: 'Volledige stack-redeploy: scrum4me-web + MCP-worker (cache-busted)',
},
{ {
href: '/flows/update-scrum4me-web', href: '/flows/update-scrum4me-web',
title: 'Update Scrum4Me website', title: 'Update Scrum4Me website',

View file

@ -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<boolean | null>(null)
const [completedFlowRunId, setCompletedFlowRunId] = useState<string | null>(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 (
<div className="space-y-6">
<div className="rounded-lg border border-border p-5 space-y-4">
<div>
<p className="text-sm text-muted-foreground">
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).
</p>
<p className="mt-1 text-xs text-muted-foreground font-mono">
repos: Scrum4Me · scrum4me-docker · scrum4me-mcp
</p>
</div>
<ol className="space-y-1">
{STEPS.map((step, i) => (
<li key={i} className="flex gap-2 text-xs font-mono text-muted-foreground">
<span className="text-border min-w-[1.5rem]">{i + 1}.</span>
<span>{step}</span>
</li>
))}
</ol>
</div>
<div className="flex items-center gap-3">
<button
onClick={() => setPendingDryRun(false)}
disabled={flowRun.status === 'running'}
className="rounded-lg bg-foreground text-background px-4 py-2 text-sm font-medium hover:opacity-90 disabled:opacity-50 transition-opacity"
>
Run
</button>
<button
onClick={() => setPendingDryRun(true)}
disabled={flowRun.status === 'running'}
className="rounded-lg border border-border px-4 py-2 text-sm hover:bg-muted/50 disabled:opacity-50 transition-colors"
>
Dry Run
</button>
{flowRun.status !== 'idle' && flowRun.status !== 'running' && (
<button
onClick={handleReset}
className="text-xs text-muted-foreground hover:text-foreground transition-colors"
>
Reset
</button>
)}
</div>
{flowRun.status !== 'idle' && (
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Output</span>
{completedFlowRunId && (
<Link
href={`/audit/${completedFlowRunId}`}
className="text-xs text-muted-foreground hover:text-foreground transition-colors"
>
View in audit log
</Link>
)}
</div>
<StreamingTerminal
lines={flowRun.lines}
status={flowRun.status}
error={flowRun.error}
/>
</div>
)}
<ConfirmDialog
open={pendingDryRun !== null}
title={pendingDryRun ? 'Dry Run: Redeploy All' : 'Run: Redeploy All'}
commandPreview={
pendingDryRun
? `[DRY RUN] flow: ${FLOW_KEY}\n\nAll steps will be shown without executing.`
: `flow: ${FLOW_KEY}\n\nSteps:\n${STEPS.map((s, i) => ` ${i + 1}. ${s}`).join('\n')}`
}
onConfirm={handleConfirm}
onCancel={() => setPendingDryRun(null)}
/>
</div>
)
}

View file

@ -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 (
<div className="min-h-screen bg-background p-6">
<div className="mx-auto max-w-4xl space-y-6">
<div className="flex items-center gap-3">
<Link href="/" className="text-sm text-muted-foreground hover:text-foreground">
Home
</Link>
<span className="text-muted-foreground">/</span>
<h1 className="text-2xl font-semibold tracking-tight">Redeploy All</h1>
</div>
<FlowPanel />
</div>
</div>
)
}

View file

@ -107,6 +107,20 @@ commands:
- ops-dashboard - ops-dashboard
description: "Build a docker compose service image" 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: docker_compose_up:
cmd: ["docker", "compose", "up", "-d"] cmd: ["docker", "compose", "up", "-d"]
cwd: "/srv/scrum4me/compose" cwd: "/srv/scrum4me/compose"

View file

@ -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

View file

@ -2,15 +2,21 @@
# Copy to /etc/ops-agent/flows/update_mcp_worker.yml on the host. # Copy to /etc/ops-agent/flows/update_mcp_worker.yml on the host.
# #
# Steps: # Steps:
# 1. Show current git status (informational) # 1. Show current git status of scrum4me-docker (informational)
# 2. Fetch remote refs # 2. Fetch remote refs for scrum4me-docker
# 3. Fast-forward pull (aborts if working tree is dirty) # 3. Fast-forward pull scrum4me-docker (aborts if working tree is dirty)
# 4. Rebuild the Docker image # 4. Fast-forward pull scrum4me-mcp — sync van de lokale repo. De image
# 5. Recreate the container in detached mode (force-recreate picks up new image) # cloned MCP zelf van GitHub, dus dit is alleen lokale referentie;
# 6. Wait for worker pre-flight to pass (checks /var/log/agent/current) # 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 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: steps:
- command_key: git_status - command_key: git_status
args: ["/srv/scrum4me/repos/scrum4me-docker"] args: ["/srv/scrum4me/repos/scrum4me-docker"]
@ -24,8 +30,11 @@ steps:
args: ["/srv/scrum4me/repos/scrum4me-docker"] args: ["/srv/scrum4me/repos/scrum4me-docker"]
on_failure: abort on_failure: abort
- command_key: docker_compose_build - command_key: git_pull
args: ["worker-idea"] args: ["/srv/scrum4me/repos/scrum4me-mcp"]
on_failure: continue
- command_key: docker_compose_build_worker_fresh
on_failure: abort on_failure: abort
- command_key: docker_compose_up_recreate - command_key: docker_compose_up_recreate