summaryrefslogtreecommitdiffstats
path: root/cmd/ship/deploy_cmd.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ship/deploy_cmd.go')
-rw-r--r--cmd/ship/deploy_cmd.go138
1 files changed, 138 insertions, 0 deletions
diff --git a/cmd/ship/deploy_cmd.go b/cmd/ship/deploy_cmd.go
new file mode 100644
index 0000000..ca5c54d
--- /dev/null
+++ b/cmd/ship/deploy_cmd.go
@@ -0,0 +1,138 @@
1package main
2
3import (
4 "fmt"
5
6 "github.com/bdw/ship/internal/ssh"
7 "github.com/bdw/ship/internal/state"
8 "github.com/spf13/cobra"
9)
10
11var deployGitCmd = &cobra.Command{
12 Use: "deploy <name>",
13 Short: "Manually rebuild and deploy a git-deployed app",
14 Long: `Trigger a manual rebuild from the latest code in the git repo.
15
16This runs the same steps as the post-receive hook: checkout code,
17install .ship/ configs, docker build (for apps), and restart.
18
19Examples:
20 ship deploy myapp`,
21 Args: cobra.ExactArgs(1),
22 RunE: runDeployGit,
23}
24
25func runDeployGit(cmd *cobra.Command, args []string) error {
26 name := args[0]
27
28 st, err := state.Load()
29 if err != nil {
30 return fmt.Errorf("error loading state: %w", err)
31 }
32
33 host := hostFlag
34 if host == "" {
35 host = st.GetDefaultHost()
36 }
37 if host == "" {
38 return fmt.Errorf("--host is required")
39 }
40
41 app, err := st.GetApp(host, name)
42 if err != nil {
43 return err
44 }
45
46 if app.Type != "git-app" && app.Type != "git-static" {
47 return fmt.Errorf("%s is not a git-deployed app (type: %s)", name, app.Type)
48 }
49
50 fmt.Printf("Deploying %s...\n", name)
51
52 client, err := ssh.Connect(host)
53 if err != nil {
54 return fmt.Errorf("error connecting to VPS: %w", err)
55 }
56 defer client.Close()
57
58 if app.Type == "git-app" {
59 if err := deployGitApp(client, name); err != nil {
60 return err
61 }
62 } else {
63 if err := deployGitStatic(client, name); err != nil {
64 return err
65 }
66 }
67
68 fmt.Println("\nDeploy complete!")
69 return nil
70}
71
72func deployGitApp(client *ssh.Client, name string) error {
73 repo := fmt.Sprintf("/srv/git/%s.git", name)
74 src := fmt.Sprintf("/var/lib/%s/src", name)
75
76 fmt.Println("-> Checking out code...")
77 if _, err := client.RunSudo(fmt.Sprintf("git --work-tree=%s --git-dir=%s checkout -f main", src, repo)); err != nil {
78 return fmt.Errorf("error checking out code: %w", err)
79 }
80
81 // Install deployment config from repo
82 serviceSrc := fmt.Sprintf("%s/.ship/service", src)
83 serviceDst := fmt.Sprintf("/etc/systemd/system/%s.service", name)
84 fmt.Println("-> Installing systemd unit...")
85 if _, err := client.RunSudo(fmt.Sprintf("cp %s %s", serviceSrc, serviceDst)); err != nil {
86 fmt.Printf(" Warning: no .ship/service found, skipping\n")
87 } else {
88 if _, err := client.RunSudo("systemctl daemon-reload"); err != nil {
89 return fmt.Errorf("error reloading systemd: %w", err)
90 }
91 }
92
93 caddySrc := fmt.Sprintf("%s/.ship/Caddyfile", src)
94 caddyDst := fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", name)
95 fmt.Println("-> Installing Caddy config...")
96 if _, err := client.RunSudo(fmt.Sprintf("cp %s %s", caddySrc, caddyDst)); err != nil {
97 fmt.Printf(" Warning: no .ship/Caddyfile found, skipping\n")
98 } else {
99 if _, err := client.RunSudo("systemctl reload caddy"); err != nil {
100 return fmt.Errorf("error reloading Caddy: %w", err)
101 }
102 }
103
104 fmt.Println("-> Building Docker image...")
105 if err := client.RunSudoStream(fmt.Sprintf("docker build -t %s:latest %s", name, src)); err != nil {
106 return fmt.Errorf("error building Docker image: %w", err)
107 }
108
109 fmt.Println("-> Restarting service...")
110 if _, err := client.RunSudo(fmt.Sprintf("systemctl restart %s", name)); err != nil {
111 return fmt.Errorf("error restarting service: %w", err)
112 }
113
114 return nil
115}
116
117func deployGitStatic(client *ssh.Client, name string) error {
118 repo := fmt.Sprintf("/srv/git/%s.git", name)
119 webroot := fmt.Sprintf("/var/www/%s", name)
120
121 fmt.Println("-> Deploying static site...")
122 if _, err := client.RunSudo(fmt.Sprintf("git --work-tree=%s --git-dir=%s checkout -f main", webroot, repo)); err != nil {
123 return fmt.Errorf("error checking out code: %w", err)
124 }
125
126 caddySrc := fmt.Sprintf("%s/.ship/Caddyfile", webroot)
127 caddyDst := fmt.Sprintf("/etc/caddy/sites-enabled/%s.caddy", name)
128 fmt.Println("-> Installing Caddy config...")
129 if _, err := client.RunSudo(fmt.Sprintf("cp %s %s", caddySrc, caddyDst)); err != nil {
130 fmt.Printf(" Warning: no .ship/Caddyfile found, skipping\n")
131 } else {
132 if _, err := client.RunSudo("systemctl reload caddy"); err != nil {
133 return fmt.Errorf("error reloading Caddy: %w", err)
134 }
135 }
136
137 return nil
138}