-
-
Notifications
You must be signed in to change notification settings - Fork 104
feat: Add Restart All Processes functionality #436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Implemented RestartAllProcesses method in the API and ProjectRunner. - Added corresponding route for restarting all processes. - Created client method to call the restart all processes API. - Integrated restart all processes action in TUI with confirmation dialog. - Updated shortcuts and help text for the new action.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a “Restart All Processes” capability across the backend API, client, and TUI so users can restart the whole process set in one operation instead of restarting each process individually.
Changes:
- Extended the core runner (
ProjectRunner) andIProjectinterface with aRestartAllProcessesoperation that stops all running processes in a controlled order, resets internal state, and restarts them using the initial run order. - Exposed the restart-all behavior over HTTP via a new
/processes/restartAPI route, added a corresponding client method, and wired it into the TUI’s shortcuts/help footer with a confirmation dialog andCtrl+Ubinding. - Updated TUI shortcut configuration and help text to include the new “Restart All” action alongside existing process controls.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/tui/view.go |
Wires the new ActionProcessRestartAll shortcut to handleRestartAll and adds “Restart All” to the help footer so the action is discoverable in the TUI. |
src/tui/proc-table.go |
Implements the confirmation modal and execution path (handleRestartAll / executeRestartAll) that invoke RestartAllProcesses with a progress indicator and error reporting. |
src/tui/actions.go |
Defines the ActionProcessRestartAll action name, default Ctrl+U shortcut, ordering, and description for display in the help/footer UI. |
src/client/restart.go |
Adds a low-level restartAllProcesses HTTP helper posting to /processes/restart, mirroring the existing single-process restart client. |
src/client/client.go |
Exposes RestartAllProcesses as part of the public PcClient API used by the TUI and other callers. |
src/app/project_runner.go |
Extends ProjectRunner with restart-all state (isRestartingAll), adjusts the main run loop to stay alive during restart-all, and implements RestartAllProcesses to orchestrate ordered shutdown, state reset, and restart. |
src/app/project_interface.go |
Extends the IProject interface with a RestartAllProcesses method so both local and remote project implementations can support the new behavior. |
src/api/routes.go |
Registers a new POST /processes/restart route, delegating to the API handler for restart-all. |
src/api/pc_api.go |
Adds the RestartAllProcesses HTTP handler with Swagger annotations, invoking the project’s restart-all logic and returning a simple status payload. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (p *ProjectRunner) RestartAllProcesses() error { | ||
| log.Info().Msg("Restarting all processes") | ||
|
|
||
| // Set flag to prevent main loop from exiting when process count reaches 0 | ||
| p.restartAllMutex.Lock() | ||
| p.isRestartingAll = true | ||
| p.restartAllMutex.Unlock() | ||
|
|
||
| // Build shutdown order | ||
| p.runProcMutex.Lock() | ||
| shutdownOrder := []*Process{} | ||
| if p.isOrderedShutdown { | ||
| err := p.project.WithProcesses([]string{}, func(process types.ProcessConfig) error { | ||
| if runningProc, ok := p.runningProcesses[process.ReplicaName]; ok { | ||
| shutdownOrder = append(shutdownOrder, runningProc) | ||
| } | ||
| return nil | ||
| }) | ||
| if err != nil { | ||
| log.Error().Msgf("Failed to build project run order: %s", err.Error()) | ||
| } | ||
| slices.Reverse(shutdownOrder) | ||
| } else { | ||
| for _, proc := range p.runningProcesses { | ||
| shutdownOrder = append(shutdownOrder, proc) | ||
| } | ||
| } | ||
| p.runProcMutex.Unlock() | ||
|
|
||
| var nameOrder []string | ||
| for _, v := range shutdownOrder { | ||
| nameOrder = append(nameOrder, v.getName()) | ||
| } | ||
| log.Debug().Msgf("Stopping %d processes for restart. Order: %q", len(shutdownOrder), nameOrder) | ||
|
|
||
| // Prepare all processes for shutdown (prevents auto-restart) | ||
| for _, proc := range shutdownOrder { | ||
| proc.prepareForShutDown() | ||
| } | ||
|
|
||
| // Stop all processes | ||
| p.shutDownAndWait(shutdownOrder) | ||
|
|
||
| // Clear done processes map | ||
| p.doneProcMutex.Lock() | ||
| p.doneProcesses = make(map[string]*Process) | ||
| p.doneProcMutex.Unlock() | ||
|
|
||
| // Reset process states to pending | ||
| p.statesMutex.Lock() | ||
| for name, state := range p.processStates { | ||
| state.Status = types.ProcessStatePending | ||
| state.Pid = 0 | ||
| state.ExitCode = 0 | ||
| state.IsRunning = false | ||
| p.processStates[name] = state | ||
| } | ||
| p.statesMutex.Unlock() | ||
|
|
||
| // Build run order (same as initial Run()) | ||
| runOrder := []types.ProcessConfig{} | ||
| err := p.project.WithProcesses([]string{}, func(process types.ProcessConfig) error { | ||
| if process.IsDeferred() { | ||
| return nil | ||
| } | ||
| runOrder = append(runOrder, process) | ||
| return nil | ||
| }) | ||
| if err != nil { | ||
| // Clear the restart flag on error | ||
| p.restartAllMutex.Lock() | ||
| p.isRestartingAll = false | ||
| p.restartAllMutex.Unlock() | ||
| return fmt.Errorf("failed to build project run order: %w", err) | ||
| } | ||
|
|
||
| var startOrder []string | ||
| for _, v := range runOrder { | ||
| startOrder = append(startOrder, v.ReplicaName) | ||
| } | ||
| log.Debug().Msgf("Starting %d processes. Order: %q", len(runOrder), startOrder) | ||
|
|
||
| // Start all processes | ||
| for _, proc := range runOrder { | ||
| if proc.Schedule != nil && proc.Schedule.IsScheduled() { | ||
| continue | ||
| } | ||
| newConf := proc | ||
| p.runProcess(&newConf) | ||
| } | ||
|
|
||
| // Clear the restart flag now that new processes are started | ||
| p.restartAllMutex.Lock() | ||
| p.isRestartingAll = false | ||
| p.restartAllMutex.Unlock() | ||
|
|
||
| log.Info().Msg("All processes restarted") | ||
| return nil | ||
| } |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RestartAllProcesses introduces non-trivial shutdown and restart sequencing logic (including ordered shutdown, restart flags, and state resets), but there are currently no tests covering this method or the /processes/restart flow, while similar behavior (e.g., RestartProcess and other runner operations) is covered in project_runner_test.go and system_test.go. It would be valuable to add unit / system tests that exercise the restart-all path (including ordered vs. unordered shutdown and interaction with scheduled processes) to prevent regressions in this central control-flow.
|


A common workflow in many projects is to run a set of services, make changes, build and restart the services. In process compose TUI the only path to accomplish this is to individually restart each process which is tedious.
This PR implements a "Restart All" shortcut that stops all of the processes and reruns them the same way it does when starting for the first time.
The shortcut is "Ctrl+U" and is made available in the footer as well as the "Shortcuts" menu.
Below is a summary of the changes included:
Screencast_20260123_161747.webm