summaryrefslogtreecommitdiffstats
path: root/cmd/deploy/env.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-01-23 21:52:50 -0800
committerbndw <ben@bdw.to>2026-01-23 21:52:50 -0800
commit87752492d0dc7df3cf78011d5ce315a3eb0cad51 (patch)
tree76843c127fece33f5c28dd7bd533044043478825 /cmd/deploy/env.go
parent57eb67df265a7a6bb544cde83a3be5eadf53fdf2 (diff)
Restructure CLI with Cobra
Replace custom switch-based routing with Cobra for cleaner command hierarchy. Reorganize commands into logical groups: - Root command handles deployment (--binary, --static, --domain, etc.) - App management at top level: list, logs, status, restart, remove - env subcommand group: list, set, unset - host subcommand group: init, status, update, ssh - Standalone: ui (renamed from webui), version Add version command with ldflags support for build info.
Diffstat (limited to 'cmd/deploy/env.go')
-rw-r--r--cmd/deploy/env.go170
1 files changed, 0 insertions, 170 deletions
diff --git a/cmd/deploy/env.go b/cmd/deploy/env.go
deleted file mode 100644
index a43cd6a..0000000
--- a/cmd/deploy/env.go
+++ /dev/null
@@ -1,170 +0,0 @@
1package main
2
3import (
4 "flag"
5 "fmt"
6 "os"
7 "strings"
8
9 "github.com/bdw/deploy/internal/ssh"
10 "github.com/bdw/deploy/internal/state"
11)
12
13func runEnv(args []string) {
14 fs := flag.NewFlagSet("env", flag.ExitOnError)
15 host := fs.String("host", "", "VPS host (SSH config alias or user@host)")
16 var setVars envFlags
17 fs.Var(&setVars, "set", "Set environment variable (KEY=VALUE, can be specified multiple times)")
18 var unsetVars envFlags
19 fs.Var(&unsetVars, "unset", "Unset environment variable (KEY, can be specified multiple times)")
20 envFile := fs.String("file", "", "Load environment from file")
21 fs.Parse(args)
22
23 if len(fs.Args()) == 0 {
24 fmt.Fprintf(os.Stderr, "Error: app name is required\n")
25 fmt.Fprintf(os.Stderr, "Usage: deploy env <app-name> [--set KEY=VALUE] [--unset KEY] [--file .env] --host user@vps-ip\n")
26 os.Exit(1)
27 }
28
29 name := fs.Args()[0]
30
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
39 if *host == "" {
40 *host = st.GetDefaultHost()
41 }
42
43 if *host == "" {
44 fmt.Fprintf(os.Stderr, "Error: --host is required\n")
45 fs.Usage()
46 os.Exit(1)
47 }
48
49 // Get app info
50 app, err := st.GetApp(*host, name)
51 if err != nil {
52 fmt.Fprintf(os.Stderr, "Error: %v\n", err)
53 os.Exit(1)
54 }
55
56 if app.Type != "app" {
57 fmt.Fprintf(os.Stderr, "Error: env is only available for apps, not static sites\n")
58 os.Exit(1)
59 }
60
61 // If no flags, just display current env (masked)
62 if len(setVars) == 0 && len(unsetVars) == 0 && *envFile == "" {
63 fmt.Printf("Environment variables for %s:\n\n", name)
64 if len(app.Env) == 0 {
65 fmt.Println(" (none)")
66 } else {
67 for k, v := range app.Env {
68 // Mask sensitive-looking values
69 display := v
70 if isSensitive(k) {
71 display = "***"
72 }
73 fmt.Printf(" %s=%s\n", k, display)
74 }
75 }
76 return
77 }
78
79 // Initialize env if nil
80 if app.Env == nil {
81 app.Env = make(map[string]string)
82 }
83
84 // Apply changes
85 changed := false
86
87 // Unset variables
88 for _, key := range unsetVars {
89 if _, exists := app.Env[key]; exists {
90 delete(app.Env, key)
91 changed = true
92 fmt.Printf("Unset %s\n", key)
93 }
94 }
95
96 // Set variables from flags
97 for _, e := range setVars {
98 parts := strings.SplitN(e, "=", 2)
99 if len(parts) == 2 {
100 app.Env[parts[0]] = parts[1]
101 changed = true
102 fmt.Printf("Set %s\n", parts[0])
103 }
104 }
105
106 // Set variables from file
107 if *envFile != "" {
108 fileEnv, err := parseEnvFile(*envFile)
109 if err != nil {
110 fmt.Fprintf(os.Stderr, "Error reading env file: %v\n", err)
111 os.Exit(1)
112 }
113 for k, v := range fileEnv {
114 app.Env[k] = v
115 changed = true
116 fmt.Printf("Set %s\n", k)
117 }
118 }
119
120 if !changed {
121 fmt.Println("No changes made")
122 return
123 }
124
125 // Save state
126 if err := st.Save(); err != nil {
127 fmt.Fprintf(os.Stderr, "Error saving state: %v\n", err)
128 os.Exit(1)
129 }
130
131 // Connect to VPS and update env file
132 client, err := ssh.Connect(*host)
133 if err != nil {
134 fmt.Fprintf(os.Stderr, "Error connecting to VPS: %v\n", err)
135 os.Exit(1)
136 }
137 defer client.Close()
138
139 // Regenerate env file
140 fmt.Println("→ Updating environment file on VPS...")
141 envFilePath := fmt.Sprintf("/etc/deploy/env/%s.env", name)
142 envContent := ""
143 for k, v := range app.Env {
144 envContent += fmt.Sprintf("%s=%s\n", k, v)
145 }
146 if err := client.WriteSudoFile(envFilePath, envContent); err != nil {
147 fmt.Fprintf(os.Stderr, "Error updating env file: %v\n", err)
148 os.Exit(1)
149 }
150
151 // Restart service to pick up new env
152 fmt.Println("→ Restarting service...")
153 if _, err := client.RunSudo(fmt.Sprintf("systemctl restart %s", name)); err != nil {
154 fmt.Fprintf(os.Stderr, "Error restarting service: %v\n", err)
155 os.Exit(1)
156 }
157
158 fmt.Println("✓ Environment variables updated successfully")
159}
160
161func isSensitive(key string) bool {
162 key = strings.ToLower(key)
163 sensitiveWords := []string{"key", "secret", "password", "token", "api"}
164 for _, word := range sensitiveWords {
165 if strings.Contains(key, word) {
166 return true
167 }
168 }
169 return false
170}