@akash-chowdhury-24/deployhub 2.0.27 → 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.
- package/README.md +5 -2
- package/package.json +1 -1
- package/src/core/config.js +7 -0
- package/src/utils/github-actions.js +128 -8
package/README.md
CHANGED
|
@@ -651,6 +651,7 @@ All JS frontends share the same install/build flow: `npm ci` → `npm run build`
|
|
|
651
651
|
|
|
652
652
|
- **Detect:** `composer.json` with `laravel/framework` or `symfony/framework-bundle`.
|
|
653
653
|
- **Install:** Composer (on CI and server).
|
|
654
|
+
- **CI runtime:** Generated workflows install PHP via `shivammathur/setup-php@v2` with Composer. Default version is **8.4** (meets current Laravel platform requirements). Override with `"phpVersion": "8.3"` at the config root or under `"backend"` in `deployhub.config.json`, then run `deployhub sync-workflows`.
|
|
654
655
|
- **Deploy:** SSH with PHP-FPM or `php artisan` for Laravel.
|
|
655
656
|
|
|
656
657
|
> ⚠️ **PHP-FPM deployments restart the FPM service for the ENTIRE host on every deploy.** If you run multiple DeployHub-managed environments on the same server, deploying ANY of them will briefly interrupt in-flight requests for ALL of them. For production use with multiple environments, either use separate hosts per environment, or set up per-environment PHP-FPM pools manually (not yet automated by DeployHub).
|
|
@@ -695,7 +696,9 @@ git commit -m "Add DeployHub CI"
|
|
|
695
696
|
2. Add every secret listed at the end of `deployhub init` (storage + deployment).
|
|
696
697
|
3. Push to `main` or `master` — the deploy workflow triggers on push.
|
|
697
698
|
|
|
698
|
-
The deploy workflow (`deployhub.yml`) 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.
|
|
699
|
+
The deploy workflow (`deployhub.yml`) installs the correct language runtime (Node, Python, PHP, Java, Go, .NET, Ruby) based on your `deployhub.config.json`, installs DeployHub, runs `deployhub build`, and uses your secrets. For PHP projects, CI uses `shivammathur/setup-php` (default **8.4**; override with `phpVersion` / `backend.phpVersion`).
|
|
700
|
+
|
|
701
|
+
When an environment uses Kubernetes, the workflow installs `kubectl` and writes kubeconfig from secrets — but only when that run actually needs cluster access (push with a push-triggered k8s env, or workflow_dispatch / rollback targeting a k8s env, `all`, or blank). Plain pushes that only auto-deploy non-k8s environments (e.g. EC2 development) skip those steps.
|
|
699
702
|
|
|
700
703
|
To run a deploy manually: **Actions → DeployHub → Run workflow**.
|
|
701
704
|
|
|
@@ -1002,7 +1005,7 @@ DeployHub detects whether the server uses Debian-style `sites-available` or RHEL
|
|
|
1002
1005
|
|
|
1003
1006
|
**What DeployHub automates:**
|
|
1004
1007
|
- Starter `k8s/deployment.yaml` and `k8s/service.yaml` when no manifests exist (skipped if you already have a `k8s/` directory or root-level Kubernetes YAML files)
|
|
1005
|
-
- GitHub Actions installs `kubectl` on the CI runner and writes kubeconfig from secrets
|
|
1008
|
+
- GitHub Actions installs `kubectl` on the CI runner and writes kubeconfig from secrets when the run targets a Kubernetes environment (skipped on push when k8s is manual-only)
|
|
1006
1009
|
- Lists `kubectl` contexts during `init` for easy selection
|
|
1007
1010
|
- Auto-detects `~/.kube/config`
|
|
1008
1011
|
- Complete `.env.example` for kubeconfig, context, namespace, and registry settings
|
package/package.json
CHANGED
package/src/core/config.js
CHANGED
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
const SideConfigSchema = z.object({
|
|
21
21
|
framework: z.string(),
|
|
22
22
|
language: z.string().optional(),
|
|
23
|
+
/** PHP runtime for CI (`shivammathur/setup-php`); e.g. `"8.4"`. */
|
|
24
|
+
phpVersion: z.string().optional(),
|
|
23
25
|
buildCommand: z.string().nullable().optional(),
|
|
24
26
|
startCommand: z.string().nullable().optional(),
|
|
25
27
|
buildOutput: z.string().optional(),
|
|
@@ -81,6 +83,11 @@ const ConfigSchema = z.object({
|
|
|
81
83
|
projectType: z.enum(['frontend', 'backend', 'both']).default('frontend'),
|
|
82
84
|
framework: z.string().optional(),
|
|
83
85
|
language: z.string().optional(),
|
|
86
|
+
/**
|
|
87
|
+
* PHP runtime for generated GitHub Actions (`shivammathur/setup-php`).
|
|
88
|
+
* Prefer `backend.phpVersion` for backend/both projects. Default when unset: `8.4`.
|
|
89
|
+
*/
|
|
90
|
+
phpVersion: z.string().optional(),
|
|
84
91
|
buildCommand: z.string().nullable().optional(),
|
|
85
92
|
startCommand: z.string().nullable().optional(),
|
|
86
93
|
buildOutput: z.string().optional(),
|
|
@@ -18,10 +18,14 @@ import {
|
|
|
18
18
|
} from '../deployment/deployment-env.js';
|
|
19
19
|
import {
|
|
20
20
|
getEnvMethod,
|
|
21
|
+
getEnvTrigger,
|
|
21
22
|
getEnabledEnvironmentNames,
|
|
22
23
|
isEnvEnabled,
|
|
23
24
|
} from '../core/environments.js';
|
|
24
25
|
|
|
26
|
+
/** Default PHP for CI when config does not set `phpVersion` / `backend.phpVersion`. */
|
|
27
|
+
export const DEFAULT_PHP_VERSION = '8.4';
|
|
28
|
+
|
|
25
29
|
/** @typedef {'aws'|'azure'|'gcp'|'gdrive'|'dropbox'|'local'|'ftp'|'ssh'} ProviderEnvKey */
|
|
26
30
|
|
|
27
31
|
/** Storage-only providers — never treat deployment method ids as storage. */
|
|
@@ -267,6 +271,40 @@ function getGithubGitConfigStep() {
|
|
|
267
271
|
fi`;
|
|
268
272
|
}
|
|
269
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Resolve PHP version for CI setup-php.
|
|
276
|
+
* Order: `backend.phpVersion` → top-level `phpVersion` → {@link DEFAULT_PHP_VERSION} (`8.4`).
|
|
277
|
+
* Set either config key in deployhub.config.json to pin a different runtime
|
|
278
|
+
* (e.g. `"phpVersion": "8.3"` or `"backend": { "phpVersion": "8.3" }`).
|
|
279
|
+
*
|
|
280
|
+
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
281
|
+
* @returns {string}
|
|
282
|
+
*/
|
|
283
|
+
export function resolvePhpVersion(config) {
|
|
284
|
+
const fromBackend = config?.backend?.phpVersion;
|
|
285
|
+
if (typeof fromBackend === 'string' && fromBackend.trim()) {
|
|
286
|
+
return fromBackend.trim();
|
|
287
|
+
}
|
|
288
|
+
const fromRoot = config?.phpVersion;
|
|
289
|
+
if (typeof fromRoot === 'string' && fromRoot.trim()) {
|
|
290
|
+
return fromRoot.trim();
|
|
291
|
+
}
|
|
292
|
+
return DEFAULT_PHP_VERSION;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
297
|
+
* @returns {boolean}
|
|
298
|
+
*/
|
|
299
|
+
function isPhpProject(config) {
|
|
300
|
+
if (!config) return false;
|
|
301
|
+
const projectType = config.projectType || 'frontend';
|
|
302
|
+
if (projectType !== 'backend' && projectType !== 'both') return false;
|
|
303
|
+
const framework = config.backend?.framework || config.framework || '';
|
|
304
|
+
const language = config.backend?.language || config.language || '';
|
|
305
|
+
return language === 'php' || ['laravel', 'symfony', 'php'].includes(framework);
|
|
306
|
+
}
|
|
307
|
+
|
|
270
308
|
/**
|
|
271
309
|
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
272
310
|
* @returns {string[]}
|
|
@@ -292,6 +330,14 @@ function getBackendSetupSteps(config) {
|
|
|
292
330
|
with:
|
|
293
331
|
python-version: '3.11'`);
|
|
294
332
|
}
|
|
333
|
+
if (isPhpProject(config)) {
|
|
334
|
+
const phpVersion = resolvePhpVersion(config);
|
|
335
|
+
steps.push(` - name: Setup PHP
|
|
336
|
+
uses: shivammathur/setup-php@v2
|
|
337
|
+
with:
|
|
338
|
+
php-version: '${phpVersion}'
|
|
339
|
+
tools: composer`);
|
|
340
|
+
}
|
|
295
341
|
if (['spring', 'java'].includes(framework)) {
|
|
296
342
|
steps.push(` - uses: actions/setup-java@v4
|
|
297
343
|
with:
|
|
@@ -362,18 +408,69 @@ function resolveKubeconfigWorkflowSecretName(deployEnvironments, environments, c
|
|
|
362
408
|
: 'KUBECONFIG';
|
|
363
409
|
}
|
|
364
410
|
|
|
411
|
+
/**
|
|
412
|
+
* GitHub Actions `if:` so kubectl/kubeconfig only run when this job needs k8s.
|
|
413
|
+
* - Push: only when at least one push-triggered env uses kubernetes (skip when push
|
|
414
|
+
* only deploys EC2/SSH/etc. and k8s is manual-only).
|
|
415
|
+
* - workflow_dispatch / rollback: when selected env is k8s, `all`, or blank
|
|
416
|
+
* (blank = deploy/rollback all enabled envs).
|
|
417
|
+
* Returns null when every enabled env is kubernetes (steps always needed).
|
|
418
|
+
*
|
|
419
|
+
* @param {string[]} deployEnvironments
|
|
420
|
+
* @param {Record<string, { type?: string, method?: string, trigger?: string }>} environments
|
|
421
|
+
* @param {'deploy'|'rollback'} [kind]
|
|
422
|
+
* @returns {string|null}
|
|
423
|
+
*/
|
|
424
|
+
function getKubernetesSetupIfExpression(
|
|
425
|
+
deployEnvironments,
|
|
426
|
+
environments,
|
|
427
|
+
kind = 'deploy'
|
|
428
|
+
) {
|
|
429
|
+
const k8sNames = deployEnvironments.filter(
|
|
430
|
+
(n) => getEnvMethod(environments[n]) === 'kubernetes'
|
|
431
|
+
);
|
|
432
|
+
if (k8sNames.length === 0) return null;
|
|
433
|
+
|
|
434
|
+
const nonK8s = deployEnvironments.filter(
|
|
435
|
+
(n) => getEnvMethod(environments[n]) !== 'kubernetes'
|
|
436
|
+
);
|
|
437
|
+
// All enabled envs are kubernetes — setup is always required.
|
|
438
|
+
if (nonK8s.length === 0) return null;
|
|
439
|
+
|
|
440
|
+
const dispatchNeedK8s = [
|
|
441
|
+
...k8sNames.map((n) => `inputs.environment == '${n}'`),
|
|
442
|
+
`inputs.environment == 'all'`,
|
|
443
|
+
`inputs.environment == ''`,
|
|
444
|
+
].join(' || ');
|
|
445
|
+
|
|
446
|
+
if (kind === 'rollback') {
|
|
447
|
+
return `github.event_name == 'workflow_dispatch' && (${dispatchNeedK8s})`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const pushK8s = k8sNames.filter(
|
|
451
|
+
(n) => getEnvTrigger(environments[n]) === 'push'
|
|
452
|
+
);
|
|
453
|
+
if (pushK8s.length > 0) {
|
|
454
|
+
return `github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && (${dispatchNeedK8s}))`;
|
|
455
|
+
}
|
|
456
|
+
// Manual-only kubernetes: never configure kubeconfig on plain push.
|
|
457
|
+
return `github.event_name == 'workflow_dispatch' && (${dispatchNeedK8s})`;
|
|
458
|
+
}
|
|
459
|
+
|
|
365
460
|
/**
|
|
366
461
|
* @param {string} [kubeconfigSecretName]
|
|
462
|
+
* @param {string|null} [ifExpression]
|
|
367
463
|
* @returns {string}
|
|
368
464
|
*/
|
|
369
|
-
function getKubernetesSetupSteps(kubeconfigSecretName = 'KUBECONFIG') {
|
|
465
|
+
function getKubernetesSetupSteps(kubeconfigSecretName = 'KUBECONFIG', ifExpression = null) {
|
|
370
466
|
// One kubeconfig file per job — see resolveKubeconfigWorkflowSecretName LIMITATION.
|
|
371
|
-
|
|
467
|
+
const ifLine = ifExpression ? `\n if: ${ifExpression}` : '';
|
|
468
|
+
return ` - name: Setup kubectl${ifLine}
|
|
372
469
|
uses: azure/setup-kubectl@v4
|
|
373
470
|
with:
|
|
374
471
|
version: '${KUBECTL_VERSION}'
|
|
375
472
|
|
|
376
|
-
- name: Configure kubeconfig
|
|
473
|
+
- name: Configure kubeconfig${ifLine}
|
|
377
474
|
env:
|
|
378
475
|
KUBECONFIG_SECRET: \${{ secrets.${kubeconfigSecretName} }}
|
|
379
476
|
run: |
|
|
@@ -580,8 +677,13 @@ export function generateWorkflowYaml(
|
|
|
580
677
|
environments,
|
|
581
678
|
config
|
|
582
679
|
);
|
|
680
|
+
const k8sIf = getKubernetesSetupIfExpression(
|
|
681
|
+
allDeployNames,
|
|
682
|
+
environments,
|
|
683
|
+
'deploy'
|
|
684
|
+
);
|
|
583
685
|
const kubernetesSteps = hasKubernetesDeploy(allDeployNames, environments)
|
|
584
|
-
? `${getKubernetesSetupSteps(kubeconfigSecret)}\n`
|
|
686
|
+
? `${getKubernetesSetupSteps(kubeconfigSecret, k8sIf)}\n`
|
|
585
687
|
: '';
|
|
586
688
|
|
|
587
689
|
const projectType = config?.projectType || 'frontend';
|
|
@@ -688,6 +790,13 @@ export function generateRollbackWorkflowYaml(
|
|
|
688
790
|
const envBlock = formatWorkflowEnvBlock(envVars);
|
|
689
791
|
|
|
690
792
|
const installSpec = getCliInstallSpec(cliSource);
|
|
793
|
+
// Rollback does not generally install project deps; PHP is the exception
|
|
794
|
+
// (composer install) so we only inject setup-php here — not other backends.
|
|
795
|
+
const phpSetupSteps = isPhpProject(config)
|
|
796
|
+
? [...new Set(getBackendSetupSteps(config))]
|
|
797
|
+
.filter((s) => s.includes('setup-php'))
|
|
798
|
+
.join('\n')
|
|
799
|
+
: '';
|
|
691
800
|
const githubGitConfigStep = isGithubCliSource(cliSource)
|
|
692
801
|
? `${getGithubGitConfigStep()}\n`
|
|
693
802
|
: '';
|
|
@@ -696,14 +805,25 @@ export function generateRollbackWorkflowYaml(
|
|
|
696
805
|
environments,
|
|
697
806
|
config
|
|
698
807
|
);
|
|
808
|
+
const k8sIf = getKubernetesSetupIfExpression(
|
|
809
|
+
allDeployNames,
|
|
810
|
+
environments,
|
|
811
|
+
'rollback'
|
|
812
|
+
);
|
|
699
813
|
const kubernetesSteps = hasKubernetesDeploy(allDeployNames, environments)
|
|
700
|
-
? `${getKubernetesSetupSteps(kubeconfigSecret)}\n`
|
|
814
|
+
? `${getKubernetesSetupSteps(kubeconfigSecret, k8sIf)}\n`
|
|
701
815
|
: '';
|
|
702
816
|
|
|
703
817
|
const rollbackCmd = getCliRollbackCommand();
|
|
704
818
|
const envChoiceOptions = formatEnvironmentChoiceOptions(environments);
|
|
705
819
|
const hasEnvs = envNames.length > 0;
|
|
706
820
|
|
|
821
|
+
const phpInstallStep = isPhpProject(config)
|
|
822
|
+
? ` - name: Install project dependencies
|
|
823
|
+
run: ${getInstallDepsCommand(config)}
|
|
824
|
+
`
|
|
825
|
+
: '';
|
|
826
|
+
|
|
707
827
|
const environmentInput = hasEnvs
|
|
708
828
|
? ` environment:
|
|
709
829
|
description: 'Environment to roll back (or "all")'
|
|
@@ -747,10 +867,10 @@ ${environmentInput}jobs:
|
|
|
747
867
|
runs-on: ubuntu-latest
|
|
748
868
|
steps:
|
|
749
869
|
- uses: actions/checkout@v4
|
|
750
|
-
- uses: actions/setup-node@v4
|
|
870
|
+
${phpSetupSteps ? `${phpSetupSteps}\n` : ''} - uses: actions/setup-node@v4
|
|
751
871
|
with:
|
|
752
872
|
node-version: '20'
|
|
753
|
-
${kubernetesSteps}${githubGitConfigStep} - name: Install DeployHub CLI
|
|
873
|
+
${kubernetesSteps}${githubGitConfigStep}${phpInstallStep} - name: Install DeployHub CLI
|
|
754
874
|
run: npm install ${installSpec} --no-save
|
|
755
875
|
- name: Rollback
|
|
756
876
|
env:
|
|
@@ -774,7 +894,7 @@ function getInstallDepsCommand(config) {
|
|
|
774
894
|
if (language === 'python' || ['fastapi', 'django', 'flask', 'python'].includes(framework)) {
|
|
775
895
|
return 'pip install -r requirements.txt';
|
|
776
896
|
}
|
|
777
|
-
if (['laravel', 'symfony', 'php'].includes(framework)) {
|
|
897
|
+
if (language === 'php' || ['laravel', 'symfony', 'php'].includes(framework)) {
|
|
778
898
|
return 'composer install --no-interaction';
|
|
779
899
|
}
|
|
780
900
|
if (framework === 'spring' || framework === 'java') {
|