From 7bc52ac8c5528ef4ca38a40a272423993ea13976 Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Mon, 12 Jan 2026 21:00:47 +0000 Subject: [PATCH] Re-run setup and cleanup scripts (#1981) ## Summary I've implemented the "re-run setup/cleanup script" functionality in the new UI command bar. The changes include: ### Files Modified 1. **`frontend/src/components/ui-new/actions/index.ts`** - Added `isAttemptRunning: boolean` to `ActionVisibilityContext` interface (line 109) - Added `RunSetupScript` and `RunCleanupScript` workspace actions (lines 737-777) - Both actions use the existing `attemptsApi.runSetupScript/runCleanupScript` methods - Actions are disabled when `isAttemptRunning` is true - Error handling for `no_script_configured` and `process_already_running` errors 2. **`frontend/src/components/ui-new/actions/useActionVisibility.ts`** - Import `useExecutionProcessesContext` - Added `isAttemptRunningVisible` to the hook call - Added `isAttemptRunning: isAttemptRunningVisible` to the returned context 3. **`frontend/src/components/ui-new/actions/pages.ts`** - Added a new "Scripts" group to the `workspaceActions` page containing both actions ### Testing The dev server is running at: - Frontend: http://localhost:3000/ - Backend: http://127.0.0.1:3001 To test: 1. Navigate to the new UI (`/workspaces`) 2. Select a workspace 3. Open the command bar (CMD+K) 4. Navigate to "Workspace Actions" 5. You should see a "Scripts" group with "Run Setup Script" and "Run Cleanup Script" options --- .../src/components/ui-new/actions/index.ts | 46 +++++++++++++++++++ .../src/components/ui-new/actions/pages.ts | 8 ++++ .../ui-new/actions/useActionVisibility.ts | 4 ++ 3 files changed, 58 insertions(+) diff --git a/frontend/src/components/ui-new/actions/index.ts b/frontend/src/components/ui-new/actions/index.ts index 02aca096..a017bf42 100644 --- a/frontend/src/components/ui-new/actions/index.ts +++ b/frontend/src/components/ui-new/actions/index.ts @@ -104,6 +104,9 @@ export interface ActionVisibilityContext { hasMultipleRepos: boolean; hasOpenPR: boolean; hasUnpushedCommits: boolean; + + // Execution state + isAttemptRunning: boolean; } // Base properties shared by all actions @@ -729,6 +732,49 @@ export const Actions = { invalidateWorkspaceQueries(ctx.queryClient, workspaceId); }, }, + + // === Script Actions === + RunSetupScript: { + id: 'run-setup-script', + label: 'Run Setup Script', + icon: TerminalIcon, + requiresTarget: true, + isVisible: (ctx) => ctx.hasWorkspace, + isEnabled: (ctx) => !ctx.isAttemptRunning, + execute: async (_ctx, workspaceId) => { + const result = await attemptsApi.runSetupScript(workspaceId); + if (!result.success) { + if (result.error?.type === 'no_script_configured') { + throw new Error('No setup script configured for this project'); + } + if (result.error?.type === 'process_already_running') { + throw new Error('Cannot run script while another process is running'); + } + throw new Error('Failed to run setup script'); + } + }, + }, + + RunCleanupScript: { + id: 'run-cleanup-script', + label: 'Run Cleanup Script', + icon: TerminalIcon, + requiresTarget: true, + isVisible: (ctx) => ctx.hasWorkspace, + isEnabled: (ctx) => !ctx.isAttemptRunning, + execute: async (_ctx, workspaceId) => { + const result = await attemptsApi.runCleanupScript(workspaceId); + if (!result.success) { + if (result.error?.type === 'no_script_configured') { + throw new Error('No cleanup script configured for this project'); + } + if (result.error?.type === 'process_already_running') { + throw new Error('Cannot run script while another process is running'); + } + throw new Error('Failed to run cleanup script'); + } + }, + }, } as const satisfies Record; // Helper to resolve dynamic label diff --git a/frontend/src/components/ui-new/actions/pages.ts b/frontend/src/components/ui-new/actions/pages.ts index f8f85ae9..1aed61d8 100644 --- a/frontend/src/components/ui-new/actions/pages.ts +++ b/frontend/src/components/ui-new/actions/pages.ts @@ -110,6 +110,14 @@ export const Pages: Record = { { type: 'action', action: Actions.DeleteWorkspace }, ], }, + { + type: 'group', + label: 'Scripts', + items: [ + { type: 'action', action: Actions.RunSetupScript }, + { type: 'action', action: Actions.RunCleanupScript }, + ], + }, ], }, diff --git a/frontend/src/components/ui-new/actions/useActionVisibility.ts b/frontend/src/components/ui-new/actions/useActionVisibility.ts index 2fed226d..a8e8e03f 100644 --- a/frontend/src/components/ui-new/actions/useActionVisibility.ts +++ b/frontend/src/components/ui-new/actions/useActionVisibility.ts @@ -6,6 +6,7 @@ import { useWorkspaceContext } from '@/contexts/WorkspaceContext'; import { useUserSystem } from '@/components/ConfigProvider'; import { useDevServer } from '@/hooks/useDevServer'; import { useBranchStatus } from '@/hooks/useBranchStatus'; +import { useExecutionProcessesContext } from '@/contexts/ExecutionProcessesContext'; import type { Workspace, Merge } from 'shared/types'; import type { ActionVisibilityContext, @@ -31,6 +32,7 @@ export function useActionVisibilityContext(): ActionVisibilityContext { const { isStarting, isStopping, runningDevServers } = useDevServer(workspaceId); const { data: branchStatus } = useBranchStatus(workspaceId); + const { isAttemptRunningVisible } = useExecutionProcessesContext(); return useMemo(() => { // Compute isAllDiffsExpanded @@ -79,6 +81,7 @@ export function useActionVisibilityContext(): ActionVisibilityContext { hasMultipleRepos: repos.length > 1, hasOpenPR, hasUnpushedCommits, + isAttemptRunning: isAttemptRunningVisible, }; }, [ layout.isChangesMode, @@ -98,6 +101,7 @@ export function useActionVisibilityContext(): ActionVisibilityContext { isStopping, runningDevServers, branchStatus, + isAttemptRunningVisible, ]); }