--- name: ship-deploy description: Deploy an app to a ship VPS. Orchestrates ship-binary or ship-static depending on what you're deploying. Use when you want to deploy or redeploy an app and want Claude to guide the process. argument-hint: " [host-nickname]" --- # ship-deploy Orchestration runbook for deploying apps to a ship VPS. Guides the full deployment process by calling the appropriate ship skills in the right order. ## Prerequisites - `/ship-setup` must have been run at least once - `~/.config/ship/config.json` must exist and contain at least one host - The app must already be built locally (binary compiled or dist folder ready) ## Read Config ```bash python3 -c " import json, os cfg = json.load(open(os.path.expanduser('~/.config/ship/config.json'))) nick = '' h = cfg['hosts'].get(nick, cfg['hosts'][cfg['default']]) print(h['host']) print(h['domain']) " ``` ## Step 1 — Understand what we're deploying Ask the user (or infer from context): - **What is the app name?** (e.g. `foodtracker`) - **What type?** Binary or static site? - **Where is the artifact?** Path to binary or dist folder - **What host?** Default unless specified - **Any env vars needed?** Especially for first-time deploys - **Custom domain?** Or use `.` If any of these are unclear, ask before proceeding. ## Step 2 — Check if app already exists ```bash ssh "test -f /etc/ship/ports/ && echo exists || echo new" ``` Tell the user whether this is a fresh deploy or an update to an existing app. ## Step 3 — Deploy ### For a binary app → follow ship-binary Key steps in order: 1. Backup any SQLite databases in `/var/lib//` 2. Allocate or retrieve port 3. Upload binary via scp 4. Write/merge env file 5. Write systemd unit 6. `systemctl restart` (existing) or `systemctl enable --now` (new) 7. Write Caddy config and reload ### For a static site → follow ship-static Key steps in order: 1. Validate `index.html` exists in dist folder 2. Rsync dist folder to `/var/www//` 3. Fix ownership to `www-data` 4. Write Caddy config and reload ## Step 4 — Verify After deploying, confirm the service came up: **Binary:** ```bash ssh "sudo systemctl is-active " ``` If not active, immediately check logs: ```bash ssh "sudo journalctl -u -n 30 --no-pager" ``` **Static:** ```bash ssh "curl -sI https:// | head -5" ``` ## Step 5 — Confirm to user Report: - App name and live URL - Type (binary / static) - New deploy or update - Port (binary only) - Any SQLite backups made - Any env vars that were set - Whether the service is running ## Checklist (reference) Use this to make sure nothing is missed: - [ ] Config file read, host resolved - [ ] App type confirmed (binary / static) - [ ] Artifact path confirmed and exists locally - [ ] App name and domain confirmed - [ ] Existing app check done - [ ] SQLite backed up (binary, if db files exist) - [ ] Port allocated or retrieved - [ ] Artifact uploaded - [ ] Env file written with merge semantics - [ ] Systemd unit written and service started/restarted (binary) - [ ] Caddy config written and reloaded - [ ] Service confirmed running ## Notes - Never skip the SQLite backup step for binary apps — always check even if you don't expect a db - Always merge env vars — never overwrite the whole env file - If anything fails mid-deploy, tell the user exactly where it failed and what state the server is in - Use `systemctl restart` for existing apps, `enable --now` for new ones — not the other way around - If the user says "deploy X" without more context, ask the minimum necessary questions before starting