@akash-chowdhury-24/deployhub 1.0.15 → 2.0.0

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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  Zero-configuration deployment and artifact manager for Node.js projects. When you push to GitHub, DeployHub automatically detects your project type, builds it, creates a versioned artifact, uploads to cloud storage, and optionally deploys to your server.
4
4
 
5
+ **Supported deployment targets:** SSH, Docker, EC2, Azure VM, GCP VM, and Kubernetes — self-hosted and cloud VM only.
6
+
7
+ DeployHub no longer integrates with managed platforms like Vercel or Netlify — those tools already offer superior native git-push deployment. DeployHub instead focuses on artifact-first backups and self-hosted/server deployment, where no equivalent native solution exists.
8
+
5
9
  ## Installation
6
10
 
7
11
  DeployHub can be installed via **npm** (requires Node.js 18+) or as a **standalone binary** (no Node.js required).
@@ -80,7 +84,7 @@ This interactive wizard will:
80
84
  - Detect your framework (React, Vue, Next.js, Node, Python, etc.)
81
85
  - Configure build commands and output directory
82
86
  - Set up storage providers (AWS, Google Drive, Azure, GCP, Dropbox, Local)
83
- - Optionally configure deployment targets (SSH, Docker, EC2, Kubernetes, etc.)
87
+ - Optionally configure deployment targets (SSH, Docker, EC2, Azure VM, GCP VM, Kubernetes)
84
88
  - Generate `deployhub.config.json`
85
89
  - Generate `.github/workflows/deployhub.yml`
86
90
  - Generate `.env.example`
@@ -132,7 +136,7 @@ When you run `deployhub build` (locally or in GitHub Actions), DeployHub runs th
132
136
  | **verify** | Hit your health-check URL — **only if configured** |
133
137
  | **notify** | Slack / email / webhook — **only if enabled** |
134
138
 
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.
139
+ **Storage only** means you answer **No** to *Configure deployment?* during `init`. You still get builds and cloud backups; nothing is pushed to a server.
136
140
 
137
141
  **Storage + deployment** means you answer **Yes**, pick targets, and add the matching secrets. Deploy always runs **after** storage upload succeeds.
138
142
 
@@ -181,7 +185,6 @@ The wizard asks the same core questions for every setup:
181
185
  - `.github/workflows/deployhub.yml` — CI pipeline
182
186
  - `.env.example` — list of env vars you may need
183
187
  - `nginx.conf` — auto-generated if frontend deploys to SSH
184
- - `firebase.json` — auto-generated if you pick Firebase Hosting
185
188
 
186
189
  ---
187
190
 
@@ -301,53 +304,39 @@ If **Yes** to deployment:
301
304
 
302
305
  | Prompt | Options |
303
306
  |--------|---------|
304
- | How do you want to deploy? | **Managed platform** or **Self-hosted server** |
307
+ | Deployment type | **ssh**, docker, ec2, azure-vm, gcp-vm, kubernetes |
308
+ | Host, user, deploy path | SSH credentials and remote directory |
305
309
 
306
- #### Option A — Managed platform (Vercel, Netlify, …)
310
+ #### Self-hosted server (SSH, Docker, EC2, …)
307
311
 
308
- Best for static sites and Jamstack frontends. DeployHub builds locally/CI, uploads the artifact, then invokes the platform CLI.
312
+ Best when you serve static files from your own VPS or cloud VM. DeployHub uploads the built `dist/` (or your output dir) over SSH and can generate `nginx.conf`.
309
313
 
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` |
314
+ | Deploy type | You provide |
315
+ |-------------|-------------|
316
+ | **ssh** | `SSH_HOST`, `SSH_USER`, `SSH_KEY`, deploy path |
317
+ | **docker** | Docker host access / image registry per your setup |
318
+ | **ec2** | SSH credentials to EC2 instance |
319
+ | **azure-vm** / **gcp-vm** | SSH to VM |
320
+ | **kubernetes** | Cluster credentials (via env / kubeconfig) |
319
321
 
320
- **Example: Vue → Google Drive + Netlify**
322
+ **Example: Vue → Google Drive + SSH**
321
323
 
322
324
  ```bash
323
325
  deployhub init
324
326
  # Frontend only → Vue
325
327
  # Storage: Local + Google Drive
326
- # Configure deployment? Yes
327
- # Managed platform Netlify
328
- # Site ID: (from Netlify dashboard)
328
+ # Configure deployment? Yes → ssh
329
+ # Host, user, deploy path: /var/www/my-app
329
330
  ```
330
331
 
331
332
  ```bash
332
333
  cp .env.example .env
333
- # GDRIVE_* and NETLIFY_AUTH_TOKEN, NETLIFY_SITE_ID
334
+ # GDRIVE_* and SSH_HOST, SSH_USER, SSH_KEY
334
335
 
335
336
  deployhub doctor
336
337
  git push origin main
337
338
  ```
338
339
 
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
340
  **Example: Angular → Azure Blob + SSH**
352
341
 
353
342
  ```bash
@@ -368,7 +357,7 @@ Add `AZURE_*`, `SSH_HOST`, `SSH_USER`, `SSH_KEY` to `.env` and GitHub Secrets. R
368
357
  | React | `npm run build` | `dist` or `build` | Create React App uses `build` |
369
358
  | Vue | `npm run build` | `dist` | Vite default |
370
359
  | Angular | `ng build` | `dist` | |
371
- | Next.js | `npm run build` | `.next` | Platform deploy recommended |
360
+ | Next.js | `npm run build` | `.next` | Use Vercel/Netlify native deploy for managed hosting |
372
361
  | Svelte | `npm run build` | `public` | |
373
362
  | Astro | `astro build` | `dist` | |
374
363
  | Vanilla JS | *(none)* | `.` | Copies static files as-is |
@@ -377,7 +366,7 @@ Add `AZURE_*`, `SSH_HOST`, `SSH_USER`, `SSH_KEY` to `.env` and GitHub Secrets. R
377
366
 
378
367
  ## Walkthrough: Backend only
379
368
 
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.
369
+ Backends always deploy to a **self-hosted target** (SSH, Docker, EC2, Azure VM, GCP VM, or Kubernetes).
381
370
 
382
371
  ### Init choices
383
372
 
@@ -453,10 +442,10 @@ Choose **Both (monorepo / fullstack)** when frontend and backend live in the **s
453
442
  2. **Backend** — framework, start command, port.
454
443
  3. **Storage** — one or more providers.
455
444
  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, …).
445
+ 5. **Frontend deploy path** — self-hosted SSH (static files + nginx).
446
+ 6. **Backend deploy** — SSH, Docker, EC2, etc.
458
447
 
459
- DeployHub runs **both** builds, packs them into one artifact, uploads once, then deploys frontend and backend to their respective targets.
448
+ DeployHub runs **both** builds, packs them into one artifact, uploads once, then deploys frontend and backend to their respective server targets.
460
449
 
461
450
  ### Example: React + Express monorepo
462
451
 
@@ -467,11 +456,11 @@ deployhub init
467
456
  # Backend: Express, npm start, port 3000
468
457
  # Storage: AWS S3 + Local
469
458
  # Configure deployment? Yes
470
- # Frontend: Managed platform Vercel
459
+ # Frontend: ssh/var/www/my-app/public
471
460
  # Backend: ssh → api.example.com, path /var/www/my-app/api, PM2 name my-app-api
472
461
  ```
473
462
 
474
- **Secrets:** AWS + `VERCEL_*` + `SSH_*`
463
+ **Secrets:** AWS + `SSH_*`
475
464
 
476
465
  **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
466
 
@@ -489,7 +478,7 @@ DeployHub generates `nginx.conf` to serve static files and proxy API requests.
489
478
 
490
479
  ### Example: Next.js API routes only
491
480
 
492
- Use **Frontend only** with **Next.js** and deploy to **Vercel** or **Firebase App Hosting** — no separate backend entry needed.
481
+ Use **Frontend only** with **Next.js** and deploy via **SSH** or your platform's native git-push workflow — no separate backend entry needed.
493
482
 
494
483
  ---
495
484
 
@@ -585,9 +574,9 @@ You can enable **multiple providers** — DeployHub uploads to all of them in pa
585
574
 
586
575
  | Project type | Frontend deploy options | Backend deploy options |
587
576
  |--------------|-------------------------|------------------------|
588
- | Frontend only | Platform **or** SSH/Docker/EC2/K8s | — |
577
+ | Frontend only | SSH/Docker/EC2/Azure VM/GCP VM/K8s | — |
589
578
  | Backend only | — | SSH/Docker/EC2/Azure VM/GCP VM/K8s |
590
- | Full stack | Platform **or** SSH (static + nginx) | SSH/Docker/EC2/K8s (always) |
579
+ | Full stack | SSH (static + nginx) | SSH/Docker/EC2/K8s (always) |
591
580
 
592
581
  | Mode | Storage | Deploy | When to use |
593
582
  |------|---------|--------|-------------|
@@ -654,7 +643,6 @@ Prefer `deployhub init` over hand-writing config — it sets adapters, workflow,
654
643
  | SSH deploy fails | Verify `SSH_KEY` is the **private** key; user can write to deploy path |
655
644
  | Wrong output uploaded | Fix `buildOutput` in config (`dist` vs `build` vs `.next`) |
656
645
  | 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
646
  | Monorepo subfolders | Edit `buildCommand` paths in `deployhub.config.json` after init |
659
647
 
660
648
  Run `deployhub doctor` after any config change.
@@ -715,18 +703,6 @@ Add these secrets in your repository (Settings → Secrets and variables → Act
715
703
  | `SSH_APP_NAME` | PM2 process name for backends |
716
704
  | `SSH_PORT` | App port on server (optional) |
717
705
 
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 |
729
-
730
706
  ## `deployhub doctor` Output
731
707
 
732
708
  The doctor command runs independent checks and always completes without crashing:
@@ -773,6 +749,25 @@ artifact/
773
749
  README.md
774
750
  ```
775
751
 
752
+ `deployment.json` records server deployment metadata per environment:
753
+
754
+ ```json
755
+ {
756
+ "targets": ["production"],
757
+ "deployedAt": "2026-07-01T12:00:00.000Z",
758
+ "deployments": [
759
+ {
760
+ "environmentName": "production",
761
+ "serverAddress": "203.0.113.10",
762
+ "processId": "my-api",
763
+ "timestamp": "2026-07-01T12:00:00.000Z"
764
+ }
765
+ ]
766
+ }
767
+ ```
768
+
769
+ Rollback redeploys a previous artifact to the configured server targets using this metadata.
770
+
776
771
  ## Configuration
777
772
 
778
773
  `deployhub.config.json` is generated by `init`. Credentials are **never** stored in this file — only in `.env` or GitHub Secrets.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akash-chowdhury-24/deployhub",
3
- "version": "1.0.15",
3
+ "version": "2.0.0",
4
4
  "description": "Zero-configuration deployment and artifact manager",
5
5
  "type": "module",
6
6
  "main": "./src/cli/index.js",
@@ -7,9 +7,6 @@ import { loadConfig, loadEnv } from '../core/config.js';
7
7
  import { testProvider } from '../storage/index.js';
8
8
  import { getDeploymentProvider } from '../deployment/index.js';
9
9
  import { PROVIDER_ENV_MAP } from '../utils/github-actions.js';
10
- import { PLATFORM_ENV_MAP, PLATFORM_CLI_MAP } from '../utils/platform-env.js';
11
- import { createPlatformProvider } from '../deployment/providers/platforms/index.js';
12
- import { isCliInstalled } from '../deployment/providers/platforms/_shared.js';
13
10
  import { printDoctorFooter } from '../utils/author.js';
14
11
  import { createLocalProvider } from '../storage/providers/local.js';
15
12
 
@@ -163,132 +160,6 @@ async function runBackendProcessChecks(config, envName) {
163
160
  return checks;
164
161
  }
165
162
 
166
- /**
167
- * @param {import('../core/config.js').DeployHubConfig} config
168
- * @param {string} envName
169
- * @param {string} [cwd]
170
- * @returns {Promise<CheckResult[]>}
171
- */
172
- async function runPlatformChecks(config, envName, cwd = process.cwd()) {
173
- const env = config.environments[envName];
174
- const platform = env?.platform;
175
- if (!platform) return [];
176
-
177
- const cli = PLATFORM_CLI_MAP[platform];
178
- const envKeys = PLATFORM_ENV_MAP[platform] || [];
179
- /** @type {CheckResult[]} */
180
- const checks = [];
181
-
182
- if (cli?.binary) {
183
- const cliLabel = cli.binary.charAt(0).toUpperCase() + cli.binary.slice(1);
184
- checks.push(
185
- await runCheck(`${cliLabel} CLI`, async () => {
186
- const installed = await isCliInstalled(cli.binary);
187
- if (installed) {
188
- return { name: `${cliLabel} CLI`, pass: true, message: `${cli.binary} CLI installed` };
189
- }
190
- return {
191
- name: `${cliLabel} CLI`,
192
- pass: false,
193
- message: `not found — run: ${cli.globalInstall || `npm install -g ${cli.install}`}`,
194
- };
195
- })
196
- );
197
- }
198
-
199
- for (const key of envKeys) {
200
- const isToken = key.includes('TOKEN') || key.includes('KEY');
201
- checks.push(
202
- await runCheck(key, async () => {
203
- if (!process.env[key]) {
204
- return { name: key, pass: false, message: 'not set in .env' };
205
- }
206
- if (isToken && key === envKeys[0]) {
207
- try {
208
- const provider = createPlatformProvider(platform, config, envName);
209
- if (provider.testConnection) {
210
- await provider.testConnection();
211
- return { name: key, pass: true, message: 'token valid' };
212
- }
213
- } catch (err) {
214
- return {
215
- name: key,
216
- pass: false,
217
- message: err instanceof Error ? err.message : 'token invalid',
218
- };
219
- }
220
- }
221
- return { name: key, pass: true, message: 'present' };
222
- })
223
- );
224
- }
225
-
226
- if (platform === 'vercel') {
227
- checks.push(
228
- await runCheck('Vercel project link', async () => {
229
- const vercelJson = path.join(cwd, '.vercel', 'project.json');
230
- if (await fs.pathExists(vercelJson)) {
231
- return { name: 'Vercel project link', pass: true, message: '.vercel/project.json found' };
232
- }
233
- return {
234
- name: 'Vercel project link',
235
- pass: false,
236
- message: 'not found — run: vercel link',
237
- };
238
- })
239
- );
240
- }
241
-
242
- if (platform === 'firebase-hosting') {
243
- checks.push(
244
- await runCheck('firebase.json', async () => {
245
- if (await fs.pathExists(path.join(cwd, 'firebase.json'))) {
246
- return { name: 'firebase.json', pass: true, message: 'found' };
247
- }
248
- return {
249
- name: 'firebase.json',
250
- pass: false,
251
- message: 'missing — run deployhub init or create manually',
252
- };
253
- })
254
- );
255
- }
256
-
257
- try {
258
- const provider = createPlatformProvider(platform, config, envName);
259
- if (provider.testConnection && envKeys.every((k) => process.env[k])) {
260
- checks.push(
261
- await runCheck(`${platform} connection`, async () => {
262
- await provider.testConnection();
263
- if (platform === 'netlify') {
264
- return { name: 'NETLIFY_SITE_ID', pass: true, message: 'site found' };
265
- }
266
- if (platform === 'cloudflare-pages') {
267
- return { name: 'CF project', pass: true, message: 'project exists' };
268
- }
269
- if (platform === 'aws-amplify') {
270
- return { name: 'AMPLIFY_APP_ID', pass: true, message: 'app found in AWS' };
271
- }
272
- if (platform.startsWith('firebase')) {
273
- return { name: 'Firebase project', pass: true, message: 'project ID valid' };
274
- }
275
- return { name: `${platform} connection`, pass: true, message: 'connected' };
276
- })
277
- );
278
- }
279
- } catch (err) {
280
- if (envKeys.every((k) => process.env[k])) {
281
- checks.push({
282
- name: `${platform} connection`,
283
- pass: false,
284
- message: err instanceof Error ? err.message : String(err),
285
- });
286
- }
287
- }
288
-
289
- return checks;
290
- }
291
-
292
163
  /**
293
164
  * @param {import('commander').Command} program
294
165
  */
@@ -467,11 +338,6 @@ export function registerDoctorCommand(program) {
467
338
  const env = config.environments[envName];
468
339
  if (!env) continue;
469
340
 
470
- if (env.deploymentType === 'platform' || env.frontendDeploymentType === 'platform') {
471
- const platformChecks = await runPlatformChecks(config, envName, cwd);
472
- results.push(...platformChecks);
473
- }
474
-
475
341
  if (env.type && ['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(env.type)) {
476
342
  results.push(
477
343
  await runCheck('SSH target', async () => {
@@ -541,13 +407,7 @@ export function registerDoctorCommand(program) {
541
407
  const env = config.environments[envName];
542
408
  if (!env) continue;
543
409
 
544
- if (env.deploymentType === 'platform' || env.frontendDeploymentType === 'platform') {
545
- const platform = env.platform;
546
- if (platform) {
547
- const keys = PLATFORM_ENV_MAP[platform] || [];
548
- required.push(...keys);
549
- }
550
- } else if (env.type) {
410
+ if (env.type) {
551
411
  const keys = PROVIDER_ENV_MAP[env.type] || [];
552
412
  required.push(...keys);
553
413
  }
@@ -18,15 +18,7 @@ import {
18
18
  addDeployhubToPackageJson,
19
19
  DEFAULT_NPM_CLI_SOURCE,
20
20
  } from '../utils/github-actions.js';
21
- import {
22
- promptPlatformQuestions,
23
- appendPlatformEnvExample,
24
- showPlatformComparison,
25
- suggestHealthUrl,
26
- PLATFORM_CHOICES,
27
- } from '../utils/init-platform.js';
28
21
  import { printAuthorFooter } from '../utils/author.js';
29
- import { ensureFirebaseJson } from '../utils/firebase-config-generator.js';
30
22
  import { generateNginxConfig } from '../utils/nginx.js';
31
23
 
32
24
  const FRONTEND_CHOICES = [
@@ -297,24 +289,10 @@ function buildServerEnvEntry(
297
289
  async function generateProjectScaffold(config, environments, cwd) {
298
290
  const envList = Object.values(environments);
299
291
 
300
- const usesFirebase = envList.some(
301
- (env) =>
302
- env.platform === 'firebase-hosting' || env.platform === 'firebase-app-hosting'
292
+ const usesFrontendSsh = envList.some(
293
+ (env) => env.type === 'ssh' || env.deploymentType === 'server'
303
294
  );
304
295
 
305
- if (usesFirebase && !(await fs.pathExists(path.join(cwd, 'firebase.json')))) {
306
- const buildOutput =
307
- config.frontend?.buildOutput || config.buildOutput || 'dist';
308
- await ensureFirebaseJson(buildOutput, cwd);
309
- console.log(chalk.gray(' • firebase.json (auto-generated)'));
310
- }
311
-
312
- const usesFrontendSsh = envList.some((env) => {
313
- if (env.frontendDeploymentType === 'platform') return false;
314
- if (env.deploymentType === 'platform' && !env.type) return false;
315
- return env.type === 'ssh' || env.deploymentType === 'server';
316
- });
317
-
318
296
  const isFrontendProject =
319
297
  config.projectType === 'frontend' || config.projectType === 'both';
320
298
 
@@ -455,9 +433,6 @@ export function registerInitCommand(program) {
455
433
  let healthUrl = '';
456
434
 
457
435
  if (answers.configureDeploy) {
458
- const buildOutput =
459
- frontendConfig?.buildOutput || singleConfig?.buildOutput || 'dist';
460
-
461
436
  if (projectType === 'backend') {
462
437
  const deployAnswers = await promptServerDeployment(
463
438
  projectName,
@@ -479,103 +454,24 @@ export function registerInitCommand(program) {
479
454
  healthUrl = `http://localhost:${singleConfig.port}/health`;
480
455
  }
481
456
  } else if (projectType === 'frontend') {
482
- const { deployMethod } = await inquirer.prompt([
483
- {
484
- type: 'list',
485
- name: 'deployMethod',
486
- message: 'How do you want to deploy?',
487
- choices: [
488
- {
489
- name: 'Managed platform (Vercel, Netlify, Cloudflare Pages, etc.)',
490
- value: 'platform',
491
- },
492
- {
493
- name: 'Self-hosted server (SSH, Docker, EC2, etc.)',
494
- value: 'server',
495
- },
496
- ],
497
- },
498
- ]);
499
-
500
- const { envName } = await inquirer.prompt([
501
- {
502
- type: 'input',
503
- name: 'envName',
504
- message: 'Environment name:',
505
- default: 'production',
506
- },
507
- ]);
508
- deploy.push(envName);
509
-
510
- if (deployMethod === 'platform') {
511
- showPlatformComparison();
512
- const { platform } = await inquirer.prompt([
513
- {
514
- type: 'list',
515
- name: 'platform',
516
- message: 'Select platform:',
517
- choices: PLATFORM_CHOICES,
518
- },
519
- ]);
520
-
521
- const platformConfig = await promptPlatformQuestions(
522
- platform,
523
- projectName,
524
- buildOutput,
525
- cwd
526
- );
527
- await appendPlatformEnvExample(platform, cwd);
528
-
529
- const { healthUrlInput } = await inquirer.prompt([
530
- {
531
- type: 'input',
532
- name: 'healthUrlInput',
533
- message: 'Health check URL (optional):',
534
- default: suggestHealthUrl(platform, projectName),
535
- },
536
- ]);
537
-
538
- environments[envName] = {
539
- deploymentType: 'platform',
540
- ...platformConfig,
541
- };
542
- healthUrl = healthUrlInput || suggestHealthUrl(platform, projectName);
543
- } else {
544
- const deployAnswers = await promptServerDeployment(
545
- projectName,
546
- projectType,
547
- backendConfig,
548
- singleConfig
549
- );
550
- deployAnswers.envName = envName;
551
- environments[envName] = buildServerEnvEntry(
552
- deployAnswers,
553
- projectType,
554
- projectName,
555
- backendConfig,
556
- singleConfig
557
- );
558
- if (deployAnswers.healthUrl) healthUrl = deployAnswers.healthUrl;
457
+ const deployAnswers = await promptServerDeployment(
458
+ projectName,
459
+ projectType,
460
+ backendConfig,
461
+ singleConfig
462
+ );
463
+ deploy.push(deployAnswers.envName);
464
+ environments[deployAnswers.envName] = buildServerEnvEntry(
465
+ deployAnswers,
466
+ projectType,
467
+ projectName,
468
+ backendConfig,
469
+ singleConfig
470
+ );
471
+ if (deployAnswers.healthUrl) {
472
+ healthUrl = deployAnswers.healthUrl;
559
473
  }
560
474
  } else {
561
- const { frontendDeployMethod } = await inquirer.prompt([
562
- {
563
- type: 'list',
564
- name: 'frontendDeployMethod',
565
- message: 'How do you want to deploy the frontend?',
566
- choices: [
567
- {
568
- name: 'Managed platform (Vercel, Netlify, Cloudflare Pages, etc.)',
569
- value: 'platform',
570
- },
571
- {
572
- name: 'Self-hosted server (SSH, Docker, EC2, etc.)',
573
- value: 'server',
574
- },
575
- ],
576
- },
577
- ]);
578
-
579
475
  const { envName } = await inquirer.prompt([
580
476
  {
581
477
  type: 'input',
@@ -587,36 +483,11 @@ export function registerInitCommand(program) {
587
483
  deploy.push(envName);
588
484
 
589
485
  /** @type {Record<string, unknown>} */
590
- const envEntry = { backendDeploymentType: 'server' };
591
-
592
- if (frontendDeployMethod === 'platform') {
593
- showPlatformComparison();
594
- const { platform } = await inquirer.prompt([
595
- {
596
- type: 'list',
597
- name: 'platform',
598
- message: 'Select frontend platform:',
599
- choices: PLATFORM_CHOICES,
600
- },
601
- ]);
602
-
603
- const platformConfig = await promptPlatformQuestions(
604
- platform,
605
- projectName,
606
- buildOutput,
607
- cwd
608
- );
609
- await appendPlatformEnvExample(platform, cwd);
610
-
611
- Object.assign(envEntry, {
612
- frontendDeploymentType: 'platform',
613
- deploymentType: 'platform',
614
- ...platformConfig,
615
- });
616
- healthUrl = suggestHealthUrl(platform, projectName);
617
- } else {
618
- envEntry.frontendDeploymentType = 'server';
619
- }
486
+ const envEntry = {
487
+ backendDeploymentType: 'server',
488
+ frontendDeploymentType: 'server',
489
+ deploymentType: 'server',
490
+ };
620
491
 
621
492
  console.log(chalk.gray('\nBackend will be deployed to a self-hosted server.'));
622
493
  const backendDeployAnswers = await inquirer.prompt([
@@ -664,8 +535,7 @@ export function registerInitCommand(program) {
664
535
  path: backendDeployAnswers.backendDeployPath || `/var/www/${projectName}/api`,
665
536
  });
666
537
 
667
- if (frontendDeployMethod === 'server') {
668
- const { frontendDeployPath } = await inquirer.prompt([
538
+ const { frontendDeployPath } = await inquirer.prompt([
669
539
  {
670
540
  type: 'input',
671
541
  name: 'frontendDeployPath',
@@ -673,8 +543,7 @@ export function registerInitCommand(program) {
673
543
  default: `/var/www/${projectName}/public`,
674
544
  },
675
545
  ]);
676
- envEntry.frontendDeployPath = frontendDeployPath;
677
- }
546
+ envEntry.frontendDeployPath = frontendDeployPath;
678
547
 
679
548
  const { healthUrlInput } = await inquirer.prompt([
680
549
  {
@@ -14,19 +14,8 @@ const SideConfigSchema = z.object({
14
14
 
15
15
  const EnvironmentSchema = z.object({
16
16
  type: z.string().optional(),
17
- deploymentType: z.enum(['platform', 'server']).optional(),
18
- platform: z.string().optional(),
19
- projectName: z.string().optional(),
20
- siteId: z.string().optional(),
21
- accountId: z.string().optional(),
22
- appId: z.string().optional(),
23
- region: z.string().optional(),
24
- branch: z.string().optional(),
25
- githubConnected: z.boolean().optional(),
26
- resourceName: z.string().optional(),
27
- projectId: z.string().optional(),
28
- backendName: z.string().optional(),
29
- frontendDeploymentType: z.enum(['platform', 'server']).optional(),
17
+ deploymentType: z.enum(['server']).optional(),
18
+ frontendDeploymentType: z.enum(['server']).optional(),
30
19
  backendDeploymentType: z.enum(['server']).optional(),
31
20
  host: z.string().optional(),
32
21
  user: z.string().optional(),
@@ -5,8 +5,6 @@ import { uploadToAll } from '../storage/index.js';
5
5
  import { deployToAll } from '../deployment/index.js';
6
6
  import { sendNotifications } from '../notifications/index.js';
7
7
  import axios from 'axios';
8
- import fs from 'fs-extra';
9
- import path from 'path';
10
8
  import { getProjectVersion } from '../utils/version.js';
11
9
 
12
10
  /**
@@ -149,15 +147,6 @@ export function buildPipelineStages(config, cwd, state) {
149
147
  const deployed = await deployToAll(ctx.config, artifactDir);
150
148
  ctx.state.deployedTargets = deployed;
151
149
 
152
- const deploymentPath = path.join(artifactDir, 'deployment.json');
153
- if (await fs.pathExists(deploymentPath)) {
154
- const data = await fs.readJson(deploymentPath);
155
- const last = data.lastDeployment;
156
- if (last?.deployUrl || last?.deploymentUrl) {
157
- ctx.state.lastDeployUrl = last.deployUrl || last.deploymentUrl;
158
- }
159
- }
160
-
161
150
  await repackArtifactZip(artifactDir);
162
151
  const zipPath = /** @type {string} */ (ctx.state.zipPath);
163
152
  if (zipPath) {