@akash-chowdhury-24/deployhub 2.0.26 → 2.0.28

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.
@@ -207,6 +207,9 @@ export function isInterpretedBackendFramework(framework) {
207
207
  'flask',
208
208
  'laravel',
209
209
  'symfony',
210
+ 'php',
211
+ 'python',
212
+ 'ruby',
210
213
  'rails',
211
214
  ].includes(framework || '');
212
215
  }
@@ -224,21 +227,21 @@ export function describeInterpretedBackendGap(framework) {
224
227
  installCmd: 'npm ci',
225
228
  };
226
229
  }
227
- if (['fastapi', 'django', 'flask'].includes(framework)) {
230
+ if (['fastapi', 'django', 'flask', 'python'].includes(framework)) {
228
231
  return {
229
232
  ecosystem: 'Python',
230
233
  missing: 'a pre-installed virtualenv / site-packages (artifacts only ship requirements.txt + source)',
231
234
  installCmd: 'pip install',
232
235
  };
233
236
  }
234
- if (['laravel', 'symfony'].includes(framework)) {
237
+ if (['laravel', 'symfony', 'php'].includes(framework)) {
235
238
  return {
236
239
  ecosystem: 'PHP',
237
240
  missing: 'vendor/ (Composer dependencies are not packaged into the artifact)',
238
241
  installCmd: 'composer install',
239
242
  };
240
243
  }
241
- if (framework === 'rails') {
244
+ if (framework === 'rails' || framework === 'ruby') {
242
245
  return {
243
246
  ecosystem: 'Ruby',
244
247
  missing: 'vendor/bundle / installed gems (artifacts only ship Gemfile + lock)',
@@ -71,6 +71,7 @@ export function resolveFallbackContainerPort(config) {
71
71
  if (framework === 'go') return 8080;
72
72
  if (framework === 'dotnet') return 5000;
73
73
  if (framework === 'rails') return 3000;
74
+ if (framework === 'ruby') return 9292;
74
75
 
75
76
  // nextjs, nestjs, express, and other Node backends
76
77
  return 3000;
@@ -24,26 +24,69 @@ export function resolveDockerSettings(config) {
24
24
  const projectType = config.projectType || 'frontend';
25
25
 
26
26
  if (projectType === 'both' && config.backend) {
27
+ const framework = config.backend.framework || 'express';
27
28
  return {
28
- framework: config.backend.framework || 'express',
29
+ framework,
29
30
  buildCommand: config.backend.buildCommand ?? null,
30
- buildOutput: config.backend.buildOutput || '.',
31
- startCommand: config.backend.startCommand || 'npm start',
32
- port: config.backend.port || 3000,
31
+ buildOutput: config.backend.buildOutput || defaultBuildOutput(framework, projectType),
32
+ // null lets each generator supply a language-correct CMD (do not force npm start).
33
+ startCommand: config.backend.startCommand ?? null,
34
+ port: config.backend.port || defaultPort(framework, projectType),
33
35
  projectType,
34
36
  };
35
37
  }
36
38
 
39
+ const framework =
40
+ config.framework || (projectType === 'frontend' ? 'react' : 'express');
37
41
  return {
38
- framework: config.framework || 'express',
42
+ framework,
39
43
  buildCommand: config.buildCommand ?? null,
40
- buildOutput: config.buildOutput || 'dist',
41
- startCommand: config.startCommand || 'npm start',
42
- port: config.port || 3000,
44
+ buildOutput: config.buildOutput || defaultBuildOutput(framework, projectType),
45
+ startCommand: config.startCommand ?? null,
46
+ port: config.port || defaultPort(framework, projectType),
43
47
  projectType,
44
48
  };
45
49
  }
46
50
 
51
+ /**
52
+ * @param {string} framework
53
+ * @param {string} projectType
54
+ */
55
+ function defaultBuildOutput(framework, projectType) {
56
+ if (
57
+ projectType === 'frontend' ||
58
+ ['react', 'vue', 'angular', 'svelte', 'astro', 'vanilla'].includes(framework)
59
+ ) {
60
+ return 'dist';
61
+ }
62
+ if (framework === 'nestjs') return 'dist';
63
+ if (framework === 'dotnet') return 'publish';
64
+ if (framework === 'spring' || framework === 'java') return 'target';
65
+ if (framework === 'go') return 'bin';
66
+ return '.';
67
+ }
68
+
69
+ /**
70
+ * @param {string} framework
71
+ * @param {string} projectType
72
+ */
73
+ function defaultPort(framework, projectType) {
74
+ if (
75
+ projectType === 'frontend' ||
76
+ ['react', 'vue', 'angular', 'svelte', 'astro', 'vanilla'].includes(framework)
77
+ ) {
78
+ return 80;
79
+ }
80
+ if (['laravel', 'symfony', 'php'].includes(framework)) return 80;
81
+ if (['fastapi', 'django', 'python'].includes(framework)) return 8000;
82
+ if (framework === 'flask') return 5000;
83
+ if (['spring', 'java', 'go'].includes(framework)) return 8080;
84
+ if (framework === 'dotnet') return 5000;
85
+ if (framework === 'rails') return 3000;
86
+ if (framework === 'ruby') return 9292;
87
+ return 3000;
88
+ }
89
+
47
90
  /**
48
91
  * Generate a .dockerignore matching the Dockerfile language/framework.
49
92
  * @param {import('../core/config.js').DeployHubConfig} config
@@ -82,9 +125,10 @@ export function generateDockerignore(config) {
82
125
  'express',
83
126
  'fastify',
84
127
  'koa',
128
+ 'node',
85
129
  ];
86
- const pythonFrameworks = ['fastapi', 'django', 'flask'];
87
- const phpFrameworks = ['laravel', 'symfony'];
130
+ const pythonFrameworks = ['fastapi', 'django', 'flask', 'python'];
131
+ const phpFrameworks = ['laravel', 'symfony', 'php'];
88
132
 
89
133
  if (nodeFrameworks.includes(framework) || !framework) {
90
134
  lines.push('node_modules', 'npm-debug.log*', 'yarn-error.log*', '.npm', '');
@@ -119,7 +163,7 @@ export function generateDockerignore(config) {
119
163
  lines.push('vendor', '');
120
164
  }
121
165
 
122
- if (framework === 'spring') {
166
+ if (framework === 'spring' || framework === 'java') {
123
167
  lines.push('target', '.gradle', 'build', '');
124
168
  }
125
169
 
@@ -131,7 +175,7 @@ export function generateDockerignore(config) {
131
175
  lines.push('bin', 'obj', '');
132
176
  }
133
177
 
134
- if (framework === 'rails') {
178
+ if (framework === 'rails' || framework === 'ruby') {
135
179
  lines.push('tmp', 'log', 'vendor/bundle', '');
136
180
  }
137
181
 
@@ -159,6 +203,7 @@ export function generateDockerfile(config) {
159
203
  case 'express':
160
204
  case 'fastify':
161
205
  case 'koa':
206
+ case 'node':
162
207
  return generateNodeBackendDockerfile(settings);
163
208
  case 'fastapi':
164
209
  return generateFastapiDockerfile(settings);
@@ -166,11 +211,16 @@ export function generateDockerfile(config) {
166
211
  return generateDjangoDockerfile(settings);
167
212
  case 'flask':
168
213
  return generateFlaskDockerfile(settings);
214
+ case 'python':
215
+ return generatePythonDockerfile(settings);
169
216
  case 'laravel':
170
217
  return generateLaravelDockerfile(settings);
171
218
  case 'symfony':
172
219
  return generateSymfonyDockerfile(settings);
220
+ case 'php':
221
+ return generatePhpDockerfile(settings);
173
222
  case 'spring':
223
+ case 'java':
174
224
  return generateSpringDockerfile(settings);
175
225
  case 'go':
176
226
  return generateGoDockerfile(settings);
@@ -178,6 +228,8 @@ export function generateDockerfile(config) {
178
228
  return generateDotnetDockerfile(settings);
179
229
  case 'rails':
180
230
  return generateRailsDockerfile(settings);
231
+ case 'ruby':
232
+ return generateRubyDockerfile(settings);
181
233
  default:
182
234
  return generateNodeBackendDockerfile(settings);
183
235
  }
@@ -383,25 +435,59 @@ CMD ${JSON.stringify(startParts)}
383
435
  `;
384
436
  }
385
437
 
438
+ /**
439
+ * Plain Python (no FastAPI/Django/Flask): optional requirements + stdlib HTTP server.
440
+ * @param {{ startCommand: string|null, port: number }} settings
441
+ */
442
+ function generatePythonDockerfile(settings) {
443
+ const port = settings.port || 8000;
444
+ const startCmd =
445
+ settings.startCommand || `python -m http.server ${port} --bind 0.0.0.0`;
446
+ const startParts = parseCommand(startCmd);
447
+
448
+ return `${GENERATED_HEADER}
449
+ FROM python:3.11-slim
450
+ WORKDIR /app
451
+ COPY requirements.txt* ./
452
+ RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; fi
453
+ COPY . .
454
+ EXPOSE ${port}
455
+ CMD ${JSON.stringify(startParts)}
456
+ `;
457
+ }
458
+
386
459
  /**
387
460
  * @param {{ port: number }} settings
388
461
  */
389
462
  function generateLaravelDockerfile(settings) {
390
463
  const port = settings.port || 80;
464
+ // Single-process HTTP for container/K8s fitness. SSH deploys still use host
465
+ // php-fpm + nginx; this image must listen on EXPOSE (FPM alone listens on 9000).
466
+ const cmd = JSON.stringify([
467
+ 'php',
468
+ 'artisan',
469
+ 'serve',
470
+ '--host=0.0.0.0',
471
+ `--port=${port}`,
472
+ ]);
391
473
 
392
474
  return `${GENERATED_HEADER}
393
475
  FROM composer:2 AS vendor
394
476
  WORKDIR /app
395
- COPY composer.json composer.lock ./
396
- RUN composer install --no-dev --optimize-autoloader --no-interaction
477
+ COPY composer.json composer.lock* ./
478
+ RUN composer install --no-dev --optimize-autoloader --no-interaction --ignore-platform-reqs
397
479
 
398
- FROM php:8.2-fpm-alpine
480
+ FROM php:8.2-cli-alpine
399
481
  WORKDIR /var/www/html
482
+ RUN apk add --no-cache icu-libs libzip \\
483
+ && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS icu-dev libzip-dev oniguruma-dev \\
484
+ && docker-php-ext-install -j$(nproc) pdo_mysql mbstring zip opcache \\
485
+ && apk del .build-deps
400
486
  COPY --from=vendor /app/vendor ./vendor
401
487
  COPY . .
402
488
  RUN chown -R www-data:www-data storage bootstrap/cache || true
403
489
  EXPOSE ${port}
404
- CMD ["php-fpm"]
490
+ CMD ${cmd}
405
491
  `;
406
492
  }
407
493
 
@@ -410,19 +496,54 @@ CMD ["php-fpm"]
410
496
  */
411
497
  function generateSymfonyDockerfile(settings) {
412
498
  const port = settings.port || 80;
499
+ // Built-in server binds HTTP to EXPOSE — suitable for a single-container pod.
500
+ // Prefer a production reverse-proxy image if you outgrow this starter template.
501
+ const cmd = JSON.stringify(['php', '-S', `0.0.0.0:${port}`, '-t', 'public']);
413
502
 
414
503
  return `${GENERATED_HEADER}
415
504
  FROM composer:2 AS vendor
416
505
  WORKDIR /app
417
- COPY composer.json composer.lock ./
418
- RUN composer install --no-dev --optimize-autoloader --no-interaction
506
+ COPY composer.json composer.lock* ./
507
+ RUN composer install --no-dev --optimize-autoloader --no-interaction --ignore-platform-reqs
419
508
 
420
- FROM php:8.2-fpm-alpine
509
+ FROM php:8.2-cli-alpine
421
510
  WORKDIR /var/www/html
511
+ RUN apk add --no-cache icu-libs libzip \\
512
+ && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS icu-dev libzip-dev oniguruma-dev \\
513
+ && docker-php-ext-install -j$(nproc) pdo_mysql mbstring zip opcache \\
514
+ && apk del .build-deps
422
515
  COPY --from=vendor /app/vendor ./vendor
423
516
  COPY . .
424
517
  EXPOSE ${port}
425
- CMD ["php-fpm"]
518
+ CMD ${cmd}
519
+ `;
520
+ }
521
+
522
+ /**
523
+ * Plain PHP (no Laravel/Symfony): single-process built-in server.
524
+ * Docroot prefers public/ at runtime when present; optional composer install.
525
+ * @param {{ port: number }} settings
526
+ */
527
+ function generatePhpDockerfile(settings) {
528
+ const port = settings.port || 80;
529
+ const cmd = JSON.stringify([
530
+ 'sh',
531
+ '-c',
532
+ `d=.; [ -d public ] && d=public; exec php -S 0.0.0.0:${port} -t "$d"`,
533
+ ]);
534
+
535
+ return `${GENERATED_HEADER}
536
+ FROM php:8.2-cli-alpine
537
+ WORKDIR /var/www/html
538
+ COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
539
+ RUN apk add --no-cache icu-libs libzip \\
540
+ && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS icu-dev libzip-dev oniguruma-dev \\
541
+ && docker-php-ext-install -j$(nproc) pdo_mysql mbstring zip opcache \\
542
+ && apk del .build-deps
543
+ COPY . .
544
+ RUN if [ -f composer.json ]; then composer install --no-dev --optimize-autoloader --no-interaction --ignore-platform-reqs; fi
545
+ EXPOSE ${port}
546
+ CMD ${cmd}
426
547
  `;
427
548
  }
428
549
 
@@ -438,11 +559,13 @@ FROM eclipse-temurin:17-jdk-alpine AS build
438
559
  WORKDIR /app
439
560
  COPY pom.xml ./
440
561
  COPY src ./src
441
- RUN apk add --no-cache maven && ${buildCmd}
562
+ RUN apk add --no-cache maven && ${buildCmd} \\
563
+ && JAR=$(ls target/*.jar 2>/dev/null | grep -v '\\-plain\\.jar$' | head -n1) \\
564
+ && test -n "$JAR" && cp "$JAR" /app/app.jar
442
565
 
443
566
  FROM eclipse-temurin:17-jre-alpine
444
567
  WORKDIR /app
445
- COPY --from=build /app/target/*.jar app.jar
568
+ COPY --from=build /app/app.jar app.jar
446
569
  EXPOSE ${port}
447
570
  CMD ["java", "-jar", "app.jar"]
448
571
  `;
@@ -458,10 +581,10 @@ function generateGoDockerfile(settings) {
458
581
  return `${GENERATED_HEADER}
459
582
  FROM golang:1.22-alpine AS build
460
583
  WORKDIR /app
461
- COPY go.mod go.sum ./
584
+ COPY go.mod go.sum* ./
462
585
  RUN go mod download
463
586
  COPY . .
464
- RUN ${buildCmd}
587
+ RUN mkdir -p /app/bin && ${buildCmd}
465
588
 
466
589
  FROM alpine:3.19
467
590
  WORKDIR /app
@@ -476,7 +599,8 @@ CMD ["./app"]
476
599
  * @param {{ buildCommand: string|null, buildOutput: string, startCommand: string|null, port: number }} settings
477
600
  */
478
601
  function generateDotnetDockerfile(settings) {
479
- const buildCmd = settings.buildCommand || 'dotnet publish -c Release -o /app/publish';
602
+ // Publish into a path under WORKDIR so the runtime stage COPY path matches.
603
+ const buildCmd = settings.buildCommand || 'dotnet publish -c Release -o publish';
480
604
  const port = settings.port || 5000;
481
605
  const output = settings.buildOutput || 'publish';
482
606
 
@@ -502,15 +626,17 @@ CMD ["dotnet", "App.dll"]
502
626
  */
503
627
  function generateRailsDockerfile(settings) {
504
628
  const port = settings.port || 3000;
505
- const startCmd = settings.startCommand || 'bundle exec puma -C config/puma.rb';
629
+ // Bind explicitly — puma.rb / SSH assumptions are not the container PID 1 contract.
630
+ const startCmd =
631
+ settings.startCommand || `bundle exec puma -b tcp://0.0.0.0:${port}`;
506
632
  const startParts = parseCommand(startCmd);
507
633
 
508
634
  return `${GENERATED_HEADER}
509
635
  FROM ruby:3.2-slim AS build
510
636
  WORKDIR /app
511
637
  RUN apt-get update -qq && apt-get install -y build-essential libpq-dev && rm -rf /var/lib/apt/lists/*
512
- COPY Gemfile Gemfile.lock ./
513
- RUN bundle install --without development test
638
+ COPY Gemfile Gemfile.lock* ./
639
+ RUN bundle config set --local without 'development test' && bundle install
514
640
  COPY . .
515
641
  RUN bundle exec rake assets:precompile || true
516
642
 
@@ -524,6 +650,28 @@ CMD ${JSON.stringify(startParts)}
524
650
  `;
525
651
  }
526
652
 
653
+ /**
654
+ * Plain Ruby / Rack (no Rails): Puma as foreground HTTP process.
655
+ * @param {{ startCommand: string|null, port: number }} settings
656
+ */
657
+ function generateRubyDockerfile(settings) {
658
+ const port = settings.port || 9292;
659
+ const startCmd =
660
+ settings.startCommand || `bundle exec puma -b tcp://0.0.0.0:${port}`;
661
+ const startParts = parseCommand(startCmd);
662
+
663
+ return `${GENERATED_HEADER}
664
+ FROM ruby:3.2-slim
665
+ WORKDIR /app
666
+ RUN apt-get update -qq && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/*
667
+ COPY Gemfile Gemfile.lock* ./
668
+ RUN bundle config set --local without 'development test' && bundle install
669
+ COPY . .
670
+ EXPOSE ${port}
671
+ CMD ${JSON.stringify(startParts)}
672
+ `;
673
+ }
674
+
527
675
  /**
528
676
  * @param {string} command
529
677
  * @returns {string[]}
@@ -567,6 +715,11 @@ export function getDockerfileFrameworkLabel(framework) {
567
715
  flask: 'Flask',
568
716
  laravel: 'Laravel',
569
717
  symfony: 'Symfony',
718
+ php: 'PHP',
719
+ python: 'Python',
720
+ node: 'Node.js',
721
+ java: 'Java',
722
+ ruby: 'Ruby',
570
723
  spring: 'Spring Boot',
571
724
  go: 'Go',
572
725
  dotnet: '.NET',