@devalade/shipnode 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +261 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # @devalade/shipnode
2
+
3
+ Deploy Node.js apps to a single VPS — zero-downtime releases, PM2, Caddy, SSH, no Docker required.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @devalade/shipnode
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ # 1. Generate config
15
+ shipnode init
16
+
17
+ # 2. Provision the server (Node, PM2, Caddy, mise)
18
+ shipnode setup
19
+
20
+ # 3. Deploy
21
+ shipnode deploy
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ `shipnode init` creates a `shipnode.config.ts` in your project root. You can also write it by hand using the fluent builder:
27
+
28
+ ```ts
29
+ import { shipnode } from '@devalade/shipnode';
30
+
31
+ export default shipnode
32
+ .backend()
33
+ .ssh({ host: '1.2.3.4', user: 'deploy' })
34
+ .deployTo('/var/www/myapp')
35
+ .pm2('myapp', { instances: 2 })
36
+ .port(3000)
37
+ .domain('api.example.com')
38
+ .healthCheck('/health')
39
+ .nodeVersion('22')
40
+ .pkgManager('pnpm')
41
+ .build();
42
+ ```
43
+
44
+ ### Frontend apps
45
+
46
+ ```ts
47
+ import { shipnode } from '@devalade/shipnode';
48
+
49
+ export default shipnode
50
+ .frontend()
51
+ .ssh({ host: '1.2.3.4', user: 'deploy' })
52
+ .deployTo('/var/www/myapp')
53
+ .domain('example.com')
54
+ .buildDir('dist')
55
+ .build();
56
+ ```
57
+
58
+ ### All options
59
+
60
+ | Method | Default | Description |
61
+ |---|---|---|
62
+ | `.backend()` / `.frontend()` | — | App type |
63
+ | `.ssh({ host, user, port?, identityFile? })` | port 22 | SSH connection |
64
+ | `.deployTo(path)` | `/var/www/app` | Remote deploy path |
65
+ | `.pm2(name, opts?)` | — | PM2 process name + options |
66
+ | `.port(n)` | `3000` | Backend port |
67
+ | `.domain(d)` | — | Domain for Caddy config |
68
+ | `.nodeVersion(v)` | `lts` | Node version (via mise) |
69
+ | `.pkgManager(pm)` | auto-detected | `npm` \| `yarn` \| `pnpm` \| `bun` |
70
+ | `.buildDir(dir)` | auto-detected | Frontend build output dir |
71
+ | `.zeroDowntime({ keepReleases? })` | true, 5 | Zero-downtime releases |
72
+ | `.legacy()` | — | Simple in-place deploy |
73
+ | `.healthCheck(path, opts?)` | `/health`, 30s, 3 retries | Post-deploy health check |
74
+ | `.noHealthCheck()` | — | Skip health check |
75
+ | `.envFile(f)` | `.env` | Local .env file to upload |
76
+ | `.sharedDirs(dirs)` | — | Dirs persisted across releases |
77
+ | `.sharedFiles(files)` | — | Files persisted across releases |
78
+ | `.database(opts)` | — | Database connection config |
79
+ | `.backup(opts)` | — | S3 backup config |
80
+ | `.cloudflare(opts)` | — | Cloudflare Tunnel config |
81
+ | `.preDeploy(fn)` | — | Hook: runs before symlink switch |
82
+ | `.postDeploy(fn)` | — | Hook: runs after deploy |
83
+
84
+ ## Commands
85
+
86
+ ### Core
87
+
88
+ ```bash
89
+ shipnode init # Generate shipnode.config.ts interactively
90
+ shipnode setup # Install Node, PM2, Caddy, fail2ban on server
91
+ shipnode deploy # Deploy (zero-downtime by default)
92
+ shipnode deploy --dry-run # Preview without making changes
93
+ shipnode deploy --skip-build # Skip local build step
94
+ shipnode doctor # Check local + remote config
95
+ shipnode doctor --security # Run security audit
96
+ shipnode status # Show PM2 process status
97
+ ```
98
+
99
+ ### Release management
100
+
101
+ ```bash
102
+ shipnode rollback # Roll back to the previous release
103
+ shipnode rollback --steps 3 # Roll back 3 releases
104
+ shipnode migrate # Migrate existing deploy to zero-downtime structure
105
+ ```
106
+
107
+ ### Environment
108
+
109
+ ```bash
110
+ shipnode env # Upload local .env to server
111
+ shipnode run "npm run migrate" # Run a one-off command on the server
112
+ shipnode run bash # Open an interactive shell
113
+ ```
114
+
115
+ ### Process management
116
+
117
+ ```bash
118
+ shipnode logs # Tail PM2 logs (last 100 lines)
119
+ shipnode logs --lines 500 # Show 500 lines
120
+ shipnode restart # Reload PM2 with --update-env
121
+ shipnode stop # Stop the application
122
+ shipnode metrics # Open PM2 monit dashboard
123
+ ```
124
+
125
+ ### Security & maintenance
126
+
127
+ ```bash
128
+ shipnode harden # SSH hardening, UFW firewall, fail2ban
129
+ shipnode unlock # Clear a stuck deployment lock
130
+ ```
131
+
132
+ ### Users
133
+
134
+ Manage SSH users via `.shipnode/users.yml`:
135
+
136
+ ```yaml
137
+ - username: alice
138
+ publicKey: ssh-ed25519 AAAA... alice@laptop
139
+ sudo: true
140
+ - username: bob
141
+ publicKey: ssh-ed25519 AAAA... bob@laptop
142
+ ```
143
+
144
+ ```bash
145
+ shipnode user sync # Create/update users from users.yml
146
+ shipnode user list # List non-system users on server
147
+ shipnode user remove alice # Remove a user
148
+ ```
149
+
150
+ ### Backups
151
+
152
+ Requires `backup` config and `aws` CLI on the server.
153
+
154
+ ```ts
155
+ export default shipnode
156
+ // ...
157
+ .backup({
158
+ s3Bucket: 'my-backups',
159
+ s3Prefix: 'myapp/',
160
+ schedule: 'daily', // hourly | daily | weekly
161
+ retentionDays: 14,
162
+ })
163
+ .build();
164
+ ```
165
+
166
+ ```bash
167
+ shipnode backup setup # Install backup script + systemd timer
168
+ shipnode backup run # Run a backup immediately
169
+ shipnode backup status # Show timer and last run log
170
+ shipnode backup list # List recent backups in S3
171
+ ```
172
+
173
+ ### Cloudflare Tunnel
174
+
175
+ Expose your app and SSH through Cloudflare without opening ports. Requires `CLOUDFLARE_API_TOKEN` env var.
176
+
177
+ ```ts
178
+ export default shipnode
179
+ // ...
180
+ .cloudflare({
181
+ zone: 'example.com',
182
+ appHostname: 'app.example.com',
183
+ sshHostname: 'ssh.example.com',
184
+ lockdownFirewall: true, // Restrict inbound to CF IPs only
185
+ })
186
+ .build();
187
+ ```
188
+
189
+ ```bash
190
+ shipnode cloudflare init # Install cloudflared, create tunnel, configure DNS
191
+ shipnode cloudflare audit # Verify DNS records and tunnel
192
+ shipnode cloudflare status # Show cloudflared service status
193
+ ```
194
+
195
+ ### CI/CD
196
+
197
+ ```bash
198
+ shipnode ci github # Generate .github/workflows/shipnode-deploy.yml
199
+ shipnode ci env-sync # Push .env vars to GitHub repository secrets
200
+ ```
201
+
202
+ ### Configuration
203
+
204
+ ```bash
205
+ shipnode config show # Print resolved config
206
+ shipnode config validate # Validate config file
207
+ shipnode config path # Print path to config file
208
+ ```
209
+
210
+ ### Customization
211
+
212
+ ```bash
213
+ shipnode eject pm2 # Eject PM2 ecosystem template to .shipnode/templates/
214
+ shipnode eject caddy # Eject Caddy config template
215
+ shipnode eject all # Eject all templates
216
+ shipnode upgrade # Upgrade to the latest version
217
+ ```
218
+
219
+ ## Deploy hooks
220
+
221
+ Run code before or after the symlink switch:
222
+
223
+ ```ts
224
+ export default shipnode
225
+ .backend()
226
+ // ...
227
+ .preDeploy(async ({ exec }) => {
228
+ await exec('npx prisma migrate deploy');
229
+ })
230
+ .postDeploy(async ({ exec, config }) => {
231
+ await exec(`curl -s https://${config.domain}/health`);
232
+ })
233
+ .build();
234
+ ```
235
+
236
+ ## Zero-downtime releases
237
+
238
+ By default, shipnode uses a Capistrano-style release structure:
239
+
240
+ ```
241
+ /var/www/myapp/
242
+ releases/
243
+ 20240101T120000/ ← previous
244
+ 20240102T090000/ ← current (symlinked)
245
+ shared/
246
+ .env
247
+ uploads/
248
+ current -> releases/20240102T090000
249
+ ```
250
+
251
+ PM2 is reloaded against `current/` after the symlink switch. If the health check fails, you can roll back instantly with `shipnode rollback`.
252
+
253
+ ## Requirements
254
+
255
+ **Local:** Node ≥ 18, `rsync`, `ssh`
256
+
257
+ **Server:** Ubuntu/Debian (setup installs everything else via `apt`)
258
+
259
+ ## License
260
+
261
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devalade/shipnode",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Deploy Node.js apps to a single VPS with zero configuration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",