From 168b81f10460308ce7cec9bb2c4506a761c0f781 Mon Sep 17 00:00:00 2001 From: Hein Date: Wed, 8 Apr 2026 13:33:39 +0200 Subject: [PATCH] feat(cmd): add startall and stopall commands for unit management * Implement startall to enable and start all installed units * Implement stopall to stop and disable all running units --- cmd/{active.go => startall.go} | 15 ++++----- cmd/stopall.go | 60 ++++++++++++++++++++++++++++++++++ runtime/docker.go | 4 +-- runtime/podman.go | 4 +-- 4 files changed, 71 insertions(+), 12 deletions(-) rename cmd/{active.go => startall.go} (76%) create mode 100644 cmd/stopall.go diff --git a/cmd/active.go b/cmd/startall.go similarity index 76% rename from cmd/active.go rename to cmd/startall.go index d2946f3..c3fc830 100644 --- a/cmd/active.go +++ b/cmd/startall.go @@ -9,19 +9,19 @@ import ( "github.com/warkanum/unitdore/systemd" ) -var activeCmd = &cobra.Command{ - Use: "active", +var startallCmd = &cobra.Command{ + Use: "startall", Short: "Enable and start all installed, enabled units", - Long: `Active runs 'systemctl enable --now' for all enabled units that have + Long: `Startall runs 'systemctl enable --now' for all enabled units that have been installed. Units must be installed first via 'unitdore install'.`, - RunE: runActive, + RunE: runStartall, } func init() { - rootCmd.AddCommand(activeCmd) + rootCmd.AddCommand(startallCmd) } -func runActive(cmd *cobra.Command, args []string) error { +func runStartall(cmd *cobra.Command, args []string) error { cfg, err := config.Load(configPath) if err != nil { return err @@ -29,7 +29,6 @@ func runActive(cmd *cobra.Command, args []string) error { prefix, suffix := cfg.Prefix, cfg.Suffix - // Sort by order then name for deterministic startup sequence units := make([]config.Unit, len(cfg.Units)) copy(units, cfg.Units) sort.Slice(units, func(i, j int) bool { @@ -50,7 +49,7 @@ func runActive(cmd *cobra.Command, args []string) error { fmt.Printf(" ! %s: not installed — run 'unitdore install' first\n", u.Name) continue } - fmt.Printf(" ▶ enabling: %s...\n", systemd.ServiceName(u, prefix, suffix)) + fmt.Printf(" ▶ starting: %s...\n", systemd.ServiceName(u, prefix, suffix)) if err := systemd.Enable(u, prefix, suffix); err != nil { fmt.Printf(" ✗ failed: %s: %v\n", u.Name, err) failed++ diff --git a/cmd/stopall.go b/cmd/stopall.go new file mode 100644 index 0000000..7642c45 --- /dev/null +++ b/cmd/stopall.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "fmt" + "sort" + + "github.com/spf13/cobra" + "github.com/warkanum/unitdore/config" + "github.com/warkanum/unitdore/systemd" +) + +var stopallCmd = &cobra.Command{ + Use: "stopall", + Short: "Stop and disable all running managed units", + Long: `Stopall runs 'systemctl disable --now' for all enabled, installed units.`, + RunE: runStopall, +} + +func init() { + rootCmd.AddCommand(stopallCmd) +} + +func runStopall(cmd *cobra.Command, args []string) error { + cfg, err := config.Load(configPath) + if err != nil { + return err + } + + prefix, suffix := cfg.Prefix, cfg.Suffix + + // Reverse order for shutdown + units := make([]config.Unit, len(cfg.Units)) + copy(units, cfg.Units) + sort.Slice(units, func(i, j int) bool { + if units[i].Order != units[j].Order { + return units[i].Order > units[j].Order + } + return units[i].Name > units[j].Name + }) + + stopped := 0 + failed := 0 + + for _, u := range units { + if !systemd.IsInstalled(u, prefix, suffix) { + continue + } + fmt.Printf(" ■ stopping: %s...\n", systemd.ServiceName(u, prefix, suffix)) + if err := systemd.Disable(u, prefix, suffix); err != nil { + fmt.Printf(" ✗ failed: %s: %v\n", u.Name, err) + failed++ + } else { + fmt.Printf(" ✓ stopped: %s\n", u.Name) + stopped++ + } + } + + fmt.Printf("\nDone. Stopped: %d Failed: %d\n", stopped, failed) + return nil +} diff --git a/runtime/docker.go b/runtime/docker.go index a29fbe7..c05cea8 100644 --- a/runtime/docker.go +++ b/runtime/docker.go @@ -68,9 +68,9 @@ func (d *Docker) Exists(name string) (bool, error) { return false, nil } - out, err := exec.Command(bin, "ps", "-a", "--format", "{{.Names}}").Output() + out, err := exec.Command(bin, "ps", "--format", "{{.Names}}").Output() if err != nil { - return false, fmt.Errorf("docker ps -a: %w", err) + return false, fmt.Errorf("docker ps: %w", err) } for _, line := range strings.Split(string(out), "\n") { diff --git a/runtime/podman.go b/runtime/podman.go index 0c0b459..3457733 100644 --- a/runtime/podman.go +++ b/runtime/podman.go @@ -68,9 +68,9 @@ func (p *Podman) Exists(name string) (bool, error) { return false, nil } - out, err := exec.Command(bin, "ps", "-a", "--format", "{{.Names}}").Output() + out, err := exec.Command(bin, "ps", "--format", "{{.Names}}").Output() if err != nil { - return false, fmt.Errorf("podman ps -a: %w", err) + return false, fmt.Errorf("podman ps: %w", err) } for _, line := range strings.Split(string(out), "\n") {