diff options
| author | bndw <ben@bdw.to> | 2026-01-23 21:10:04 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-01-23 21:10:04 -0800 |
| commit | b5d97f633c960a826577fd80cb1d29e392dce34b (patch) | |
| tree | 312b934506f835bcc77c4dcbb9e38a27efbf1528 | |
| parent | 98b9af372025595e8a4255538e2836e019311474 (diff) | |
Move default host from config file to state.json
Instead of a separate ~/.config/deploy/config file, the default host
is now stored as default_host in state.json. This simplifies the
config and keeps all state in one place.
The init command now automatically sets the default host if none
is configured.
| -rw-r--r-- | cmd/deploy/deploy.go | 9 | ||||
| -rw-r--r-- | cmd/deploy/env.go | 24 | ||||
| -rw-r--r-- | cmd/deploy/init.go | 28 | ||||
| -rw-r--r-- | cmd/deploy/list.go | 24 | ||||
| -rw-r--r-- | cmd/deploy/manage.go | 93 | ||||
| -rw-r--r-- | internal/config/config.go | 65 | ||||
| -rw-r--r-- | internal/state/state.go | 13 |
7 files changed, 84 insertions, 172 deletions
diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index 2b3ab4a..ee7ee4a 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go | |||
| @@ -9,7 +9,6 @@ import ( | |||
| 9 | "strconv" | 9 | "strconv" |
| 10 | "strings" | 10 | "strings" |
| 11 | 11 | ||
| 12 | "github.com/bdw/deploy/internal/config" | ||
| 13 | "github.com/bdw/deploy/internal/ssh" | 12 | "github.com/bdw/deploy/internal/ssh" |
| 14 | "github.com/bdw/deploy/internal/state" | 13 | "github.com/bdw/deploy/internal/state" |
| 15 | "github.com/bdw/deploy/internal/templates" | 14 | "github.com/bdw/deploy/internal/templates" |
| @@ -55,14 +54,14 @@ func runDeploy(args []string) { | |||
| 55 | 54 | ||
| 56 | fs.Parse(args) | 55 | fs.Parse(args) |
| 57 | 56 | ||
| 58 | // Get host from flag or config | 57 | // Get host from flag or state default |
| 59 | if *host == "" { | 58 | if *host == "" { |
| 60 | cfg, err := config.Load() | 59 | st, err := state.Load() |
| 61 | if err != nil { | 60 | if err != nil { |
| 62 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | 61 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) |
| 63 | os.Exit(1) | 62 | os.Exit(1) |
| 64 | } | 63 | } |
| 65 | *host = cfg.Host | 64 | *host = st.GetDefaultHost() |
| 66 | } | 65 | } |
| 67 | 66 | ||
| 68 | if *host == "" || *domain == "" { | 67 | if *host == "" || *domain == "" { |
diff --git a/cmd/deploy/env.go b/cmd/deploy/env.go index 135fb77..a43cd6a 100644 --- a/cmd/deploy/env.go +++ b/cmd/deploy/env.go | |||
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | "os" | 6 | "os" |
| 7 | "strings" | 7 | "strings" |
| 8 | 8 | ||
| 9 | "github.com/bdw/deploy/internal/config" | ||
| 10 | "github.com/bdw/deploy/internal/ssh" | 9 | "github.com/bdw/deploy/internal/ssh" |
| 11 | "github.com/bdw/deploy/internal/state" | 10 | "github.com/bdw/deploy/internal/state" |
| 12 | ) | 11 | ) |
| @@ -29,14 +28,16 @@ func runEnv(args []string) { | |||
| 29 | 28 | ||
| 30 | name := fs.Args()[0] | 29 | name := fs.Args()[0] |
| 31 | 30 | ||
| 32 | // Get host from flag or config | 31 | // Load state |
| 32 | st, err := state.Load() | ||
| 33 | if err != nil { | ||
| 34 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 35 | os.Exit(1) | ||
| 36 | } | ||
| 37 | |||
| 38 | // Get host from flag or state default | ||
| 33 | if *host == "" { | 39 | if *host == "" { |
| 34 | cfg, err := config.Load() | 40 | *host = st.GetDefaultHost() |
| 35 | if err != nil { | ||
| 36 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 37 | os.Exit(1) | ||
| 38 | } | ||
| 39 | *host = cfg.Host | ||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | if *host == "" { | 43 | if *host == "" { |
| @@ -45,13 +46,6 @@ func runEnv(args []string) { | |||
| 45 | os.Exit(1) | 46 | os.Exit(1) |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | // Load state | ||
| 49 | st, err := state.Load() | ||
| 50 | if err != nil { | ||
| 51 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 52 | os.Exit(1) | ||
| 53 | } | ||
| 54 | |||
| 55 | // Get app info | 49 | // Get app info |
| 56 | app, err := st.GetApp(*host, name) | 50 | app, err := st.GetApp(*host, name) |
| 57 | if err != nil { | 51 | if err != nil { |
diff --git a/cmd/deploy/init.go b/cmd/deploy/init.go index 72c7d53..1713879 100644 --- a/cmd/deploy/init.go +++ b/cmd/deploy/init.go | |||
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | "os" | 6 | "os" |
| 7 | "strings" | 7 | "strings" |
| 8 | 8 | ||
| 9 | "github.com/bdw/deploy/internal/config" | ||
| 10 | "github.com/bdw/deploy/internal/ssh" | 9 | "github.com/bdw/deploy/internal/ssh" |
| 11 | "github.com/bdw/deploy/internal/state" | 10 | "github.com/bdw/deploy/internal/state" |
| 12 | ) | 11 | ) |
| @@ -16,14 +15,16 @@ func runInit(args []string) { | |||
| 16 | host := fs.String("host", "", "VPS host (SSH config alias or user@host)") | 15 | host := fs.String("host", "", "VPS host (SSH config alias or user@host)") |
| 17 | fs.Parse(args) | 16 | fs.Parse(args) |
| 18 | 17 | ||
| 19 | // Get host from flag or config | 18 | // Load state |
| 19 | st, err := state.Load() | ||
| 20 | if err != nil { | ||
| 21 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 22 | os.Exit(1) | ||
| 23 | } | ||
| 24 | |||
| 25 | // Get host from flag or state default | ||
| 20 | if *host == "" { | 26 | if *host == "" { |
| 21 | cfg, err := config.Load() | 27 | *host = st.GetDefaultHost() |
| 22 | if err != nil { | ||
| 23 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 24 | os.Exit(1) | ||
| 25 | } | ||
| 26 | *host = cfg.Host | ||
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | if *host == "" { | 30 | if *host == "" { |
| @@ -115,13 +116,12 @@ import /etc/caddy/sites-enabled/* | |||
| 115 | fmt.Println(" ✓ Caddy is active") | 116 | fmt.Println(" ✓ Caddy is active") |
| 116 | } | 117 | } |
| 117 | 118 | ||
| 118 | // Initialize local state if needed | 119 | // Update state |
| 119 | st, err := state.Load() | ||
| 120 | if err != nil { | ||
| 121 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 122 | os.Exit(1) | ||
| 123 | } | ||
| 124 | st.GetHost(*host) // Ensure host exists in state | 120 | st.GetHost(*host) // Ensure host exists in state |
| 121 | if st.GetDefaultHost() == "" { | ||
| 122 | st.SetDefaultHost(*host) | ||
| 123 | fmt.Printf(" Set %s as default host\n", *host) | ||
| 124 | } | ||
| 125 | if err := st.Save(); err != nil { | 125 | if err := st.Save(); err != nil { |
| 126 | fmt.Fprintf(os.Stderr, "Error saving state: %v\n", err) | 126 | fmt.Fprintf(os.Stderr, "Error saving state: %v\n", err) |
| 127 | os.Exit(1) | 127 | os.Exit(1) |
diff --git a/cmd/deploy/list.go b/cmd/deploy/list.go index b74cf35..ce1605b 100644 --- a/cmd/deploy/list.go +++ b/cmd/deploy/list.go | |||
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | "os" | 6 | "os" |
| 7 | "text/tabwriter" | 7 | "text/tabwriter" |
| 8 | 8 | ||
| 9 | "github.com/bdw/deploy/internal/config" | ||
| 10 | "github.com/bdw/deploy/internal/state" | 9 | "github.com/bdw/deploy/internal/state" |
| 11 | ) | 10 | ) |
| 12 | 11 | ||
| @@ -15,14 +14,16 @@ func runList(args []string) { | |||
| 15 | host := fs.String("host", "", "VPS host (SSH config alias or user@host)") | 14 | host := fs.String("host", "", "VPS host (SSH config alias or user@host)") |
| 16 | fs.Parse(args) | 15 | fs.Parse(args) |
| 17 | 16 | ||
| 18 | // Get host from flag or config | 17 | // Load state |
| 18 | st, err := state.Load() | ||
| 19 | if err != nil { | ||
| 20 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 21 | os.Exit(1) | ||
| 22 | } | ||
| 23 | |||
| 24 | // Get host from flag or state default | ||
| 19 | if *host == "" { | 25 | if *host == "" { |
| 20 | cfg, err := config.Load() | 26 | *host = st.GetDefaultHost() |
| 21 | if err != nil { | ||
| 22 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 23 | os.Exit(1) | ||
| 24 | } | ||
| 25 | *host = cfg.Host | ||
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | if *host == "" { | 29 | if *host == "" { |
| @@ -31,13 +32,6 @@ func runList(args []string) { | |||
| 31 | os.Exit(1) | 32 | os.Exit(1) |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | // Load state | ||
| 35 | st, err := state.Load() | ||
| 36 | if err != nil { | ||
| 37 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 38 | os.Exit(1) | ||
| 39 | } | ||
| 40 | |||
| 41 | apps := st.ListApps(*host) | 35 | apps := st.ListApps(*host) |
| 42 | if len(apps) == 0 { | 36 | if len(apps) == 0 { |
| 43 | fmt.Printf("No deployments found for %s\n", *host) | 37 | fmt.Printf("No deployments found for %s\n", *host) |
diff --git a/cmd/deploy/manage.go b/cmd/deploy/manage.go index 3cee1f4..1f52b92 100644 --- a/cmd/deploy/manage.go +++ b/cmd/deploy/manage.go | |||
| @@ -5,7 +5,6 @@ import ( | |||
| 5 | "fmt" | 5 | "fmt" |
| 6 | "os" | 6 | "os" |
| 7 | 7 | ||
| 8 | "github.com/bdw/deploy/internal/config" | ||
| 9 | "github.com/bdw/deploy/internal/ssh" | 8 | "github.com/bdw/deploy/internal/ssh" |
| 10 | "github.com/bdw/deploy/internal/state" | 9 | "github.com/bdw/deploy/internal/state" |
| 11 | ) | 10 | ) |
| @@ -23,14 +22,16 @@ func runRemove(args []string) { | |||
| 23 | 22 | ||
| 24 | name := fs.Args()[0] | 23 | name := fs.Args()[0] |
| 25 | 24 | ||
| 26 | // Get host from flag or config | 25 | // Load state |
| 26 | st, err := state.Load() | ||
| 27 | if err != nil { | ||
| 28 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 29 | os.Exit(1) | ||
| 30 | } | ||
| 31 | |||
| 32 | // Get host from flag or state default | ||
| 27 | if *host == "" { | 33 | if *host == "" { |
| 28 | cfg, err := config.Load() | 34 | *host = st.GetDefaultHost() |
| 29 | if err != nil { | ||
| 30 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 31 | os.Exit(1) | ||
| 32 | } | ||
| 33 | *host = cfg.Host | ||
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | if *host == "" { | 37 | if *host == "" { |
| @@ -39,13 +40,6 @@ func runRemove(args []string) { | |||
| 39 | os.Exit(1) | 40 | os.Exit(1) |
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | // Load state | ||
| 43 | st, err := state.Load() | ||
| 44 | if err != nil { | ||
| 45 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 46 | os.Exit(1) | ||
| 47 | } | ||
| 48 | |||
| 49 | // Get app info | 43 | // Get app info |
| 50 | app, err := st.GetApp(*host, name) | 44 | app, err := st.GetApp(*host, name) |
| 51 | if err != nil { | 45 | if err != nil { |
| @@ -128,14 +122,16 @@ func runLogs(args []string) { | |||
| 128 | 122 | ||
| 129 | name := fs.Args()[0] | 123 | name := fs.Args()[0] |
| 130 | 124 | ||
| 131 | // Get host from flag or config | 125 | // Load state |
| 126 | st, err := state.Load() | ||
| 127 | if err != nil { | ||
| 128 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 129 | os.Exit(1) | ||
| 130 | } | ||
| 131 | |||
| 132 | // Get host from flag or state default | ||
| 132 | if *host == "" { | 133 | if *host == "" { |
| 133 | cfg, err := config.Load() | 134 | *host = st.GetDefaultHost() |
| 134 | if err != nil { | ||
| 135 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 136 | os.Exit(1) | ||
| 137 | } | ||
| 138 | *host = cfg.Host | ||
| 139 | } | 135 | } |
| 140 | 136 | ||
| 141 | if *host == "" { | 137 | if *host == "" { |
| @@ -144,13 +140,6 @@ func runLogs(args []string) { | |||
| 144 | os.Exit(1) | 140 | os.Exit(1) |
| 145 | } | 141 | } |
| 146 | 142 | ||
| 147 | // Load state to verify app exists | ||
| 148 | st, err := state.Load() | ||
| 149 | if err != nil { | ||
| 150 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 151 | os.Exit(1) | ||
| 152 | } | ||
| 153 | |||
| 154 | app, err := st.GetApp(*host, name) | 143 | app, err := st.GetApp(*host, name) |
| 155 | if err != nil { | 144 | if err != nil { |
| 156 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) | 145 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
| @@ -207,14 +196,16 @@ func runStatus(args []string) { | |||
| 207 | 196 | ||
| 208 | name := fs.Args()[0] | 197 | name := fs.Args()[0] |
| 209 | 198 | ||
| 210 | // Get host from flag or config | 199 | // Load state |
| 200 | st, err := state.Load() | ||
| 201 | if err != nil { | ||
| 202 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 203 | os.Exit(1) | ||
| 204 | } | ||
| 205 | |||
| 206 | // Get host from flag or state default | ||
| 211 | if *host == "" { | 207 | if *host == "" { |
| 212 | cfg, err := config.Load() | 208 | *host = st.GetDefaultHost() |
| 213 | if err != nil { | ||
| 214 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 215 | os.Exit(1) | ||
| 216 | } | ||
| 217 | *host = cfg.Host | ||
| 218 | } | 209 | } |
| 219 | 210 | ||
| 220 | if *host == "" { | 211 | if *host == "" { |
| @@ -223,13 +214,6 @@ func runStatus(args []string) { | |||
| 223 | os.Exit(1) | 214 | os.Exit(1) |
| 224 | } | 215 | } |
| 225 | 216 | ||
| 226 | // Load state to verify app exists | ||
| 227 | st, err := state.Load() | ||
| 228 | if err != nil { | ||
| 229 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 230 | os.Exit(1) | ||
| 231 | } | ||
| 232 | |||
| 233 | app, err := st.GetApp(*host, name) | 217 | app, err := st.GetApp(*host, name) |
| 234 | if err != nil { | 218 | if err != nil { |
| 235 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) | 219 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
| @@ -274,14 +258,16 @@ func runRestart(args []string) { | |||
| 274 | 258 | ||
| 275 | name := fs.Args()[0] | 259 | name := fs.Args()[0] |
| 276 | 260 | ||
| 277 | // Get host from flag or config | 261 | // Load state |
| 262 | st, err := state.Load() | ||
| 263 | if err != nil { | ||
| 264 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 265 | os.Exit(1) | ||
| 266 | } | ||
| 267 | |||
| 268 | // Get host from flag or state default | ||
| 278 | if *host == "" { | 269 | if *host == "" { |
| 279 | cfg, err := config.Load() | 270 | *host = st.GetDefaultHost() |
| 280 | if err != nil { | ||
| 281 | fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err) | ||
| 282 | os.Exit(1) | ||
| 283 | } | ||
| 284 | *host = cfg.Host | ||
| 285 | } | 271 | } |
| 286 | 272 | ||
| 287 | if *host == "" { | 273 | if *host == "" { |
| @@ -290,13 +276,6 @@ func runRestart(args []string) { | |||
| 290 | os.Exit(1) | 276 | os.Exit(1) |
| 291 | } | 277 | } |
| 292 | 278 | ||
| 293 | // Load state to verify app exists | ||
| 294 | st, err := state.Load() | ||
| 295 | if err != nil { | ||
| 296 | fmt.Fprintf(os.Stderr, "Error loading state: %v\n", err) | ||
| 297 | os.Exit(1) | ||
| 298 | } | ||
| 299 | |||
| 300 | app, err := st.GetApp(*host, name) | 279 | app, err := st.GetApp(*host, name) |
| 301 | if err != nil { | 280 | if err != nil { |
| 302 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) | 281 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
diff --git a/internal/config/config.go b/internal/config/config.go deleted file mode 100644 index 8651aa8..0000000 --- a/internal/config/config.go +++ /dev/null | |||
| @@ -1,65 +0,0 @@ | |||
| 1 | package config | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bufio" | ||
| 5 | "os" | ||
| 6 | "path/filepath" | ||
| 7 | "strings" | ||
| 8 | ) | ||
| 9 | |||
| 10 | // Config represents the user's configuration | ||
| 11 | type Config struct { | ||
| 12 | Host string | ||
| 13 | } | ||
| 14 | |||
| 15 | // Load reads config from ~/.config/deploy/config | ||
| 16 | func Load() (*Config, error) { | ||
| 17 | path := configPath() | ||
| 18 | |||
| 19 | // If file doesn't exist, return empty config | ||
| 20 | if _, err := os.Stat(path); os.IsNotExist(err) { | ||
| 21 | return &Config{}, nil | ||
| 22 | } | ||
| 23 | |||
| 24 | file, err := os.Open(path) | ||
| 25 | if err != nil { | ||
| 26 | return nil, err | ||
| 27 | } | ||
| 28 | defer file.Close() | ||
| 29 | |||
| 30 | cfg := &Config{} | ||
| 31 | scanner := bufio.NewScanner(file) | ||
| 32 | for scanner.Scan() { | ||
| 33 | line := strings.TrimSpace(scanner.Text()) | ||
| 34 | if line == "" || strings.HasPrefix(line, "#") { | ||
| 35 | continue | ||
| 36 | } | ||
| 37 | |||
| 38 | parts := strings.SplitN(line, ":", 2) | ||
| 39 | if len(parts) != 2 { | ||
| 40 | continue | ||
| 41 | } | ||
| 42 | |||
| 43 | key := strings.TrimSpace(parts[0]) | ||
| 44 | value := strings.TrimSpace(parts[1]) | ||
| 45 | |||
| 46 | switch key { | ||
| 47 | case "host": | ||
| 48 | cfg.Host = value | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | if err := scanner.Err(); err != nil { | ||
| 53 | return nil, err | ||
| 54 | } | ||
| 55 | |||
| 56 | return cfg, nil | ||
| 57 | } | ||
| 58 | |||
| 59 | func configPath() string { | ||
| 60 | home, err := os.UserHomeDir() | ||
| 61 | if err != nil { | ||
| 62 | return ".deploy-config" | ||
| 63 | } | ||
| 64 | return filepath.Join(home, ".config", "deploy", "config") | ||
| 65 | } | ||
diff --git a/internal/state/state.go b/internal/state/state.go index eb1dee8..def0bcf 100644 --- a/internal/state/state.go +++ b/internal/state/state.go | |||
| @@ -9,7 +9,8 @@ import ( | |||
| 9 | 9 | ||
| 10 | // State represents the entire local deployment state | 10 | // State represents the entire local deployment state |
| 11 | type State struct { | 11 | type State struct { |
| 12 | Hosts map[string]*Host `json:"hosts"` | 12 | DefaultHost string `json:"default_host,omitempty"` |
| 13 | Hosts map[string]*Host `json:"hosts"` | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | // Host represents deployment state for a single VPS | 16 | // Host represents deployment state for a single VPS |
| @@ -137,6 +138,16 @@ func (s *State) ListApps(host string) map[string]*App { | |||
| 137 | return h.Apps | 138 | return h.Apps |
| 138 | } | 139 | } |
| 139 | 140 | ||
| 141 | // GetDefaultHost returns the default host, or empty string if not set | ||
| 142 | func (s *State) GetDefaultHost() string { | ||
| 143 | return s.DefaultHost | ||
| 144 | } | ||
| 145 | |||
| 146 | // SetDefaultHost sets the default host | ||
| 147 | func (s *State) SetDefaultHost(host string) { | ||
| 148 | s.DefaultHost = host | ||
| 149 | } | ||
| 150 | |||
| 140 | // statePath returns the path to the state file | 151 | // statePath returns the path to the state file |
| 141 | func statePath() string { | 152 | func statePath() string { |
| 142 | home, err := os.UserHomeDir() | 153 | home, err := os.UserHomeDir() |
