diff options
| author | bndw <ben@bdw.to> | 2026-02-08 12:32:59 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-02-08 12:32:59 -0800 |
| commit | a8ad8e934d15d2bf84f942414a89af1d2691adbc (patch) | |
| tree | 82e6765c9d35968b27ac7ee17f5c201a421dc1d3 /cmd/ship/deploy.go | |
| parent | af109c04a3edd4dcd4e7b16242052442fb4a3b24 (diff) | |
Add git-centric deployment with Docker builds and vanity imports
New deployment model where projects start with a git remote on the VPS.
Pushing to the remote triggers automatic docker build and deploy via
post-receive hooks. The base domain serves Go vanity imports and git
HTTPS cloning via Caddy + fcgiwrap.
- Add `ship init <name>` command to create bare repos and .ship/ config
- Add `ship deploy <name>` command for manual rebuilds
- Extend `ship host init --base-domain` to set up Docker, git user,
fcgiwrap, sudoers, and vanity import infrastructure
- Add git-app and git-static types alongside existing app and static
- Update remove, status, logs, restart, list, and config-update to
handle new types
Diffstat (limited to 'cmd/ship/deploy.go')
| -rw-r--r-- | cmd/ship/deploy.go | 56 |
1 files changed, 30 insertions, 26 deletions
diff --git a/cmd/ship/deploy.go b/cmd/ship/deploy.go index 24eed1e..9ac754c 100644 --- a/cmd/ship/deploy.go +++ b/cmd/ship/deploy.go | |||
| @@ -415,7 +415,7 @@ func updateAppConfig(st *state.State, opts DeployOptions) error { | |||
| 415 | return fmt.Errorf("app %s not found (use --binary to deploy a new app)", opts.Name) | 415 | return fmt.Errorf("app %s not found (use --binary to deploy a new app)", opts.Name) |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | if existingApp.Type != "app" { | 418 | if existingApp.Type != "app" && existingApp.Type != "git-app" { |
| 419 | return fmt.Errorf("%s is a static site, not an app", opts.Name) | 419 | return fmt.Errorf("%s is a static site, not an app", opts.Name) |
| 420 | } | 420 | } |
| 421 | 421 | ||
| @@ -441,33 +441,37 @@ func updateAppConfig(st *state.State, opts DeployOptions) error { | |||
| 441 | return fmt.Errorf("error creating env file: %w", err) | 441 | return fmt.Errorf("error creating env file: %w", err) |
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | // Regenerate systemd unit | 444 | // For git-app, the systemd unit comes from .ship/service in the repo, |
| 445 | fmt.Println("-> Updating systemd service...") | 445 | // so we only update the env file and restart. |
| 446 | workDir := fmt.Sprintf("/var/lib/%s", opts.Name) | 446 | if existingApp.Type != "git-app" { |
| 447 | binaryDest := fmt.Sprintf("/usr/local/bin/%s", opts.Name) | 447 | // Regenerate systemd unit |
| 448 | serviceContent, err := templates.SystemdService(map[string]string{ | 448 | fmt.Println("-> Updating systemd service...") |
| 449 | "Name": opts.Name, | 449 | workDir := fmt.Sprintf("/var/lib/%s", opts.Name) |
| 450 | "User": opts.Name, | 450 | binaryDest := fmt.Sprintf("/usr/local/bin/%s", opts.Name) |
| 451 | "WorkDir": workDir, | 451 | serviceContent, err := templates.SystemdService(map[string]string{ |
| 452 | "BinaryPath": binaryDest, | 452 | "Name": opts.Name, |
| 453 | "Port": strconv.Itoa(existingApp.Port), | 453 | "User": opts.Name, |
| 454 | "EnvFile": envFilePath, | 454 | "WorkDir": workDir, |
| 455 | "Args": opts.Args, | 455 | "BinaryPath": binaryDest, |
| 456 | "Memory": opts.Memory, | 456 | "Port": strconv.Itoa(existingApp.Port), |
| 457 | "CPU": opts.CPU, | 457 | "EnvFile": envFilePath, |
| 458 | }) | 458 | "Args": opts.Args, |
| 459 | if err != nil { | 459 | "Memory": opts.Memory, |
| 460 | return fmt.Errorf("error generating systemd unit: %w", err) | 460 | "CPU": opts.CPU, |
| 461 | } | 461 | }) |
| 462 | if err != nil { | ||
| 463 | return fmt.Errorf("error generating systemd unit: %w", err) | ||
| 464 | } | ||
| 462 | 465 | ||
| 463 | servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", opts.Name) | 466 | servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", opts.Name) |
| 464 | if err := client.WriteSudoFile(servicePath, serviceContent); err != nil { | 467 | if err := client.WriteSudoFile(servicePath, serviceContent); err != nil { |
| 465 | return fmt.Errorf("error creating systemd unit: %w", err) | 468 | return fmt.Errorf("error creating systemd unit: %w", err) |
| 466 | } | 469 | } |
| 467 | 470 | ||
| 468 | fmt.Println("-> Reloading systemd...") | 471 | fmt.Println("-> Reloading systemd...") |
| 469 | if _, err := client.RunSudo("systemctl daemon-reload"); err != nil { | 472 | if _, err := client.RunSudo("systemctl daemon-reload"); err != nil { |
| 470 | return fmt.Errorf("error reloading systemd: %w", err) | 473 | return fmt.Errorf("error reloading systemd: %w", err) |
| 474 | } | ||
| 471 | } | 475 | } |
| 472 | 476 | ||
| 473 | fmt.Println("-> Restarting service...") | 477 | fmt.Println("-> Restarting service...") |
