Files
BrowserOS/packages/browseros-agent/tools/dev/cmd/cleanup.go
Nikhil ffc0f09c86 feat(dev): add target-aware reset cleanup (#893)
* feat(dev): add target-aware reset cleanup

* fix(dev): address cleanup reset review comments
2026-04-30 13:34:52 -07:00

133 lines
4.3 KiB
Go

package cmd
import (
"bufio"
"fmt"
"io"
"os"
"time"
"browseros-dev/proc"
"github.com/spf13/cobra"
)
var cleanupCmd = &cobra.Command{
Use: "cleanup",
Short: "Kill target processes and remove orphaned temp directories",
Long: "Stops target BrowserOS processes, clears target ports, and removes target temp directories.",
RunE: runCleanup,
}
var (
cleanupOnlyPorts bool
cleanupOnlyTemps bool
cleanupQuick bool
cleanupYes bool
cleanupTarget string
cleanupBrowserOSDir string
cleanupPortsValue string
cleanupBrowserUserDataDir string
)
type safeCleanupOptions struct {
ports bool
temps bool
}
func init() {
cleanupCmd.Flags().StringVar(&cleanupTarget, "target", targetDev, "Cleanup target: dev, dogfood, or prod")
cleanupCmd.Flags().StringVar(&cleanupBrowserOSDir, "browseros-dir", "", "Override target BrowserOS state directory")
cleanupCmd.Flags().StringVar(&cleanupPortsValue, "ports", "", "Override ports as cdp,server,extension")
cleanupCmd.Flags().StringVar(&cleanupBrowserUserDataDir, "browser-user-data-dir", "", "Override BrowserOS user-data dir to stop")
cleanupCmd.Flags().BoolVar(&cleanupOnlyPorts, "only-ports", false, "Only kill port processes")
cleanupCmd.Flags().BoolVar(&cleanupOnlyTemps, "only-temps", false, "Only remove temp directories")
cleanupCmd.Flags().BoolVar(&cleanupQuick, "quick", false, "Run safe cleanup only")
cleanupCmd.Flags().BoolVar(&cleanupYes, "yes", false, "Answer yes to the safe cleanup prompt")
rootCmd.AddCommand(cleanupCmd)
}
// runCleanup performs the non-destructive daily cleanup path for local dev.
func runCleanup(cmd *cobra.Command, args []string) error {
out := cmd.OutOrStdout()
root, err := proc.FindMonorepoRoot()
if err != nil {
return err
}
target, err := resolveResetTarget(root, resetTargetOptions{
Target: cleanupTarget,
BrowserOSDir: cleanupBrowserOSDir,
Ports: cleanupPortsValue,
BrowserUserDataDir: cleanupBrowserUserDataDir,
})
if err != nil {
return err
}
if !cleanupYes && !cleanupQuick {
ok, err := confirmYesNo(out, bufio.NewReader(os.Stdin), resetPrompt{
Title: "Run safe cleanup?",
Body: fmt.Sprintf("Stops %s processes, clears target ports, and removes target temp profiles. This does not touch saved BrowserOS data, Lima, containers, or images.", target.Name),
Action: "Run safe cleanup for " + target.Name,
})
if err != nil {
return err
}
if !ok {
fmt.Fprintln(out, dimStyle.Sprint("Skipped."))
return nil
}
}
if err := ensureTargetStopped(out, target); err != nil {
return err
}
return runSafeCleanup(out, target, safeCleanupOptions{
ports: !cleanupOnlyTemps || cleanupOnlyPorts,
temps: !cleanupOnlyPorts || cleanupOnlyTemps,
})
}
// runSafeCleanup is shared by cleanup and reset before any destructive repair steps.
func runSafeCleanup(out io.Writer, target resetTarget, opts safeCleanupOptions) error {
if opts.ports {
if target.WatchRunStateDir != "" {
stopped, err := proc.StopAllWatchProcessesInDir(target.WatchRunStateDir, 3*time.Second)
if err != nil {
return err
}
if stopped > 0 {
fmt.Fprintf(out, "%s stopped %d old %s watch process group(s)\n", successStyle.Sprint("Stopped:"), stopped, target.Name)
}
}
if len(target.BrowserUserDataDirs) > 0 {
killedBrowsers, err := proc.KillBrowserProcessesForUserDataDirs(target.BrowserUserDataDirs, 3*time.Second)
if err != nil {
return err
}
if killedBrowsers > 0 {
fmt.Fprintf(out, "%s stopped %d BrowserOS %s profile process(es)\n", successStyle.Sprint("Stopped:"), killedBrowsers, target.Name)
}
}
if target.Ports != nil {
ports := *target.Ports
fmt.Fprintf(out, "%s ports %d, %d, %d\n", labelStyle.Sprint("Clearing:"), ports.CDP, ports.Server, ports.Extension)
if err := proc.KillPortsAndWait(ports, 3*time.Second); err != nil {
return err
}
fmt.Fprintln(out, successStyle.Sprint("Ports cleared."))
}
}
if opts.temps {
n := proc.CleanupTempDirs(target.TempPrefixes...)
if n > 0 {
fmt.Fprintf(out, "%s removed %d temp directories\n", successStyle.Sprint("Removed:"), n)
} else if len(target.TempPrefixes) > 0 {
fmt.Fprintln(out, dimStyle.Sprint("No orphaned temp directories found."))
}
}
fmt.Fprintln(out)
fmt.Fprintln(out, successStyle.Sprint("Cleanup complete."))
return nil
}