From 9f590f1732e696b845f9f24032c2a48af7efbb5c Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Wed, 13 May 2026 19:42:39 +0200 Subject: [PATCH] feat(flows): add update_scrum4me_web flow and UI page - Update ops-agent/flows.example/update_scrum4me_web.yml with full deployment steps: git_status, git_fetch, git_log_ahead, git_pull, npm_ci, prisma_migrate_deploy, npm_run_build, systemctl_restart, and smoke test against thuis.jp-visser.nl/api/products - Add npm_ci, prisma_migrate_deploy, npm_run_build, and curl_smoke_scrum4me_thuis to commands.yml.example - Add /flows/update-scrum4me-web UI page with Run and Dry Run buttons, streaming terminal output, and link to audit log on completion Co-Authored-By: Claude Sonnet 4.6 --- .../_components/flow-panel.tsx | 128 ++++++++++++++++++ app/flows/update-scrum4me-web/page.tsx | 27 ++++ ops-agent/commands.yml.example | 24 ++++ .../flows.example/update_scrum4me_web.yml | 47 +++++-- 4 files changed, 211 insertions(+), 15 deletions(-) create mode 100644 app/flows/update-scrum4me-web/_components/flow-panel.tsx create mode 100644 app/flows/update-scrum4me-web/page.tsx diff --git a/app/flows/update-scrum4me-web/_components/flow-panel.tsx b/app/flows/update-scrum4me-web/_components/flow-panel.tsx new file mode 100644 index 0000000..cd11241 --- /dev/null +++ b/app/flows/update-scrum4me-web/_components/flow-panel.tsx @@ -0,0 +1,128 @@ +'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 = 'update_scrum4me_web' + +const STEPS = [ + 'git status (show current state)', + 'git fetch (fetch remote refs)', + 'git log (commits ahead of upstream)', + 'git pull --ff-only (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)', +] + +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 ( +
+
+
+

+ Reproduces the Scrum4Me website update: pulls latest code, installs + dependencies, applies migrations, builds, restarts the service, and + verifies the endpoint is responding. +

+

+ repo: /srv/scrum4me/repos/Scrum4Me +

+
+
    + {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/update-scrum4me-web/page.tsx b/app/flows/update-scrum4me-web/page.tsx new file mode 100644 index 0000000..db15971 --- /dev/null +++ b/app/flows/update-scrum4me-web/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 UpdateScrum4MeWebPage() { + const user = await getCurrentUser() + if (!user) redirect('/login') + + return ( +
+
+
+ + ← Home + + / +

Update Scrum4Me Web

+
+ + +
+
+ ) +} diff --git a/ops-agent/commands.yml.example b/ops-agent/commands.yml.example index e4c5197..9bd052e 100644 --- a/ops-agent/commands.yml.example +++ b/ops-agent/commands.yml.example @@ -162,3 +162,27 @@ commands: cmd: ["docker", "compose", "ps", "--filter", "status=running", "worker-idea"] cwd: "/srv/scrum4me/compose" description: "Verify worker-idea container is in the running state" + + # ── 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" diff --git a/ops-agent/flows.example/update_scrum4me_web.yml b/ops-agent/flows.example/update_scrum4me_web.yml index 34a36fd..8f924aa 100644 --- a/ops-agent/flows.example/update_scrum4me_web.yml +++ b/ops-agent/flows.example/update_scrum4me_web.yml @@ -1,31 +1,48 @@ -# Deploy the latest Scrum4Me web image. +# Deploy the latest Scrum4Me web application from source. # Copy to /etc/ops-agent/flows/update_scrum4me_web.yml on the host. # # Steps: -# 1. Fetch remote refs -# 2. Fast-forward pull (aborts if working tree is dirty) -# 3. Rebuild the Docker image -# 4. Recreate the container in detached mode -# 5. Smoke-test the public endpoint +# 1. Show current git status (dirty tree aborts later at git_pull) +# 2. Fetch remote refs +# 3. Show commits ahead of upstream +# 4. Fast-forward pull (aborts if working tree is dirty) +# 5. Install dependencies +# 6. Apply database migrations +# 7. Build the application +# 8. Restart the systemd service +# 9. Smoke-test the public endpoint (200 or 401 = pass) name: Update Scrum4Me Web -description: Pull latest code, rebuild Docker image, and restart the Scrum4Me web service +description: Pull latest code, install deps, run migrations, build, and restart scrum4me-web.service steps: + - command_key: git_status + args: ["/srv/scrum4me/repos/Scrum4Me"] + on_failure: continue + - command_key: git_fetch - args: ["/srv/scrum4me"] + 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"] + args: ["/srv/scrum4me/repos/Scrum4Me"] on_failure: abort - - command_key: docker_compose_build + - 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: docker_compose_up - args: ["scrum4me-web"] - on_failure: abort - - - command_key: curl_smoke_scrum4me_web + - command_key: curl_smoke_scrum4me_thuis on_failure: continue