@akash-chowdhury-24/deployhub 1.0.6 → 1.0.8
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.
- package/README.md +579 -4
- package/package.json +1 -1
- package/src/commands/init.js +7 -20
- package/src/utils/github-actions.js +137 -1
package/README.md
CHANGED
|
@@ -109,6 +109,558 @@ deployhub build
|
|
|
109
109
|
|
|
110
110
|
Or push to `main` — GitHub Actions runs `deployhub build` automatically.
|
|
111
111
|
|
|
112
|
+
For a full walkthrough by project type, language, and deployment mode, see [Complete Tutorial](#complete-tutorial) below.
|
|
113
|
+
|
|
114
|
+
## Complete Tutorial
|
|
115
|
+
|
|
116
|
+
This section walks through every supported setup: **frontend only**, **backend only**, **full stack**, **storage only** (build + upload artifacts, no deploy), and **storage + deployment** (build, upload, then deploy). Use it as a checklist from zero to a working pipeline.
|
|
117
|
+
|
|
118
|
+
### What DeployHub does on every run
|
|
119
|
+
|
|
120
|
+
When you run `deployhub build` (locally or in GitHub Actions), DeployHub runs these stages in order:
|
|
121
|
+
|
|
122
|
+
| Stage | What happens |
|
|
123
|
+
|-------|----------------|
|
|
124
|
+
| **detect** | Auto-detect framework, language, build output |
|
|
125
|
+
| **install** | Install dependencies (`npm ci`, `pip install`, `mvn`, etc.) |
|
|
126
|
+
| **test** | Run tests (skippable via config) |
|
|
127
|
+
| **build** | Run your build command(s) |
|
|
128
|
+
| **docker** | Build Docker image if `Dockerfile` exists and enabled |
|
|
129
|
+
| **artifact** | Create versioned `artifact.zip` + metadata locally |
|
|
130
|
+
| **storage** | Upload artifact to all selected providers (parallel) |
|
|
131
|
+
| **deploy** | Deploy to targets — **only if you configured deployment during `init`** |
|
|
132
|
+
| **verify** | Hit your health-check URL — **only if configured** |
|
|
133
|
+
| **notify** | Slack / email / webhook — **only if enabled** |
|
|
134
|
+
|
|
135
|
+
**Storage only** means you answer **No** to *Configure deployment?* during `init`. You still get builds and cloud backups; nothing is pushed to a server or platform.
|
|
136
|
+
|
|
137
|
+
**Storage + deployment** means you answer **Yes**, pick targets, and add the matching secrets. Deploy always runs **after** storage upload succeeds.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### Prerequisites (all projects)
|
|
142
|
+
|
|
143
|
+
1. **Git repository** with a remote (GitHub recommended for CI).
|
|
144
|
+
2. **DeployHub installed** — see [Installation](#installation) above.
|
|
145
|
+
3. **Run from your project root** (where `package.json`, `go.mod`, `pom.xml`, etc. lives).
|
|
146
|
+
|
|
147
|
+
| Language / stack | You need on the machine / in CI |
|
|
148
|
+
|------------------|----------------------------------|
|
|
149
|
+
| Node.js (React, Vue, Express, NestJS, …) | Node.js 18+, npm |
|
|
150
|
+
| Python (FastAPI, Django, Flask) | Python 3.11+, `requirements.txt` or `pyproject.toml` |
|
|
151
|
+
| PHP (Laravel, Symfony) | PHP, Composer, `composer.json` |
|
|
152
|
+
| Java (Spring Boot) | JDK 17+, Maven, `pom.xml` |
|
|
153
|
+
| Go | Go 1.22+, `go.mod` |
|
|
154
|
+
| .NET | .NET 8 SDK, `.csproj` |
|
|
155
|
+
| Ruby on Rails | Ruby 3.2+, Bundler, `Gemfile` |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### Step 1 — Initialize (every workflow)
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd your-project
|
|
163
|
+
deployhub init
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The wizard asks the same core questions for every setup:
|
|
167
|
+
|
|
168
|
+
| Prompt | What to choose |
|
|
169
|
+
|--------|----------------|
|
|
170
|
+
| **Project name** | Defaults to folder name; used in artifact paths and deploy paths |
|
|
171
|
+
| **What are you deploying?** | `Frontend only` · `Backend only` · `Both (monorepo / fullstack)` |
|
|
172
|
+
| **Framework** | Auto-detected when possible; confirm or change |
|
|
173
|
+
| **Build command / output** | Pre-filled per framework (see [Framework defaults](#framework-defaults-by-language) below) |
|
|
174
|
+
| **Storage providers** | Pick one or more: Local, AWS S3, Google Drive, Azure, GCP, Dropbox |
|
|
175
|
+
| **Configure deployment?** | **No** = storage only · **Yes** = storage + deploy |
|
|
176
|
+
| **CLI source for GitHub Actions** | Default `npm:@akash-chowdhury-24/deployhub` is fine for most users |
|
|
177
|
+
|
|
178
|
+
**Generated files:**
|
|
179
|
+
|
|
180
|
+
- `deployhub.config.json` — project settings (no secrets)
|
|
181
|
+
- `.github/workflows/deployhub.yml` — CI pipeline
|
|
182
|
+
- `.env.example` — list of env vars you may need
|
|
183
|
+
- `nginx.conf` — auto-generated if frontend deploys to SSH
|
|
184
|
+
- `firebase.json` — auto-generated if you pick Firebase Hosting
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### Step 2 — Credentials
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
cp .env.example .env
|
|
192
|
+
# Edit .env locally
|
|
193
|
+
|
|
194
|
+
deployhub storage add aws # repeat per provider
|
|
195
|
+
deployhub storage add gdrive
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For **GitHub Actions**, add the same values as repository secrets (Settings → Secrets and variables → Actions). See [GitHub Secrets](#github-secrets).
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Step 3 — Verify
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
deployhub doctor
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Fix any ✗ items before your first build.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### Step 4 — Build (and deploy if configured)
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
deployhub build
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Or push to `main` / `master` — the generated workflow runs the same command.
|
|
219
|
+
|
|
220
|
+
**Useful follow-up commands:**
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
deployhub artifact list # see uploaded versions
|
|
224
|
+
deployhub artifact restore v1.2.3 # download a past build
|
|
225
|
+
deployhub deploy # deploy latest artifact without rebuilding
|
|
226
|
+
deployhub rollback v1.2.2 # roll back on server
|
|
227
|
+
deployhub logs # last deployment logs
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Walkthrough: Storage only
|
|
233
|
+
|
|
234
|
+
Use this when you want **versioned build artifacts in the cloud** but deploy manually (or add deployment later).
|
|
235
|
+
|
|
236
|
+
### During `deployhub init`
|
|
237
|
+
|
|
238
|
+
1. Choose project type (frontend / backend / both).
|
|
239
|
+
2. Select framework and confirm build settings.
|
|
240
|
+
3. Check at least one **storage** provider (Local is checked by default).
|
|
241
|
+
4. Answer **Configure deployment?** → **No**.
|
|
242
|
+
|
|
243
|
+
### Resulting config
|
|
244
|
+
|
|
245
|
+
`deployhub.config.json` will have `"deploy": []` and `"pipeline": { "deploy": false }`. Every `deployhub build` still runs detect → install → test → build → artifact → **storage**.
|
|
246
|
+
|
|
247
|
+
### Example: React app → AWS S3 only
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
deployhub init
|
|
251
|
+
# What are you deploying? → Frontend only
|
|
252
|
+
# Framework → React
|
|
253
|
+
# Build command → npm run build
|
|
254
|
+
# Build output → dist
|
|
255
|
+
# Storage → ✓ AWS S3
|
|
256
|
+
# Configure deployment? → No
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
cp .env.example .env
|
|
261
|
+
# Set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_BUCKET, AWS_REGION
|
|
262
|
+
|
|
263
|
+
deployhub doctor
|
|
264
|
+
deployhub build
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Artifacts appear under `artifact/{projectName}/{date}/v{version}/` locally **and** in your S3 bucket.
|
|
268
|
+
|
|
269
|
+
### Same steps for other languages
|
|
270
|
+
|
|
271
|
+
Storage-only init is **identical** for every language — only the framework/build prompts change. See [Framework defaults](#framework-defaults-by-language).
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Walkthrough: Storage + deployment
|
|
276
|
+
|
|
277
|
+
Use this when you want **build → upload artifact → deploy** in one command.
|
|
278
|
+
|
|
279
|
+
### During `deployhub init`
|
|
280
|
+
|
|
281
|
+
1. Complete project type + framework setup.
|
|
282
|
+
2. Select storage provider(s).
|
|
283
|
+
3. Answer **Configure deployment?** → **Yes**.
|
|
284
|
+
4. Follow the deployment prompts (differs by project type — below).
|
|
285
|
+
|
|
286
|
+
Deployment **requires** at least one storage provider. DeployHub restores from the uploaded artifact on the target.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Walkthrough: Frontend only
|
|
291
|
+
|
|
292
|
+
### Init choices
|
|
293
|
+
|
|
294
|
+
| Prompt | Options |
|
|
295
|
+
|--------|---------|
|
|
296
|
+
| What are you deploying? | **Frontend only** |
|
|
297
|
+
| Framework | React, Vue, Angular, Next.js, Svelte, Astro, Vanilla JS, Other |
|
|
298
|
+
| Configure deployment? | No (storage only) or Yes |
|
|
299
|
+
|
|
300
|
+
If **Yes** to deployment:
|
|
301
|
+
|
|
302
|
+
| Prompt | Options |
|
|
303
|
+
|--------|---------|
|
|
304
|
+
| How do you want to deploy? | **Managed platform** or **Self-hosted server** |
|
|
305
|
+
|
|
306
|
+
#### Option A — Managed platform (Vercel, Netlify, …)
|
|
307
|
+
|
|
308
|
+
Best for static sites and Jamstack frontends. DeployHub builds locally/CI, uploads the artifact, then invokes the platform CLI.
|
|
309
|
+
|
|
310
|
+
| Platform | Best for | Secrets to add |
|
|
311
|
+
|----------|----------|----------------|
|
|
312
|
+
| Vercel | Next.js, React | `VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` |
|
|
313
|
+
| Netlify | React, Vue, Svelte | `NETLIFY_AUTH_TOKEN`, `NETLIFY_SITE_ID` |
|
|
314
|
+
| Cloudflare Pages | Astro, static React | `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `CF_PROJECT_NAME` |
|
|
315
|
+
| AWS Amplify | React, Next.js on AWS | `AWS_*`, `AMPLIFY_APP_ID` |
|
|
316
|
+
| Azure Static Web Apps | React, Angular | `AZURE_STATIC_WEB_APPS_TOKEN` |
|
|
317
|
+
| Firebase Hosting | SPAs | `FIREBASE_TOKEN`, `FIREBASE_PROJECT_ID` |
|
|
318
|
+
| Firebase App Hosting | Next.js / Angular SSR | `FIREBASE_TOKEN`, `FIREBASE_PROJECT_ID`, `FIREBASE_APP_HOSTING_BACKEND` |
|
|
319
|
+
|
|
320
|
+
**Example: Vue → Google Drive + Netlify**
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
deployhub init
|
|
324
|
+
# Frontend only → Vue
|
|
325
|
+
# Storage: Local + Google Drive
|
|
326
|
+
# Configure deployment? Yes
|
|
327
|
+
# Managed platform → Netlify
|
|
328
|
+
# Site ID: (from Netlify dashboard)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
cp .env.example .env
|
|
333
|
+
# GDRIVE_* and NETLIFY_AUTH_TOKEN, NETLIFY_SITE_ID
|
|
334
|
+
|
|
335
|
+
deployhub doctor
|
|
336
|
+
git push origin main
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### Option B — Self-hosted server (SSH, Docker, EC2, …)
|
|
340
|
+
|
|
341
|
+
Best when you serve static files from your own VPS. DeployHub uploads the built `dist/` (or your output dir) over SSH and can generate `nginx.conf`.
|
|
342
|
+
|
|
343
|
+
| Deploy type | You provide |
|
|
344
|
+
|-------------|-------------|
|
|
345
|
+
| **ssh** | `SSH_HOST`, `SSH_USER`, `SSH_KEY`, deploy path |
|
|
346
|
+
| **docker** | Docker host access / image registry per your setup |
|
|
347
|
+
| **ec2** | SSH credentials to EC2 instance |
|
|
348
|
+
| **azure-vm** / **gcp-vm** | SSH to VM |
|
|
349
|
+
| **kubernetes** | Cluster credentials (via env / kubeconfig) |
|
|
350
|
+
|
|
351
|
+
**Example: Angular → Azure Blob + SSH**
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
deployhub init
|
|
355
|
+
# Frontend only → Angular
|
|
356
|
+
# Build: ng build, output dist
|
|
357
|
+
# Storage: Azure Blob
|
|
358
|
+
# Configure deployment? Yes → Self-hosted → ssh
|
|
359
|
+
# Host, user, deploy path: /var/www/my-app
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Add `AZURE_*`, `SSH_HOST`, `SSH_USER`, `SSH_KEY` to `.env` and GitHub Secrets. Review the generated `nginx.conf` and install it on the server.
|
|
363
|
+
|
|
364
|
+
#### Frontend framework defaults
|
|
365
|
+
|
|
366
|
+
| Framework | Build command | Output dir | Notes |
|
|
367
|
+
|-----------|---------------|------------|-------|
|
|
368
|
+
| React | `npm run build` | `dist` or `build` | Create React App uses `build` |
|
|
369
|
+
| Vue | `npm run build` | `dist` | Vite default |
|
|
370
|
+
| Angular | `ng build` | `dist` | |
|
|
371
|
+
| Next.js | `npm run build` | `.next` | Platform deploy recommended |
|
|
372
|
+
| Svelte | `npm run build` | `public` | |
|
|
373
|
+
| Astro | `astro build` | `dist` | |
|
|
374
|
+
| Vanilla JS | *(none)* | `.` | Copies static files as-is |
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Walkthrough: Backend only
|
|
379
|
+
|
|
380
|
+
Backends always deploy to a **self-hosted target** (SSH, Docker, EC2, Azure VM, GCP VM, or Kubernetes). There is no “managed platform” path for backend-only projects.
|
|
381
|
+
|
|
382
|
+
### Init choices
|
|
383
|
+
|
|
384
|
+
| Prompt | Typical value |
|
|
385
|
+
|--------|----------------|
|
|
386
|
+
| What are you deploying? | **Backend only** |
|
|
387
|
+
| Language / framework | See table below |
|
|
388
|
+
| Start command | e.g. `npm start`, `uvicorn main:app …` |
|
|
389
|
+
| Port | e.g. 3000, 8000, 8080 |
|
|
390
|
+
| Storage | At least one provider |
|
|
391
|
+
| Configure deployment? | Yes for storage + deploy |
|
|
392
|
+
| Deployment type | ssh (most common), docker, ec2, kubernetes, … |
|
|
393
|
+
| App name | PM2 process name on server |
|
|
394
|
+
| Health check URL | e.g. `https://api.example.com/health` |
|
|
395
|
+
|
|
396
|
+
### Example: Express API → S3 + SSH
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
deployhub init
|
|
400
|
+
# Backend only → Node.js Express
|
|
401
|
+
# Start: npm start, port 3000
|
|
402
|
+
# Storage: AWS S3
|
|
403
|
+
# Configure deployment? Yes → ssh
|
|
404
|
+
# Host: 203.0.113.10, user: deploy, path: /var/www/my-api
|
|
405
|
+
# App name: my-api
|
|
406
|
+
# Health URL: https://api.example.com/health
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
On the server, ensure **Node.js**, **PM2**, and your app dependencies are available. DeployHub SSHs in, extracts the artifact, runs install if needed, and restarts PM2.
|
|
410
|
+
|
|
411
|
+
### Example: FastAPI → Dropbox + SSH
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
deployhub init
|
|
415
|
+
# Backend only → Python FastAPI
|
|
416
|
+
# Start: uvicorn main:app --host 0.0.0.0 --port 8000
|
|
417
|
+
# Storage: Dropbox
|
|
418
|
+
# Deploy: ssh
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Server needs **Python 3.11+**, `pip`, and ideally **gunicorn/uvicorn** for production.
|
|
422
|
+
|
|
423
|
+
### Backend framework defaults
|
|
424
|
+
|
|
425
|
+
| Framework | Language | Build | Start | Port | Test |
|
|
426
|
+
|-----------|----------|-------|-------|------|------|
|
|
427
|
+
| Express | Node | — | `npm start` | 3000 | `npm test` |
|
|
428
|
+
| NestJS | Node | `nest build` | `node dist/main` | 3000 | `npm test` |
|
|
429
|
+
| Fastify / Koa | Node | — | `npm start` | 3000 | `npm test` |
|
|
430
|
+
| FastAPI | Python | — | `uvicorn main:app --host 0.0.0.0 --port 8000` | 8000 | `pytest` |
|
|
431
|
+
| Django | Python | — | `gunicorn config.wsgi:application --bind 0.0.0.0:8000` | 8000 | `python manage.py test` |
|
|
432
|
+
| Flask | Python | — | `gunicorn app:app --bind 0.0.0.0:5000` | 5000 | `pytest` |
|
|
433
|
+
| Laravel | PHP | — | `php artisan serve` | 80 | `php artisan test` |
|
|
434
|
+
| Symfony | PHP | — | `php-fpm` | 80 | `php bin/phpunit` |
|
|
435
|
+
| Spring Boot | Java | `mvn package` | `java -jar target/*.jar` | 8080 | `mvn test` |
|
|
436
|
+
| Go | Go | `go build -o bin/app .` | `./bin/app` | 8080 | `go test ./...` |
|
|
437
|
+
| .NET | C# | `dotnet publish -c Release -o publish` | `dotnet App.dll` | 5000 | `dotnet test` |
|
|
438
|
+
| Rails | Ruby | `bundle exec rake assets:precompile` | `bundle exec puma` | 3000 | `bundle exec rspec` |
|
|
439
|
+
|
|
440
|
+
**Node.js backends** without a build step still get packaged; set `buildCommand` to empty in config if you truly have no compile step.
|
|
441
|
+
|
|
442
|
+
**Java / Go / .NET** always run a compile step before artifact creation.
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Walkthrough: Full stack (frontend + backend)
|
|
447
|
+
|
|
448
|
+
Choose **Both (monorepo / fullstack)** when frontend and backend live in the **same repository root** (typical monorepo layout).
|
|
449
|
+
|
|
450
|
+
### Init flow
|
|
451
|
+
|
|
452
|
+
1. **Frontend** — framework, build command, output directory.
|
|
453
|
+
2. **Backend** — framework, start command, port.
|
|
454
|
+
3. **Storage** — one or more providers.
|
|
455
|
+
4. **Configure deployment?** → Yes.
|
|
456
|
+
5. **Frontend deploy method** — Managed platform **or** self-hosted SSH.
|
|
457
|
+
6. **Backend deploy** — always self-hosted (SSH, Docker, EC2, …).
|
|
458
|
+
|
|
459
|
+
DeployHub runs **both** builds, packs them into one artifact, uploads once, then deploys frontend and backend to their respective targets.
|
|
460
|
+
|
|
461
|
+
### Example: React + Express monorepo
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
deployhub init
|
|
465
|
+
# Both (monorepo / fullstack)
|
|
466
|
+
# Frontend: React, npm run build, dist
|
|
467
|
+
# Backend: Express, npm start, port 3000
|
|
468
|
+
# Storage: AWS S3 + Local
|
|
469
|
+
# Configure deployment? Yes
|
|
470
|
+
# Frontend: Managed platform → Vercel
|
|
471
|
+
# Backend: ssh → api.example.com, path /var/www/my-app/api, PM2 name my-app-api
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Secrets:** AWS + `VERCEL_*` + `SSH_*`
|
|
475
|
+
|
|
476
|
+
**Layout tip:** Keep `package.json` scripts for both apps at the repo root, or ensure build commands point to the correct paths (edit `deployhub.config.json` after init if your monorepo uses subfolders).
|
|
477
|
+
|
|
478
|
+
### Example: React + Express, both on one VPS
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
deployhub init
|
|
482
|
+
# Both → React + Express
|
|
483
|
+
# Frontend deploy: Self-hosted server
|
|
484
|
+
# Frontend path: /var/www/my-app/public
|
|
485
|
+
# Backend: ssh, path /var/www/my-app/api
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
DeployHub generates `nginx.conf` to serve static files and proxy API requests.
|
|
489
|
+
|
|
490
|
+
### Example: Next.js API routes only
|
|
491
|
+
|
|
492
|
+
Use **Frontend only** with **Next.js** and deploy to **Vercel** or **Firebase App Hosting** — no separate backend entry needed.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## Framework defaults by language
|
|
497
|
+
|
|
498
|
+
### JavaScript / TypeScript (Node)
|
|
499
|
+
|
|
500
|
+
All JS frontends share the same install/build flow: `npm ci` → `npm run build` → artifact from output directory.
|
|
501
|
+
|
|
502
|
+
| Role | Frameworks |
|
|
503
|
+
|------|------------|
|
|
504
|
+
| Frontend | React, Vue, Angular, Next.js, Svelte, Astro, Vanilla |
|
|
505
|
+
| Backend | Express, NestJS, Fastify, Koa |
|
|
506
|
+
|
|
507
|
+
**Init is the same** for each; only default build/output/start commands differ (tables above).
|
|
508
|
+
|
|
509
|
+
### Python
|
|
510
|
+
|
|
511
|
+
- **Detect:** `requirements.txt` containing `fastapi`, `django`, or `flask`.
|
|
512
|
+
- **Install:** `pip install -r requirements.txt`
|
|
513
|
+
- **Test:** `pytest` (if `pytest.ini` exists) or Django test runner.
|
|
514
|
+
- **Build:** Usually skipped (`buildCommand: null`); artifact includes source + dependencies list.
|
|
515
|
+
- **Deploy (SSH):** Server must have Python + pip; start command runs uvicorn/gunicorn.
|
|
516
|
+
|
|
517
|
+
### PHP
|
|
518
|
+
|
|
519
|
+
- **Detect:** `composer.json` with `laravel/framework` or `symfony/framework-bundle`.
|
|
520
|
+
- **Install:** Composer (on CI and server).
|
|
521
|
+
- **Deploy:** SSH with PHP-FPM or `php artisan` for Laravel.
|
|
522
|
+
|
|
523
|
+
### Java
|
|
524
|
+
|
|
525
|
+
- **Detect:** `pom.xml` with Spring Boot.
|
|
526
|
+
- **Install/Build:** `mvn package` produces JAR in `target/`.
|
|
527
|
+
- **Deploy:** SSH runs `java -jar target/*.jar` (or your custom start command).
|
|
528
|
+
|
|
529
|
+
### Go
|
|
530
|
+
|
|
531
|
+
- **Detect:** `go.mod` present.
|
|
532
|
+
- **Build:** `go build -o bin/app .`
|
|
533
|
+
- **Artifact:** `bin/` binary + any config files.
|
|
534
|
+
- **Deploy:** SSH copies binary and restarts process.
|
|
535
|
+
|
|
536
|
+
### .NET
|
|
537
|
+
|
|
538
|
+
- **Detect:** `.csproj` in project root.
|
|
539
|
+
- **Build:** `dotnet publish -c Release -o publish`
|
|
540
|
+
- **Deploy:** SSH runs `dotnet YourApp.dll` from publish folder.
|
|
541
|
+
|
|
542
|
+
### Ruby
|
|
543
|
+
|
|
544
|
+
- **Detect:** `Gemfile` with `rails`.
|
|
545
|
+
- **Build:** `bundle exec rake assets:precompile` (for production assets).
|
|
546
|
+
- **Deploy:** SSH with `bundle exec puma` or your configured start command.
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
## GitHub Actions setup
|
|
551
|
+
|
|
552
|
+
After `init`, commit these files:
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
git add deployhub.config.json .github/workflows/deployhub.yml .env.example
|
|
556
|
+
git commit -m "Add DeployHub CI"
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
1. Open **Settings → Secrets and variables → Actions** in your GitHub repo.
|
|
560
|
+
2. Add every secret listed at the end of `deployhub init` (storage + deployment).
|
|
561
|
+
3. Push to `main` or `master` — the workflow triggers on push.
|
|
562
|
+
|
|
563
|
+
The workflow installs the correct language runtime (Node, Python, Java, Go, .NET, Ruby) based on your `deployhub.config.json`, installs DeployHub, runs `deployhub build`, and uses your secrets.
|
|
564
|
+
|
|
565
|
+
To run manually: **Actions → DeployHub → Run workflow**.
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Choosing storage providers
|
|
570
|
+
|
|
571
|
+
| Provider | Good for | Setup command |
|
|
572
|
+
|----------|----------|---------------|
|
|
573
|
+
| **Local** | Dev/testing, no cloud account | No credentials |
|
|
574
|
+
| **AWS S3** | Production, CI-friendly | `deployhub storage add aws` |
|
|
575
|
+
| **Google Drive** | Small teams, manual downloads | `deployhub storage add gdrive` |
|
|
576
|
+
| **Azure Blob** | Azure ecosystem | `deployhub storage add azure` |
|
|
577
|
+
| **GCP Storage** | GCP ecosystem | `deployhub storage add gcp` |
|
|
578
|
+
| **Dropbox** | Simple off-site backup | `deployhub storage add dropbox` |
|
|
579
|
+
|
|
580
|
+
You can enable **multiple providers** — DeployHub uploads to all of them in parallel on every build.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Deployment target cheat sheet
|
|
585
|
+
|
|
586
|
+
| Project type | Frontend deploy options | Backend deploy options |
|
|
587
|
+
|--------------|-------------------------|------------------------|
|
|
588
|
+
| Frontend only | Platform **or** SSH/Docker/EC2/K8s | — |
|
|
589
|
+
| Backend only | — | SSH/Docker/EC2/Azure VM/GCP VM/K8s |
|
|
590
|
+
| Full stack | Platform **or** SSH (static + nginx) | SSH/Docker/EC2/K8s (always) |
|
|
591
|
+
|
|
592
|
+
| Mode | Storage | Deploy | When to use |
|
|
593
|
+
|------|---------|--------|-------------|
|
|
594
|
+
| **Storage only** | ✓ | ✗ | Backups, audit trail, manual releases |
|
|
595
|
+
| **Storage + deploy** | ✓ | ✓ | Full CI/CD |
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## Minimal `deployhub.config.json` examples
|
|
600
|
+
|
|
601
|
+
### Storage only — React
|
|
602
|
+
|
|
603
|
+
```json
|
|
604
|
+
{
|
|
605
|
+
"project": "my-react-app",
|
|
606
|
+
"projectType": "frontend",
|
|
607
|
+
"framework": "react",
|
|
608
|
+
"buildCommand": "npm run build",
|
|
609
|
+
"buildOutput": "dist",
|
|
610
|
+
"storage": ["local", "aws"],
|
|
611
|
+
"deploy": [],
|
|
612
|
+
"pipeline": { "test": true, "deploy": false, "verify": false }
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Storage + deploy — FastAPI on SSH
|
|
617
|
+
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"project": "my-api",
|
|
621
|
+
"projectType": "backend",
|
|
622
|
+
"framework": "fastapi",
|
|
623
|
+
"language": "python",
|
|
624
|
+
"startCommand": "uvicorn main:app --host 0.0.0.0 --port 8000",
|
|
625
|
+
"port": 8000,
|
|
626
|
+
"storage": ["aws"],
|
|
627
|
+
"deploy": ["production"],
|
|
628
|
+
"environments": {
|
|
629
|
+
"production": {
|
|
630
|
+
"deploymentType": "server",
|
|
631
|
+
"type": "ssh",
|
|
632
|
+
"host": "203.0.113.10",
|
|
633
|
+
"user": "deploy",
|
|
634
|
+
"deployPath": "/var/www/my-api",
|
|
635
|
+
"appName": "my-api",
|
|
636
|
+
"framework": "fastapi"
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
"pipeline": { "deploy": true, "verify": true },
|
|
640
|
+
"healthCheck": { "url": "https://api.example.com/health", "timeout": 30 }
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Prefer `deployhub init` over hand-writing config — it sets adapters, workflow, and secrets list correctly.
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## Troubleshooting
|
|
649
|
+
|
|
650
|
+
| Problem | Fix |
|
|
651
|
+
|---------|-----|
|
|
652
|
+
| `Deploy requires storage upload` | Add at least one storage provider in config |
|
|
653
|
+
| AWS / GDrive check fails in `doctor` | Run `deployhub storage add <provider>` and match GitHub Secrets |
|
|
654
|
+
| SSH deploy fails | Verify `SSH_KEY` is the **private** key; user can write to deploy path |
|
|
655
|
+
| Wrong output uploaded | Fix `buildOutput` in config (`dist` vs `build` vs `.next`) |
|
|
656
|
+
| Tests fail in CI | Set `"pipeline": { "test": false }` temporarily, or fix tests |
|
|
657
|
+
| Platform deploy missing CLI | Install platform CLI in CI (workflow does this for Vercel, Netlify, etc.) |
|
|
658
|
+
| Monorepo subfolders | Edit `buildCommand` paths in `deployhub.config.json` after init |
|
|
659
|
+
|
|
660
|
+
Run `deployhub doctor` after any config change.
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
112
664
|
## Commands
|
|
113
665
|
|
|
114
666
|
| Command | Description |
|
|
@@ -130,7 +682,9 @@ Or push to `main` — GitHub Actions runs `deployhub build` automatically.
|
|
|
130
682
|
|
|
131
683
|
## GitHub Secrets
|
|
132
684
|
|
|
133
|
-
Add these secrets in your repository (Settings → Secrets and variables → Actions). Only add secrets for providers you selected during `init
|
|
685
|
+
Add these secrets in your repository (Settings → Secrets and variables → Actions). Only add secrets for providers you selected during `init`. At the end of `deployhub init`, DeployHub prints the exact list for your project.
|
|
686
|
+
|
|
687
|
+
### Storage
|
|
134
688
|
|
|
135
689
|
| Secret | Provider |
|
|
136
690
|
|--------|----------|
|
|
@@ -148,9 +702,30 @@ Add these secrets in your repository (Settings → Secrets and variables → Act
|
|
|
148
702
|
| `GCP_KEY_FILE` | GCP Storage |
|
|
149
703
|
| `GCP_BUCKET` | GCP Storage |
|
|
150
704
|
| `DROPBOX_ACCESS_TOKEN` | Dropbox |
|
|
151
|
-
| `
|
|
152
|
-
|
|
153
|
-
|
|
705
|
+
| `FTP_HOST`, `FTP_USER`, `FTP_PASSWORD` | FTP storage |
|
|
706
|
+
|
|
707
|
+
### Server deployment (SSH, EC2, VMs)
|
|
708
|
+
|
|
709
|
+
| Secret | Used for |
|
|
710
|
+
|--------|----------|
|
|
711
|
+
| `SSH_HOST` | Target server hostname |
|
|
712
|
+
| `SSH_USER` | SSH username |
|
|
713
|
+
| `SSH_KEY` | Private SSH key (PEM) |
|
|
714
|
+
| `SSH_DEPLOY_PATH` | Remote directory (optional if set in config) |
|
|
715
|
+
| `SSH_APP_NAME` | PM2 process name for backends |
|
|
716
|
+
| `SSH_PORT` | App port on server (optional) |
|
|
717
|
+
|
|
718
|
+
### Managed platforms (frontend)
|
|
719
|
+
|
|
720
|
+
| Secret | Platform |
|
|
721
|
+
|--------|----------|
|
|
722
|
+
| `VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` | Vercel |
|
|
723
|
+
| `NETLIFY_AUTH_TOKEN`, `NETLIFY_SITE_ID` | Netlify |
|
|
724
|
+
| `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `CF_PROJECT_NAME` | Cloudflare Pages |
|
|
725
|
+
| `AMPLIFY_APP_ID` (+ `AWS_*` if not already set) | AWS Amplify |
|
|
726
|
+
| `AZURE_STATIC_WEB_APPS_TOKEN` | Azure Static Web Apps |
|
|
727
|
+
| `FIREBASE_TOKEN`, `FIREBASE_PROJECT_ID` | Firebase Hosting |
|
|
728
|
+
| `FIREBASE_APP_HOSTING_BACKEND` | Firebase App Hosting |
|
|
154
729
|
|
|
155
730
|
## `deployhub doctor` Output
|
|
156
731
|
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -14,6 +14,7 @@ import { getProjectVersion } from '../utils/version.js';
|
|
|
14
14
|
import {
|
|
15
15
|
writeWorkflowFile,
|
|
16
16
|
getRequiredSecrets,
|
|
17
|
+
generateEnvExampleContent,
|
|
17
18
|
guessCliGithubRepo,
|
|
18
19
|
addDeployhubToPackageJson,
|
|
19
20
|
} from '../utils/github-actions.js';
|
|
@@ -288,25 +289,6 @@ function buildServerEnvEntry(
|
|
|
288
289
|
return envEntry;
|
|
289
290
|
}
|
|
290
291
|
|
|
291
|
-
/**
|
|
292
|
-
* @returns {Promise<string>}
|
|
293
|
-
*/
|
|
294
|
-
async function getEnvExampleContent() {
|
|
295
|
-
const possiblePaths = [
|
|
296
|
-
path.join(path.dirname(process.execPath), '.env.example'),
|
|
297
|
-
path.join(process.cwd(), '.env.example'),
|
|
298
|
-
new URL('../../.env.example', import.meta.url).pathname,
|
|
299
|
-
];
|
|
300
|
-
for (const p of possiblePaths) {
|
|
301
|
-
try {
|
|
302
|
-
return await fs.readFile(p, 'utf-8');
|
|
303
|
-
} catch {
|
|
304
|
-
// try next path
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return '# Add your environment variables here\n';
|
|
308
|
-
}
|
|
309
|
-
|
|
310
292
|
/**
|
|
311
293
|
* @param {Record<string, unknown>} config
|
|
312
294
|
* @param {Record<string, Record<string, unknown>>} environments
|
|
@@ -780,7 +762,12 @@ export function registerInitCommand(program) {
|
|
|
780
762
|
await generateProjectScaffold(config, environments, cwd);
|
|
781
763
|
|
|
782
764
|
const envExampleDest = path.join(cwd, '.env.example');
|
|
783
|
-
const envExampleContent =
|
|
765
|
+
const envExampleContent = generateEnvExampleContent(
|
|
766
|
+
config.storage,
|
|
767
|
+
deploy,
|
|
768
|
+
environments,
|
|
769
|
+
config
|
|
770
|
+
);
|
|
784
771
|
await fs.writeFile(envExampleDest, envExampleContent);
|
|
785
772
|
|
|
786
773
|
const secrets = getRequiredSecrets(config.storage, deploy, environments, config);
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
PLATFORM_ENV_MAP,
|
|
5
|
+
PLATFORM_CLI_MAP,
|
|
6
|
+
PLATFORM_CHOICES,
|
|
7
|
+
getPlatformEnvExample,
|
|
8
|
+
} from './platform-env.js';
|
|
4
9
|
import { getWorkflowHeaderComment } from './author.js';
|
|
5
10
|
|
|
6
11
|
/** @typedef {'aws'|'azure'|'gcp'|'gdrive'|'dropbox'|'local'|'ftp'|'ssh'} ProviderEnvKey */
|
|
@@ -32,6 +37,28 @@ const BACKEND_SSH_ENV_VARS = [
|
|
|
32
37
|
'SSH_PORT',
|
|
33
38
|
];
|
|
34
39
|
|
|
40
|
+
const PROVIDER_LABELS = {
|
|
41
|
+
aws: 'AWS S3',
|
|
42
|
+
azure: 'Azure Blob',
|
|
43
|
+
gcp: 'GCP',
|
|
44
|
+
gdrive: 'Google Drive',
|
|
45
|
+
dropbox: 'Dropbox',
|
|
46
|
+
ftp: 'FTP',
|
|
47
|
+
ssh: 'SSH Deployment',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const PLATFORM_LABELS = Object.fromEntries(
|
|
51
|
+
PLATFORM_CHOICES.map(({ name, value }) => [value, name])
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const ENV_VAR_DEFAULTS = {
|
|
55
|
+
AWS_REGION: 'us-east-1',
|
|
56
|
+
FTP_PORT: '21',
|
|
57
|
+
FTP_PATH: '/uploads',
|
|
58
|
+
SSH_DEPLOY_PATH: '/var/www/app',
|
|
59
|
+
SMTP_PORT: '587',
|
|
60
|
+
};
|
|
61
|
+
|
|
35
62
|
const NPM_PACKAGE = '@akash-chowdhury-24/deployhub';
|
|
36
63
|
const DEFAULT_NPM_CLI_SOURCE = `npm:${NPM_PACKAGE}`;
|
|
37
64
|
|
|
@@ -400,4 +427,113 @@ export function getRequiredSecrets(
|
|
|
400
427
|
return Array.from(secrets);
|
|
401
428
|
}
|
|
402
429
|
|
|
430
|
+
/**
|
|
431
|
+
* @param {string} title
|
|
432
|
+
* @param {string[]} keys
|
|
433
|
+
* @param {Record<string, string>} [defaults]
|
|
434
|
+
* @param {Set<string>} seenKeys
|
|
435
|
+
* @returns {string}
|
|
436
|
+
*/
|
|
437
|
+
function formatEnvSection(title, keys, defaults, seenKeys) {
|
|
438
|
+
const newKeys = keys.filter((key) => !seenKeys.has(key));
|
|
439
|
+
if (newKeys.length === 0) return '';
|
|
440
|
+
|
|
441
|
+
for (const key of newKeys) {
|
|
442
|
+
seenKeys.add(key);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const lines = newKeys.map((key) => {
|
|
446
|
+
const value = defaults?.[key] ?? ENV_VAR_DEFAULTS[key] ?? '';
|
|
447
|
+
return value ? `${key}=${value}` : `${key}=`;
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
return `# ${title}\n${lines.join('\n')}\n`;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @param {string[]} storageProviders
|
|
455
|
+
* @param {string[]} deployEnvironments
|
|
456
|
+
* @param {Record<string, Record<string, unknown>>} environments
|
|
457
|
+
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
458
|
+
* @returns {string}
|
|
459
|
+
*/
|
|
460
|
+
export function generateEnvExampleContent(
|
|
461
|
+
storageProviders,
|
|
462
|
+
deployEnvironments,
|
|
463
|
+
environments,
|
|
464
|
+
config = null
|
|
465
|
+
) {
|
|
466
|
+
/** @type {Set<string>} */
|
|
467
|
+
const seenKeys = new Set();
|
|
468
|
+
/** @type {string[]} */
|
|
469
|
+
const sections = [];
|
|
470
|
+
|
|
471
|
+
const addSection = (title, keys, defaults = {}) => {
|
|
472
|
+
const section = formatEnvSection(title, keys, defaults, seenKeys);
|
|
473
|
+
if (section) sections.push(section);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
for (const provider of storageProviders) {
|
|
477
|
+
const keys = PROVIDER_ENV_MAP[provider] || [];
|
|
478
|
+
if (keys.length > 0) {
|
|
479
|
+
addSection(PROVIDER_LABELS[provider] || provider, keys);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
for (const envName of deployEnvironments) {
|
|
484
|
+
const env = environments[envName];
|
|
485
|
+
if (!env) continue;
|
|
486
|
+
|
|
487
|
+
if (env.deploymentType === 'platform' || env.frontendDeploymentType === 'platform') {
|
|
488
|
+
const platform = env.platform;
|
|
489
|
+
if (platform) {
|
|
490
|
+
const examples = getPlatformEnvExample(platform);
|
|
491
|
+
addSection(
|
|
492
|
+
PLATFORM_LABELS[platform] || platform,
|
|
493
|
+
Object.keys(examples),
|
|
494
|
+
examples
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const keys = PROVIDER_ENV_MAP[env.type] || [];
|
|
501
|
+
if (keys.length > 0) {
|
|
502
|
+
addSection(PROVIDER_LABELS[env.type] || env.type, keys);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (env.type === 'ssh' && config) {
|
|
506
|
+
const projectType = config.projectType || 'frontend';
|
|
507
|
+
if (projectType === 'backend' || projectType === 'both') {
|
|
508
|
+
addSection('SSH Deployment (backend)', BACKEND_SSH_ENV_VARS);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (config?.notifications) {
|
|
514
|
+
if (config.notifications.slack) {
|
|
515
|
+
addSection('Notifications', ['SLACK_WEBHOOK_URL']);
|
|
516
|
+
}
|
|
517
|
+
if (config.notifications.webhook) {
|
|
518
|
+
addSection('Notifications', ['WEBHOOK_URL']);
|
|
519
|
+
}
|
|
520
|
+
if (config.notifications.email) {
|
|
521
|
+
addSection('Email (SMTP)', [
|
|
522
|
+
'SMTP_HOST',
|
|
523
|
+
'SMTP_PORT',
|
|
524
|
+
'SMTP_USER',
|
|
525
|
+
'SMTP_PASS',
|
|
526
|
+
'NOTIFICATION_EMAIL',
|
|
527
|
+
'NOTIFY_EMAIL_TO',
|
|
528
|
+
]);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (sections.length === 0) {
|
|
533
|
+
return '# Add your environment variables here\n';
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return `${sections.join('\n')}\n`;
|
|
537
|
+
}
|
|
538
|
+
|
|
403
539
|
export { PROVIDER_ENV_MAP };
|