summaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/ship/root.go191
1 files changed, 107 insertions, 84 deletions
diff --git a/cmd/ship/root.go b/cmd/ship/root.go
index d63772b..c46491f 100644
--- a/cmd/ship/root.go
+++ b/cmd/ship/root.go
@@ -14,38 +14,54 @@ import (
14 "github.com/spf13/cobra" 14 "github.com/spf13/cobra"
15) 15)
16 16
17// DeployOptions contains all options for deploying or updating an app
18type DeployOptions struct {
19 Host string
20 Domain string
21 Name string
22 Binary string
23 Port int
24 EnvVars []string
25 EnvFile string
26 Args string
27 Files []string
28 Memory string
29 CPU string
30}
31
17func runDeploy(cmd *cobra.Command, args []string) error { 32func runDeploy(cmd *cobra.Command, args []string) error {
18 flags := cmd.Flags() 33 flags := cmd.Flags()
19 34
20 binary, _ := flags.GetString("binary") 35 opts := DeployOptions{}
36 opts.Binary, _ = flags.GetString("binary")
21 static, _ := flags.GetBool("static") 37 static, _ := flags.GetBool("static")
22 dir, _ := flags.GetString("dir") 38 dir, _ := flags.GetString("dir")
23 domain, _ := flags.GetString("domain") 39 opts.Domain, _ = flags.GetString("domain")
24 name, _ := flags.GetString("name") 40 opts.Name, _ = flags.GetString("name")
25 port, _ := flags.GetInt("port") 41 opts.Port, _ = flags.GetInt("port")
26 envVars, _ := flags.GetStringArray("env") 42 opts.EnvVars, _ = flags.GetStringArray("env")
27 envFile, _ := flags.GetString("env-file") 43 opts.EnvFile, _ = flags.GetString("env-file")
28 binaryArgs, _ := flags.GetString("args") 44 opts.Args, _ = flags.GetString("args")
29 files, _ := flags.GetStringArray("file") 45 opts.Files, _ = flags.GetStringArray("file")
30 memory, _ := flags.GetString("memory") 46 opts.Memory, _ = flags.GetString("memory")
31 cpu, _ := flags.GetString("cpu") 47 opts.CPU, _ = flags.GetString("cpu")
32 48
33 // Get host from flag or state default 49 // Get host from flag or state default
34 host := hostFlag 50 opts.Host = hostFlag
35 if host == "" { 51 if opts.Host == "" {
36 st, err := state.Load() 52 st, err := state.Load()
37 if err != nil { 53 if err != nil {
38 return fmt.Errorf("error loading state: %w", err) 54 return fmt.Errorf("error loading state: %w", err)
39 } 55 }
40 host = st.GetDefaultHost() 56 opts.Host = st.GetDefaultHost()
41 } 57 }
42 58
43 // If no flags provided, show help 59 // If no flags provided, show help
44 if domain == "" && binary == "" && !static && name == "" { 60 if opts.Domain == "" && opts.Binary == "" && !static && opts.Name == "" {
45 return cmd.Help() 61 return cmd.Help()
46 } 62 }
47 63
48 if host == "" { 64 if opts.Host == "" {
49 return fmt.Errorf("--host is required") 65 return fmt.Errorf("--host is required")
50 } 66 }
51 67
@@ -54,78 +70,82 @@ func runDeploy(cmd *cobra.Command, args []string) error {
54 if err != nil { 70 if err != nil {
55 return fmt.Errorf("error loading state: %w", err) 71 return fmt.Errorf("error loading state: %w", err)
56 } 72 }
57 hostState := st.GetHost(host) 73 hostState := st.GetHost(opts.Host)
58 74
59 // Config update mode: --name provided without --binary or --static 75 // Config update mode: --name provided without --binary or --static
60 if name != "" && binary == "" && !static { 76 if opts.Name != "" && opts.Binary == "" && !static {
61 return updateAppConfig(host, name, envVars, envFile, binaryArgs, memory, cpu) 77 return updateAppConfig(opts)
62 } 78 }
63 79
64 if domain == "" && hostState.BaseDomain == "" { 80 if opts.Domain == "" && hostState.BaseDomain == "" {
65 return fmt.Errorf("--domain required (or configure base domain with 'ship host init --base-domain')") 81 return fmt.Errorf("--domain required (or configure base domain with 'ship host init --base-domain')")
66 } 82 }
67 83
68 // Infer name early so we can use it for subdomain generation 84 // Infer name early so we can use it for subdomain generation
69 inferredName := name 85 if opts.Name == "" {
70 if inferredName == "" {
71 if static { 86 if static {
72 inferredName = domain 87 opts.Name = opts.Domain
73 if inferredName == "" && hostState.BaseDomain != "" { 88 if opts.Name == "" && hostState.BaseDomain != "" {
74 inferredName = filepath.Base(dir) 89 opts.Name = filepath.Base(dir)
75 } 90 }
76 } else { 91 } else {
77 inferredName = filepath.Base(binary) 92 opts.Name = filepath.Base(opts.Binary)
78 } 93 }
79 } 94 }
80 95
81 // Generate subdomain if base domain configured 96 // Generate subdomain if base domain configured
82 var domains []string 97 var domains []string
83 if hostState.BaseDomain != "" { 98 if hostState.BaseDomain != "" {
84 domains = append(domains, inferredName+"."+hostState.BaseDomain) 99 domains = append(domains, opts.Name+"."+hostState.BaseDomain)
85 } 100 }
86 if domain != "" { 101 if opts.Domain != "" {
87 domains = append(domains, domain) 102 domains = append(domains, opts.Domain)
88 } 103 }
89 combinedDomains := strings.Join(domains, ", ") 104 opts.Domain = strings.Join(domains, ", ")
90 105
91 if static { 106 if static {
92 return deployStatic(host, combinedDomains, inferredName, dir) 107 return deployStatic(opts.Host, opts.Domain, opts.Name, dir)
93 } 108 }
94 return deployApp(host, combinedDomains, inferredName, binary, port, envVars, envFile, binaryArgs, files, memory, cpu) 109 return deployApp(opts)
95} 110}
96 111
97func deployApp(host, domain, name, binaryPath string, portOverride int, envVars []string, envFile, args string, files []string, memory, cpu string) error { 112func deployApp(opts DeployOptions) error {
98 if binaryPath == "" { 113 if opts.Binary == "" {
99 return fmt.Errorf("--binary is required") 114 return fmt.Errorf("--binary is required")
100 } 115 }
101 116
102 if _, err := os.Stat(binaryPath); err != nil { 117 if _, err := os.Stat(opts.Binary); err != nil {
103 return fmt.Errorf("binary not found: %s", binaryPath) 118 return fmt.Errorf("binary not found: %s", opts.Binary)
104 } 119 }
105 120
106 fmt.Printf("Deploying app: %s\n", name) 121 fmt.Printf("Deploying app: %s\n", opts.Name)
107 fmt.Printf(" Domain(s): %s\n", domain) 122 fmt.Printf(" Domain(s): %s\n", opts.Domain)
108 fmt.Printf(" Binary: %s\n", binaryPath) 123 fmt.Printf(" Binary: %s\n", opts.Binary)
109 124
110 st, err := state.Load() 125 st, err := state.Load()
111 if err != nil { 126 if err != nil {
112 return fmt.Errorf("error loading state: %w", err) 127 return fmt.Errorf("error loading state: %w", err)
113 } 128 }
114 129
115 existingApp, _ := st.GetApp(host, name) 130 existingApp, _ := st.GetApp(opts.Host, opts.Name)
116 var port int 131 var port int
117 if existingApp != nil { 132 if existingApp != nil {
118 port = existingApp.Port 133 port = existingApp.Port
119 fmt.Printf(" Updating existing deployment (port %d)\n", port) 134 fmt.Printf(" Updating existing deployment (port %d)\n", port)
120 } else { 135 } else {
121 if portOverride > 0 { 136 if opts.Port > 0 {
122 port = portOverride 137 port = opts.Port
123 } else { 138 } else {
124 port = st.AllocatePort(host) 139 port = st.AllocatePort(opts.Host)
125 } 140 }
126 fmt.Printf(" Allocated port: %d\n", port) 141 fmt.Printf(" Allocated port: %d\n", port)
127 } 142 }
128 143
144 // Merge with existing config
145 args := opts.Args
146 files := opts.Files
147 memory := opts.Memory
148 cpu := opts.CPU
129 env := make(map[string]string) 149 env := make(map[string]string)
130 if existingApp != nil { 150 if existingApp != nil {
131 for k, v := range existingApp.Env { 151 for k, v := range existingApp.Env {
@@ -145,15 +165,15 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
145 } 165 }
146 } 166 }
147 167
148 for _, e := range envVars { 168 for _, e := range opts.EnvVars {
149 parts := strings.SplitN(e, "=", 2) 169 parts := strings.SplitN(e, "=", 2)
150 if len(parts) == 2 { 170 if len(parts) == 2 {
151 env[parts[0]] = parts[1] 171 env[parts[0]] = parts[1]
152 } 172 }
153 } 173 }
154 174
155 if envFile != "" { 175 if opts.EnvFile != "" {
156 fileEnv, err := parseEnvFile(envFile) 176 fileEnv, err := parseEnvFile(opts.EnvFile)
157 if err != nil { 177 if err != nil {
158 return fmt.Errorf("error reading env file: %w", err) 178 return fmt.Errorf("error reading env file: %w", err)
159 } 179 }
@@ -164,32 +184,32 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
164 184
165 env["PORT"] = strconv.Itoa(port) 185 env["PORT"] = strconv.Itoa(port)
166 186
167 client, err := ssh.Connect(host) 187 client, err := ssh.Connect(opts.Host)
168 if err != nil { 188 if err != nil {
169 return fmt.Errorf("error connecting to VPS: %w", err) 189 return fmt.Errorf("error connecting to VPS: %w", err)
170 } 190 }
171 defer client.Close() 191 defer client.Close()
172 192
173 fmt.Println("-> Uploading binary...") 193 fmt.Println("-> Uploading binary...")
174 remoteTmpPath := fmt.Sprintf("/tmp/%s", name) 194 remoteTmpPath := fmt.Sprintf("/tmp/%s", opts.Name)
175 if err := client.Upload(binaryPath, remoteTmpPath); err != nil { 195 if err := client.Upload(opts.Binary, remoteTmpPath); err != nil {
176 return fmt.Errorf("error uploading binary: %w", err) 196 return fmt.Errorf("error uploading binary: %w", err)
177 } 197 }
178 198
179 fmt.Println("-> Creating system user...") 199 fmt.Println("-> Creating system user...")
180 client.RunSudo(fmt.Sprintf("useradd -r -s /bin/false %s", name)) 200 client.RunSudo(fmt.Sprintf("useradd -r -s /bin/false %s", opts.Name))
181 201
182 fmt.Println("-> Setting up directories...") 202 fmt.Println("-> Setting up directories...")
183 workDir := fmt.Sprintf("/var/lib/%s", name) 203 workDir := fmt.Sprintf("/var/lib/%s", opts.Name)
184 if _, err := client.RunSudo(fmt.Sprintf("mkdir -p %s", workDir)); err != nil { 204 if _, err := client.RunSudo(fmt.Sprintf("mkdir -p %s", workDir)); err != nil {
185 return fmt.Errorf("error creating work directory: %w", err) 205 return fmt.Errorf("error creating work directory: %w", err)
186 } 206 }
187 if _, err := client.RunSudo(fmt.Sprintf("chown %s:%s %s", name, name, workDir)); err != nil { 207 if _, err := client.RunSudo(fmt.Sprintf("chown %s:%s %s", opts.Name, opts.Name, workDir)); err != nil {
188 return fmt.Errorf("error setting work directory ownership: %w", err) 208 return fmt.Errorf("error setting work directory ownership: %w", err)
189 } 209 }
190 210
191 fmt.Println("-> Installing binary...") 211 fmt.Println("-> Installing binary...")
192 binaryDest := fmt.Sprintf("/usr/local/bin/%s", name) 212 binaryDest := fmt.Sprintf("/usr/local/bin/%s", opts.Name)
193 if _, err := client.RunSudo(fmt.Sprintf("mv %s %s", remoteTmpPath, binaryDest)); err != nil { 213 if _, err := client.RunSudo(fmt.Sprintf("mv %s %s", remoteTmpPath, binaryDest)); err != nil {
194 return fmt.Errorf("error moving binary: %w", err) 214 return fmt.Errorf("error moving binary: %w", err)
195 } 215 }
@@ -205,17 +225,17 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
205 } 225 }
206 226
207 remotePath := fmt.Sprintf("%s/%s", workDir, filepath.Base(file)) 227 remotePath := fmt.Sprintf("%s/%s", workDir, filepath.Base(file))
208 remoteTmpPath := fmt.Sprintf("/tmp/%s_%s", name, filepath.Base(file)) 228 fileTmpPath := fmt.Sprintf("/tmp/%s_%s", opts.Name, filepath.Base(file))
209 229
210 if err := client.Upload(file, remoteTmpPath); err != nil { 230 if err := client.Upload(file, fileTmpPath); err != nil {
211 return fmt.Errorf("error uploading config file %s: %w", file, err) 231 return fmt.Errorf("error uploading config file %s: %w", file, err)
212 } 232 }
213 233
214 if _, err := client.RunSudo(fmt.Sprintf("mv %s %s", remoteTmpPath, remotePath)); err != nil { 234 if _, err := client.RunSudo(fmt.Sprintf("mv %s %s", fileTmpPath, remotePath)); err != nil {
215 return fmt.Errorf("error moving config file %s: %w", file, err) 235 return fmt.Errorf("error moving config file %s: %w", file, err)
216 } 236 }
217 237
218 if _, err := client.RunSudo(fmt.Sprintf("chown %s:%s %s", name, name, remotePath)); err != nil { 238 if _, err := client.RunSudo(fmt.Sprintf("chown %s:%s %s", opts.Name, opts.Name, remotePath)); err != nil {
219 return fmt.Errorf("error setting config file ownership %s: %w", file, err) 239 return fmt.Errorf("error setting config file ownership %s: %w", file, err)
220 } 240 }
221 241
@@ -224,7 +244,7 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
224 } 244 }
225 245
226 fmt.Println("-> Creating environment file...") 246 fmt.Println("-> Creating environment file...")
227 envFilePath := fmt.Sprintf("/etc/ship/env/%s.env", name) 247 envFilePath := fmt.Sprintf("/etc/ship/env/%s.env", opts.Name)
228 envContent := "" 248 envContent := ""
229 for k, v := range env { 249 for k, v := range env {
230 envContent += fmt.Sprintf("%s=%s\n", k, v) 250 envContent += fmt.Sprintf("%s=%s\n", k, v)
@@ -235,14 +255,14 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
235 if _, err := client.RunSudo(fmt.Sprintf("chmod 600 %s", envFilePath)); err != nil { 255 if _, err := client.RunSudo(fmt.Sprintf("chmod 600 %s", envFilePath)); err != nil {
236 return fmt.Errorf("error setting env file permissions: %w", err) 256 return fmt.Errorf("error setting env file permissions: %w", err)
237 } 257 }
238 if _, err := client.RunSudo(fmt.Sprintf("chown %s:%s %s", name, name, envFilePath)); err != nil { 258 if _, err := client.RunSudo(fmt.Sprintf("chown %s:%s %s", opts.Name, opts.Name, envFilePath)); err != nil {
239 return fmt.Errorf("error setting env file ownership: %w", err) 259 return fmt.Errorf("error setting env file ownership: %w", err)
240 } 260 }
241 261
242 fmt.Println("-> Creating systemd service...") 262 fmt.Println("-> Creating systemd service...")
243 serviceContent, err := templates.SystemdService(map[string]string{ 263 serviceContent, err := templates.SystemdService(map[string]string{
244 "Name": name, 264 "Name": opts.Name,
245 "User": name, 265 "User": opts.Name,
246 "WorkDir": workDir, 266 "WorkDir": workDir,
247 "BinaryPath": binaryDest, 267 "BinaryPath": binaryDest,
248 "Port": strconv.Itoa(port), 268 "Port": strconv.Itoa(port),
@@ -255,21 +275,21 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
255 return fmt.Errorf("error generating systemd unit: %w", err) 275 return fmt.Errorf("error generating systemd unit: %w", err)
256 } 276 }
257 277
258 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", name) 278 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", opts.Name)
259 if err := client.WriteSudoFile(servicePath, serviceContent); err != nil { 279 if err := client.WriteSudoFile(servicePath, serviceContent); err != nil {
260 return fmt.Errorf("error creating systemd unit: %w", err) 280 return fmt.Errorf("error creating systemd unit: %w", err)
261 } 281 }
262 282
263 fmt.Println("-> Configuring Caddy...") 283 fmt.Println("-> Configuring Caddy...")
264 caddyContent, err := templates.AppCaddy(map[string]string{ 284 caddyContent, err := templates.AppCaddy(map[string]string{
265 "Domain": domain, 285 "Domain": opts.Domain,
266 "Port": strconv.Itoa(port), 286 "Port": strconv.Itoa(port),
267 }) 287 })
268 if err != nil { 288 if err != nil {
269 return fmt.Errorf("error generating Caddy config: %w", err) 289 return fmt.Errorf("error generating Caddy config: %w", err)
270 } 290 }
271 291
272 caddyPath := fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", name) 292 caddyPath := fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", opts.Name)
273 if err := client.WriteSudoFile(caddyPath, caddyContent); err != nil { 293 if err := client.WriteSudoFile(caddyPath, caddyContent); err != nil {
274 return fmt.Errorf("error creating Caddy config: %w", err) 294 return fmt.Errorf("error creating Caddy config: %w", err)
275 } 295 }
@@ -280,10 +300,10 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
280 } 300 }
281 301
282 fmt.Println("-> Starting service...") 302 fmt.Println("-> Starting service...")
283 if _, err := client.RunSudo(fmt.Sprintf("systemctl enable %s", name)); err != nil { 303 if _, err := client.RunSudo(fmt.Sprintf("systemctl enable %s", opts.Name)); err != nil {
284 return fmt.Errorf("error enabling service: %w", err) 304 return fmt.Errorf("error enabling service: %w", err)
285 } 305 }
286 if _, err := client.RunSudo(fmt.Sprintf("systemctl restart %s", name)); err != nil { 306 if _, err := client.RunSudo(fmt.Sprintf("systemctl restart %s", opts.Name)); err != nil {
287 return fmt.Errorf("error starting service: %w", err) 307 return fmt.Errorf("error starting service: %w", err)
288 } 308 }
289 309
@@ -292,9 +312,9 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
292 return fmt.Errorf("error reloading Caddy: %w", err) 312 return fmt.Errorf("error reloading Caddy: %w", err)
293 } 313 }
294 314
295 st.AddApp(host, name, &state.App{ 315 st.AddApp(opts.Host, opts.Name, &state.App{
296 Type: "app", 316 Type: "app",
297 Domain: domain, 317 Domain: opts.Domain,
298 Port: port, 318 Port: port,
299 Env: env, 319 Env: env,
300 Args: args, 320 Args: args,
@@ -308,36 +328,39 @@ func deployApp(host, domain, name, binaryPath string, portOverride int, envVars
308 328
309 fmt.Printf("\n App deployed successfully!\n") 329 fmt.Printf("\n App deployed successfully!\n")
310 // Show first domain in the URL message 330 // Show first domain in the URL message
311 primaryDomain := strings.Split(domain, ",")[0] 331 primaryDomain := strings.Split(opts.Domain, ",")[0]
312 primaryDomain = strings.TrimSpace(primaryDomain) 332 primaryDomain = strings.TrimSpace(primaryDomain)
313 fmt.Printf(" https://%s (may take a minute for HTTPS cert)\n", primaryDomain) 333 fmt.Printf(" https://%s (may take a minute for HTTPS cert)\n", primaryDomain)
314 return nil 334 return nil
315} 335}
316 336
317func updateAppConfig(host, name string, envVars []string, envFile, args, memory, cpu string) error { 337func updateAppConfig(opts DeployOptions) error {
318 st, err := state.Load() 338 st, err := state.Load()
319 if err != nil { 339 if err != nil {
320 return fmt.Errorf("error loading state: %w", err) 340 return fmt.Errorf("error loading state: %w", err)
321 } 341 }
322 342
323 existingApp, err := st.GetApp(host, name) 343 existingApp, err := st.GetApp(opts.Host, opts.Name)
324 if err != nil { 344 if err != nil {
325 return fmt.Errorf("app %s not found (use --binary to deploy a new app)", name) 345 return fmt.Errorf("app %s not found (use --binary to deploy a new app)", opts.Name)
326 } 346 }
327 347
328 if existingApp.Type != "app" { 348 if existingApp.Type != "app" {
329 return fmt.Errorf("%s is a static site, not an app", name) 349 return fmt.Errorf("%s is a static site, not an app", opts.Name)
330 } 350 }
331 351
332 fmt.Printf("Updating config: %s\n", name) 352 fmt.Printf("Updating config: %s\n", opts.Name)
333 353
334 // Merge with existing values 354 // Merge with existing values
355 args := opts.Args
335 if args == "" { 356 if args == "" {
336 args = existingApp.Args 357 args = existingApp.Args
337 } 358 }
359 memory := opts.Memory
338 if memory == "" { 360 if memory == "" {
339 memory = existingApp.Memory 361 memory = existingApp.Memory
340 } 362 }
363 cpu := opts.CPU
341 if cpu == "" { 364 if cpu == "" {
342 cpu = existingApp.CPU 365 cpu = existingApp.CPU
343 } 366 }
@@ -347,14 +370,14 @@ func updateAppConfig(host, name string, envVars []string, envFile, args, memory,
347 for k, v := range existingApp.Env { 370 for k, v := range existingApp.Env {
348 env[k] = v 371 env[k] = v
349 } 372 }
350 for _, e := range envVars { 373 for _, e := range opts.EnvVars {
351 parts := strings.SplitN(e, "=", 2) 374 parts := strings.SplitN(e, "=", 2)
352 if len(parts) == 2 { 375 if len(parts) == 2 {
353 env[parts[0]] = parts[1] 376 env[parts[0]] = parts[1]
354 } 377 }
355 } 378 }
356 if envFile != "" { 379 if opts.EnvFile != "" {
357 fileEnv, err := parseEnvFile(envFile) 380 fileEnv, err := parseEnvFile(opts.EnvFile)
358 if err != nil { 381 if err != nil {
359 return fmt.Errorf("error reading env file: %w", err) 382 return fmt.Errorf("error reading env file: %w", err)
360 } 383 }
@@ -364,7 +387,7 @@ func updateAppConfig(host, name string, envVars []string, envFile, args, memory,
364 } 387 }
365 env["PORT"] = strconv.Itoa(existingApp.Port) 388 env["PORT"] = strconv.Itoa(existingApp.Port)
366 389
367 client, err := ssh.Connect(host) 390 client, err := ssh.Connect(opts.Host)
368 if err != nil { 391 if err != nil {
369 return fmt.Errorf("error connecting to VPS: %w", err) 392 return fmt.Errorf("error connecting to VPS: %w", err)
370 } 393 }
@@ -372,7 +395,7 @@ func updateAppConfig(host, name string, envVars []string, envFile, args, memory,
372 395
373 // Update env file 396 // Update env file
374 fmt.Println("-> Updating environment file...") 397 fmt.Println("-> Updating environment file...")
375 envFilePath := fmt.Sprintf("/etc/ship/env/%s.env", name) 398 envFilePath := fmt.Sprintf("/etc/ship/env/%s.env", opts.Name)
376 envContent := "" 399 envContent := ""
377 for k, v := range env { 400 for k, v := range env {
378 envContent += fmt.Sprintf("%s=%s\n", k, v) 401 envContent += fmt.Sprintf("%s=%s\n", k, v)
@@ -383,11 +406,11 @@ func updateAppConfig(host, name string, envVars []string, envFile, args, memory,
383 406
384 // Regenerate systemd unit 407 // Regenerate systemd unit
385 fmt.Println("-> Updating systemd service...") 408 fmt.Println("-> Updating systemd service...")
386 workDir := fmt.Sprintf("/var/lib/%s", name) 409 workDir := fmt.Sprintf("/var/lib/%s", opts.Name)
387 binaryDest := fmt.Sprintf("/usr/local/bin/%s", name) 410 binaryDest := fmt.Sprintf("/usr/local/bin/%s", opts.Name)
388 serviceContent, err := templates.SystemdService(map[string]string{ 411 serviceContent, err := templates.SystemdService(map[string]string{
389 "Name": name, 412 "Name": opts.Name,
390 "User": name, 413 "User": opts.Name,
391 "WorkDir": workDir, 414 "WorkDir": workDir,
392 "BinaryPath": binaryDest, 415 "BinaryPath": binaryDest,
393 "Port": strconv.Itoa(existingApp.Port), 416 "Port": strconv.Itoa(existingApp.Port),
@@ -400,7 +423,7 @@ func updateAppConfig(host, name string, envVars []string, envFile, args, memory,
400 return fmt.Errorf("error generating systemd unit: %w", err) 423 return fmt.Errorf("error generating systemd unit: %w", err)
401 } 424 }
402 425
403 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", name) 426 servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", opts.Name)
404 if err := client.WriteSudoFile(servicePath, serviceContent); err != nil { 427 if err := client.WriteSudoFile(servicePath, serviceContent); err != nil {
405 return fmt.Errorf("error creating systemd unit: %w", err) 428 return fmt.Errorf("error creating systemd unit: %w", err)
406 } 429 }
@@ -411,7 +434,7 @@ func updateAppConfig(host, name string, envVars []string, envFile, args, memory,
411 } 434 }
412 435
413 fmt.Println("-> Restarting service...") 436 fmt.Println("-> Restarting service...")
414 if _, err := client.RunSudo(fmt.Sprintf("systemctl restart %s", name)); err != nil { 437 if _, err := client.RunSudo(fmt.Sprintf("systemctl restart %s", opts.Name)); err != nil {
415 return fmt.Errorf("error restarting service: %w", err) 438 return fmt.Errorf("error restarting service: %w", err)
416 } 439 }
417 440