aboutsummaryrefslogtreecommitdiffstats
path: root/skills/ship-binary
diff options
context:
space:
mode:
Diffstat (limited to 'skills/ship-binary')
-rw-r--r--skills/ship-binary/SKILL.md168
1 files changed, 168 insertions, 0 deletions
diff --git a/skills/ship-binary/SKILL.md b/skills/ship-binary/SKILL.md
new file mode 100644
index 0000000..16056ff
--- /dev/null
+++ b/skills/ship-binary/SKILL.md
@@ -0,0 +1,168 @@
1---
2name: ship-binary
3description: Upload and deploy a pre-built binary to a ship VPS. Handles port allocation, systemd service, env vars, Caddy config, and SQLite backup. Use when deploying a Go binary or other compiled executable.
4argument-hint: "<path-to-binary> <app-name> [host-nickname]"
5---
6
7# ship-binary
8
9Upload a pre-built binary and deploy it as a systemd service with Caddy reverse proxy.
10
11## Read Config
12
13```bash
14python3 -c "
15import json, os
16cfg = json.load(open(os.path.expanduser('~/.config/ship/config.json')))
17nick = '<nickname-or-default>'
18h = cfg['hosts'].get(nick, cfg['hosts'][cfg['default']])
19print(h['host'])
20print(h['domain'])
21"
22```
23
24## Inputs
25
26- **Binary path** — local path to the compiled binary
27- **App name** — short lowercase name, becomes the service name and subdomain (e.g. `foodtracker`)
28- **Domain** — defaults to `<app-name>.<base-domain>` from config, ask if different
29- **Env vars** — ask if there are any env vars to set (beyond PORT/SHIP_NAME/SHIP_URL)
30- **Host** — use default unless specified
31
32## Steps
33
34### 1. Check if app already exists
35
36```bash
37ssh <host> "test -f /etc/ship/ports/<app-name> && echo exists || echo new"
38```
39
40This determines whether to allocate a new port or reuse the existing one.
41
42### 2. Backup SQLite databases (if app exists)
43
44Before touching anything, check for SQLite files in the app's data directory:
45
46```bash
47ssh <host> "find /var/lib/<app-name>/ -name '*.db' 2>/dev/null"
48```
49
50If any `.db` files are found, back them up:
51
52```bash
53ssh <host> "sudo mkdir -p /var/lib/<app-name>/backups && sudo cp /var/lib/<app-name>/data/<name>.db /var/lib/<app-name>/backups/<name>-\$(date +%Y%m%d-%H%M%S).db"
54```
55
56Tell the user what was backed up before proceeding.
57
58### 3. Allocate or retrieve port
59
60**New app** — find the highest port in use and add 1:
61```bash
62ssh <host> "sudo bash -c 'max=9000; for f in /etc/ship/ports/*; do p=\$(cat \$f 2>/dev/null); [ \"\$p\" -gt \"\$max\" ] && max=\$p; done; port=\$((max+1)); echo \$port | tee /etc/ship/ports/<app-name>; echo \$port'"
63```
64
65**Existing app** — reuse the existing port:
66```bash
67ssh <host> "cat /etc/ship/ports/<app-name>"
68```
69
70### 4. Upload binary
71
72```bash
73scp <binary-path> <host>:/tmp/<app-name>
74ssh <host> "sudo mv /tmp/<app-name> /usr/local/bin/<app-name> && sudo chmod +x /usr/local/bin/<app-name>"
75```
76
77### 5. Create work directory and service user
78
79```bash
80ssh <host> "sudo mkdir -p /var/lib/<app-name>/data && sudo useradd -r -s /bin/false <app-name> 2>/dev/null || true && sudo chown -R <app-name>:<app-name> /var/lib/<app-name>"
81```
82
83### 6. Write env file
84
85Build the env file content merging ship-managed vars with any user-provided vars.
86If the file already exists, read it first and merge (new values win, old values survive):
87
88```bash
89ssh <host> "sudo cat /etc/ship/env/<app-name>.env 2>/dev/null"
90```
91
92Then write merged result:
93```bash
94ssh <host> "sudo tee /etc/ship/env/<app-name>.env > /dev/null << 'EOF'
95PORT=<port>
96SHIP_NAME=<app-name>
97SHIP_URL=https://<domain>
98<user-env-vars>
99EOF
100sudo chmod 600 /etc/ship/env/<app-name>.env"
101```
102
103### 7. Write systemd unit
104
105```bash
106ssh <host> "sudo tee /etc/systemd/system/<app-name>.service > /dev/null << 'EOF'
107[Unit]
108Description=<app-name>
109After=network.target
110
111[Service]
112Type=simple
113User=<app-name>
114WorkingDirectory=/var/lib/<app-name>
115EnvironmentFile=/etc/ship/env/<app-name>.env
116ExecStart=/usr/local/bin/<app-name>
117Restart=always
118RestartSec=5s
119NoNewPrivileges=true
120PrivateTmp=true
121
122[Install]
123WantedBy=multi-user.target
124EOF"
125```
126
127### 8. Start or restart service
128
129```bash
130ssh <host> "sudo systemctl daemon-reload"
131```
132
133**New app:**
134```bash
135ssh <host> "sudo systemctl enable --now <app-name>"
136```
137
138**Existing app:**
139```bash
140ssh <host> "sudo systemctl restart <app-name>"
141```
142
143### 9. Write Caddy config
144
145```bash
146ssh <host> "sudo tee /etc/caddy/sites-enabled/<app-name>.caddy > /dev/null << 'EOF'
147<domain> {
148 reverse_proxy 127.0.0.1:<port>
149}
150EOF
151sudo systemctl reload caddy"
152```
153
154### 10. Confirm
155
156Tell the user:
157- App name, URL, and port
158- Whether it was a new deploy or update
159- Any SQLite backups made
160- Any env vars set
161
162## Notes
163
164- Always back up SQLite before swapping the binary
165- Always merge env vars — never replace the whole file
166- Use `systemctl restart` for existing apps, `enable --now` for new ones
167- The data directory `/var/lib/<app-name>/data/` persists across deploys
168- If the user doesn't specify env vars, ask if they need any before deploying