underpost 3.2.2 → 3.2.4

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.
@@ -3,17 +3,27 @@ on:
3
3
  push:
4
4
  tags:
5
5
  - 'v*'
6
+ permissions:
7
+ contents: write
8
+ packages: write
9
+ id-token: write
6
10
  jobs:
7
11
  build-and-publish:
8
12
  # prevents this action from running on forks
9
13
  if: github.repository == 'underpostnet/pwa-microservices-template'
10
14
  runs-on: ubuntu-latest
11
15
  permissions:
12
- contents: read
16
+ contents: write
17
+ packages: write
13
18
  id-token: write
14
19
  steps:
15
20
  - uses: actions/checkout@v6
16
21
 
22
+ - name: Install required packages
23
+ run: |
24
+ sudo apt-get update -y
25
+ sudo apt-get install -y sudo tar gzip bzip2 git curl
26
+
17
27
  - uses: actions/setup-node@v6
18
28
  with:
19
29
  node-version: '24.x'
@@ -27,12 +37,21 @@ jobs:
27
37
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
28
38
  run: npm publish --provenance --access public
29
39
 
40
+ - name: Dispatch release CD
41
+ run: |
42
+ curl -s -f -X POST \
43
+ -H "Accept: application/vnd.github.v3+json" \
44
+ -H "Authorization: token ${{ secrets.GIT_AUTH_TOKEN }}" \
45
+ "https://api.github.com/repos/underpostnet/pwa-microservices-template/actions/workflows/release.cd.yml/dispatches" \
46
+ -d '{"ref":"master"}'
47
+
30
48
  build-and-publish-ghpkg:
31
49
  # prevents this action from running on forks
32
50
  if: github.repository == 'underpostnet/pwa-microservices-template-ghpkg'
33
51
  runs-on: ubuntu-latest
34
52
  permissions:
35
- contents: read
53
+ contents: write
54
+ packages: write
36
55
  id-token: write
37
56
  steps:
38
57
  - uses: actions/checkout@v6
@@ -66,9 +85,3 @@ jobs:
66
85
  npm publish
67
86
  env:
68
87
  NODE_AUTH_TOKEN: ${{ secrets.GIT_AUTH_TOKEN }}
69
-
70
- # Release deploy — triggers release.cd.yml after Docker build succeeds
71
- deploy-release:
72
- needs: build-and-publish
73
- uses: ./.github/workflows/release.cd.yml
74
- secrets: inherit
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## 2026-04-15
4
4
 
5
+ ### cli-cron
6
+
7
+ - Update underpost container environment path for volume mount ([8fdfb5416](https://github.com/underpostnet/engine/commit/8fdfb54165f4ef7379fccbeb20e5c476320bc1f6))
8
+
9
+ ### github-actions
10
+
11
+ - Add dispatch step for release CD in publish workflow ([8dc0e3ccd](https://github.com/underpostnet/engine/commit/8dc0e3ccd1a578f776edf215428d08640b44c3d1))
12
+
13
+ ## New release v:3.2.3 (2026-04-15)
14
+
15
+ ### cli-cron
16
+
17
+ - Remove is-inside-container dependency and implement isInsideContainer method in env module ([79d39ece0](https://github.com/underpostnet/engine/commit/79d39ece0db1f3acb65af22e3bc7f7c6a66487a9))
18
+
19
+ ### github-actions
20
+
21
+ - Ensure deploy-release job runs only on successful build-and-publish ([08ba04632](https://github.com/underpostnet/engine/commit/08ba0463263f6cb2b6c14a6bd56e547c152a0a3a))
22
+
23
+ ## New release v:3.2.2 (2026-04-15)
24
+
5
25
  ### docker-image
6
26
 
7
27
  - Remove unnecessary directory creation and volume declaration for working directory in Dockerfile ([84f7f8950](https://github.com/underpostnet/engine/commit/84f7f8950d45512b6177c7523e4d278f2db25ef4))
package/CLI-HELP.md CHANGED
@@ -1,4 +1,4 @@
1
- ## underpost ci/cd cli v3.2.2
1
+ ## underpost ci/cd cli v3.2.4
2
2
 
3
3
  ### Usage: `underpost [options] [command]`
4
4
  ```
@@ -321,7 +321,7 @@ Manages Underpost configurations using various operators.
321
321
 
322
322
  Arguments:
323
323
  operator The configuration operation to perform. Options: set,
324
- delete, get, list, clean.
324
+ delete, get, list, clean, isInsideContainer.
325
325
  key Optional: The specific configuration key to manage.
326
326
  value Optional: The value to set for the configuration key.
327
327
 
@@ -562,6 +562,8 @@ Options:
562
562
  environment file.
563
563
  --create-from-env Creates secrets from container environment
564
564
  variables (envFrom: secretRef).
565
+ --global-clean Removes all filesystem traces of secrets
566
+ (engine-private, .env, conf cache).
565
567
  --list Lists all available secrets for the
566
568
  platform.
567
569
  -h, --help display help for command
@@ -685,6 +687,7 @@ Options:
685
687
  --instances Apply to instance data collection
686
688
  --generate Generate cluster metadata
687
689
  --itc Apply under container execution context
690
+ --dev Sets the development cli context
688
691
  -h, --help display help for command
689
692
 
690
693
  ```
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  <div align="center">
18
18
 
19
- [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/3.2.2)](https://socket.dev/npm/package/underpost/overview/3.2.2) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
19
+ [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/3.2.4)](https://socket.dev/npm/package/underpost/overview/3.2.4) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
20
20
 
21
21
  </div>
22
22
 
@@ -61,7 +61,7 @@ npm run dev
61
61
  <a target="_top" href="https://www.nexodev.org/docs?cid=src">See Docs here.</a>
62
62
 
63
63
  <!-- cli-index-start -->
64
- ## underpost ci/cd cli v3.2.2
64
+ ## underpost ci/cd cli v3.2.4
65
65
 
66
66
  ### Usage: `underpost [options] [command]`
67
67
  ```
@@ -23,7 +23,7 @@ spec:
23
23
  spec:
24
24
  containers:
25
25
  - name: dd-cron-backup
26
- image: underpost/underpost-engine:v3.2.2
26
+ image: underpost/underpost-engine:v3.2.4
27
27
  command:
28
28
  - /bin/sh
29
29
  - -c
@@ -35,9 +35,8 @@ spec:
35
35
  volumeMounts:
36
36
  - mountPath: /home/dd/engine
37
37
  name: underpost-cron-container-volume
38
- - mountPath: /usr/lib/node_modules/underpost/.env
38
+ - mountPath: /usr/lib/node_modules/underpost
39
39
  name: underpost-share-env
40
- subPath: .env
41
40
  volumes:
42
41
  - hostPath:
43
42
  path: /home/dd/engine
@@ -23,7 +23,7 @@ spec:
23
23
  spec:
24
24
  containers:
25
25
  - name: dd-cron-dns
26
- image: underpost/underpost-engine:v3.2.2
26
+ image: underpost/underpost-engine:v3.2.4
27
27
  command:
28
28
  - /bin/sh
29
29
  - -c
@@ -35,9 +35,8 @@ spec:
35
35
  volumeMounts:
36
36
  - mountPath: /home/dd/engine
37
37
  name: underpost-cron-container-volume
38
- - mountPath: /usr/lib/node_modules/underpost/.env
38
+ - mountPath: /usr/lib/node_modules/underpost
39
39
  name: underpost-share-env
40
- subPath: .env
41
40
  volumes:
42
41
  - hostPath:
43
42
  path: /home/dd/engine
@@ -17,7 +17,7 @@ spec:
17
17
  spec:
18
18
  containers:
19
19
  - name: dd-default-development-blue
20
- image: localhost/rockylinux9-underpost:v3.2.2
20
+ image: localhost/rockylinux9-underpost:v3.2.4
21
21
  # resources:
22
22
  # requests:
23
23
  # memory: "124Ki"
@@ -100,7 +100,7 @@ spec:
100
100
  spec:
101
101
  containers:
102
102
  - name: dd-default-development-green
103
- image: localhost/rockylinux9-underpost:v3.2.2
103
+ image: localhost/rockylinux9-underpost:v3.2.4
104
104
  # resources:
105
105
  # requests:
106
106
  # memory: "124Ki"
@@ -20,7 +20,7 @@ spec:
20
20
  spec:
21
21
  containers:
22
22
  - name: dd-test-development-blue
23
- image: localhost/rockylinux9-underpost:v3.2.2
23
+ image: localhost/rockylinux9-underpost:v3.2.4
24
24
  envFrom:
25
25
  - secretRef:
26
26
  name: underpost-config
@@ -112,7 +112,7 @@ spec:
112
112
  spec:
113
113
  containers:
114
114
  - name: dd-test-development-green
115
- image: localhost/rockylinux9-underpost:v3.2.2
115
+ image: localhost/rockylinux9-underpost:v3.2.4
116
116
  envFrom:
117
117
  - secretRef:
118
118
  name: underpost-config
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "3.2.2",
5
+ "version": "3.2.4",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "node --max-old-space-size=8192 src/server",
@@ -86,7 +86,6 @@
86
86
  "http-proxy-middleware": "^3.0.5",
87
87
  "ignore-walk": "^8.0.0",
88
88
  "iovalkey": "^0.3.3",
89
- "is-inside-container": "^1.0.0",
90
89
  "json-colorizer": "^3.0.1",
91
90
  "jsonwebtoken": "^9.0.3",
92
91
  "mariadb": "^3.2.2",
package/src/cli/db.js CHANGED
@@ -14,7 +14,6 @@ import { DataBaseProvider } from '../db/DataBaseProvider.js';
14
14
  import { loadReplicas, pathPortAssignmentFactory, loadCronDeployEnv } from '../server/conf.js';
15
15
  import Underpost from '../index.js';
16
16
  import { timer } from '../client/components/core/CommonJs.js';
17
- import isInsideContainer from 'is-inside-container';
18
17
  const logger = loggerFactory(import.meta);
19
18
 
20
19
  /**
@@ -582,10 +581,9 @@ class UnderpostDB {
582
581
  repoBackup: false,
583
582
  },
584
583
  ) {
585
- // Ensure engine-private is available (clone ephemerally if inside a deployment
584
+ // Ensure engine-private is available (clone if inside a deployment
586
585
  // container where globalSecretClean has already removed it).
587
586
  const firstDeployId = deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
588
- const { ephemeral } = Underpost.repo.privateEngineRepoFactory(firstDeployId || undefined);
589
587
  try {
590
588
  loadCronDeployEnv();
591
589
  const newBackupTimestamp = new Date().getTime();
@@ -948,11 +946,6 @@ class UnderpostDB {
948
946
  } catch (error) {
949
947
  logger.error('Database operation failed', { error: error.message });
950
948
  throw error;
951
- } finally {
952
- if (ephemeral && isInsideContainer()) {
953
- Underpost.repo.cleanupPrivateEngineRepo();
954
- Underpost.env.clean();
955
- }
956
949
  }
957
950
  },
958
951
 
@@ -965,6 +958,8 @@ class UnderpostDB {
965
958
  * @param {string} [deployId=process.env.DEFAULT_DEPLOY_ID] - The deployment ID.
966
959
  * @param {string} [host=process.env.DEFAULT_DEPLOY_HOST] - The host identifier.
967
960
  * @param {string} [path=process.env.DEFAULT_DEPLOY_PATH] - The path identifier.
961
+ * @param {object} [options] - Options.
962
+ * @param {boolean} [options.dev=false] - Development mode flag.
968
963
  * @return {Promise<void>} Resolves when metadata creation is complete.
969
964
  * @throws {Error} If database configuration is invalid or connection fails.
970
965
  */
@@ -972,8 +967,8 @@ class UnderpostDB {
972
967
  deployId = process.env.DEFAULT_DEPLOY_ID,
973
968
  host = process.env.DEFAULT_DEPLOY_HOST,
974
969
  path = process.env.DEFAULT_DEPLOY_PATH,
970
+ options = { dev: false },
975
971
  ) {
976
- const { ephemeral } = Underpost.repo.privateEngineRepoFactory(deployId || undefined);
977
972
  try {
978
973
  loadCronDeployEnv();
979
974
  deployId = deployId ? deployId : process.env.DEFAULT_DEPLOY_ID;
@@ -1146,11 +1141,6 @@ class UnderpostDB {
1146
1141
  } catch (error) {
1147
1142
  logger.error('Cluster metadata creation failed', { error: error.message });
1148
1143
  throw error;
1149
- } finally {
1150
- if (ephemeral && isInsideContainer()) {
1151
- Underpost.repo.cleanupPrivateEngineRepo();
1152
- Underpost.env.clean();
1153
- }
1154
1144
  }
1155
1145
  },
1156
1146
 
@@ -1165,6 +1155,7 @@ class UnderpostDB {
1165
1155
  * @param {string} [options.hosts=''] - Comma-separated list of hosts to filter.
1166
1156
  * @param {string} [options.paths=''] - Comma-separated list of paths to filter.
1167
1157
  * @param {boolean} [options.dryRun=false] - If true, only reports what would be deleted.
1158
+ * @param {boolean} [options.dev=false] - Development mode flag.
1168
1159
  * @return {Promise<void>} Resolves when clean operation is complete.
1169
1160
  */
1170
1161
  async cleanFsCollection(
@@ -1173,10 +1164,10 @@ class UnderpostDB {
1173
1164
  hosts: '',
1174
1165
  paths: '',
1175
1166
  dryRun: false,
1167
+ dev: false,
1176
1168
  },
1177
1169
  ) {
1178
1170
  const firstDeployId = deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
1179
- const { ephemeral } = Underpost.repo.privateEngineRepoFactory(firstDeployId || undefined);
1180
1171
  try {
1181
1172
  loadCronDeployEnv();
1182
1173
  if (deployList === 'dd') deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
@@ -1386,11 +1377,6 @@ class UnderpostDB {
1386
1377
  } catch (error) {
1387
1378
  logger.error('File collection cleanup failed', { error: error.message });
1388
1379
  throw error;
1389
- } finally {
1390
- if (ephemeral && isInsideContainer()) {
1391
- Underpost.repo.cleanupPrivateEngineRepo();
1392
- Underpost.env.clean();
1393
- }
1394
1380
  }
1395
1381
  },
1396
1382
 
@@ -1410,6 +1396,7 @@ class UnderpostDB {
1410
1396
  * @param {boolean} [options.export=false] - Export metadata to backup.
1411
1397
  * @param {boolean} [options.instances=false] - Process instances collection.
1412
1398
  * @param {boolean} [options.crons=false] - Process crons collection.
1399
+ * @param {boolean} [options.dev=false] - Development mode flag.
1413
1400
  * @return {Promise<void>} Resolves when backup operation is complete.
1414
1401
  */
1415
1402
  async clusterMetadataBackupCallback(
@@ -1423,9 +1410,9 @@ class UnderpostDB {
1423
1410
  export: false,
1424
1411
  instances: false,
1425
1412
  crons: false,
1413
+ dev: false,
1426
1414
  },
1427
1415
  ) {
1428
- const { ephemeral } = Underpost.repo.privateEngineRepoFactory(deployId || undefined);
1429
1416
  try {
1430
1417
  loadCronDeployEnv();
1431
1418
  deployId = deployId ? deployId : process.env.DEFAULT_DEPLOY_ID;
@@ -1492,11 +1479,6 @@ class UnderpostDB {
1492
1479
  } catch (error) {
1493
1480
  logger.error('Cluster metadata backup operation failed', { error: error.message });
1494
1481
  throw error;
1495
- } finally {
1496
- if (ephemeral && isInsideContainer()) {
1497
- Underpost.repo.cleanupPrivateEngineRepo();
1498
- Underpost.env.clean();
1499
- }
1500
1482
  }
1501
1483
  },
1502
1484
  };
package/src/cli/env.js CHANGED
@@ -153,6 +153,16 @@ class UnderpostRootEnv {
153
153
  const envPath = `${exeRootPath}/.env`;
154
154
  fs.removeSync(envPath);
155
155
  },
156
+ /**
157
+ * @method isInsideContainer
158
+ * @description Detects whether the current process is running inside a container.
159
+ * Checks for Kubernetes service injection or Docker's .dockerenv marker.
160
+ * @returns {boolean} True if running inside a container.
161
+ * @memberof UnderpostEnv
162
+ */
163
+ isInsideContainer() {
164
+ return !!process.env.KUBERNETES_SERVICE_HOST || fs.existsSync('/.dockerenv');
165
+ },
156
166
  };
157
167
  }
158
168
 
package/src/cli/index.js CHANGED
@@ -338,9 +338,11 @@ program
338
338
  .option('--init', 'Initializes the secrets platform environment.')
339
339
  .option('--create-from-file <path-env-file>', 'Creates secrets from a specified environment file.')
340
340
  .option('--create-from-env', 'Creates secrets from container environment variables (envFrom: secretRef).')
341
+ .option('--global-clean', 'Removes all filesystem traces of secrets (engine-private, .env, conf cache).')
341
342
  .option('--list', 'Lists all available secrets for the platform.')
342
343
  .description(`Manages secrets for various platforms.`)
343
344
  .action((...args) => {
345
+ if (args[1].globalClean) return Underpost.secret.globalSecretClean();
344
346
  if (args[1].createFromFile) return Underpost.secret[args[0]].createFromEnvFile(args[1].createFromFile);
345
347
  if (args[1].createFromEnv) return Underpost.secret[args[0]].createFromContainerEnv();
346
348
  if (args[1].list) return Underpost.secret[args[0]].list();
@@ -442,6 +444,7 @@ program
442
444
  .option('--instances', 'Apply to instance data collection')
443
445
  .option('--generate', 'Generate cluster metadata')
444
446
  .option('--itc', 'Apply under container execution context')
447
+ .option('--dev', 'Sets the development cli context')
445
448
  .description('Manages cluster metadata operations, including import and export.')
446
449
  .action(Underpost.db.clusterMetadataBackupCallback);
447
450
 
@@ -1501,6 +1501,7 @@ Prevent build private config repo.`,
1501
1501
  `git config --global --add safe.directory '${siteRoot}' 2>/dev/null || true`,
1502
1502
  `cd '${siteRoot}' && git add -A && git commit -m 'backup $(date -u +%Y-%m-%dT%H:%M:%SZ)' || true`,
1503
1503
  `cd '${siteRoot}' && underpost push . ${githubUsername}/${repoName}`,
1504
+ `cd /home/dd/engine && node bin secret underpost --global-clean`,
1504
1505
  ].join(' && ');
1505
1506
 
1506
1507
  try {
@@ -1517,38 +1518,41 @@ Prevent build private config repo.`,
1517
1518
  /**
1518
1519
  * Clones the deploy-specific private repository into `./engine-private`
1519
1520
  * when it does not already exist on disk. Returns `{ ephemeral: true }`
1520
- * when a fresh clone was performed so the caller can remove it after use,
1521
- * or `{ ephemeral: false }` when the directory was already present (host,
1522
- * cron hostPath mount, or prior build step).
1521
+ * If `./engine-private` already exists, the call is a no-op unless
1522
+ * `options.force` is `true`, in which case the directory is removed and
1523
+ * re-cloned.
1523
1524
  *
1524
1525
  * @param {string} [deployId] - Deploy ID (e.g. `dd-core`) used to derive
1525
1526
  * the repo name `engine-{component}-private`. Falls back to
1526
- * `process.env.DEFAULT_DEPLOY_ID`.
1527
- * @returns {{ ephemeral: boolean }}
1527
+ * `process.env.DEFAULT_DEPLOY_ID`. When neither is available the
1528
+ * default repo name `engine-private` is used.
1529
+ * @param {object} [options]
1530
+ * @param {boolean} [options.force=false] - Remove existing `engine-private`
1531
+ * and re-clone.
1528
1532
  * @memberof UnderpostRepository
1529
1533
  */
1530
- privateEngineRepoFactory(deployId) {
1531
- if (fs.existsSync('./engine-private')) return { ephemeral: false };
1534
+ privateEngineRepoFactory(deployId, options = { force: false }) {
1535
+ if (fs.existsSync('./engine-private') && !options.force) return;
1532
1536
 
1533
- const effectiveDeployId = deployId || process.env.DEFAULT_DEPLOY_ID;
1534
- if (!effectiveDeployId) {
1535
- throw new Error('privateEngineRepoFactory: no deployId provided and DEFAULT_DEPLOY_ID not set');
1537
+ if (options.force && fs.existsSync('./engine-private')) {
1538
+ fs.removeSync('./engine-private');
1539
+ logger.info('engine-private removed (force re-clone)');
1536
1540
  }
1541
+
1542
+ const effectiveDeployId = deployId || process.env.DEFAULT_DEPLOY_ID;
1543
+
1537
1544
  const username = process.env.GITHUB_USERNAME;
1538
1545
  if (!username) {
1539
1546
  throw new Error('privateEngineRepoFactory: GITHUB_USERNAME not set');
1540
1547
  }
1541
1548
 
1542
- const component = effectiveDeployId.split('-')[1];
1543
- const repoName = `engine-${component}-private`;
1544
- logger.info(`engine-private missing — cloning ${username}/${repoName} (ephemeral)`);
1549
+ const repoName = effectiveDeployId ? `engine-${effectiveDeployId.split('-')[1]}-private` : 'engine-private';
1550
+ logger.info(`engine-private missing cloning ${username}/${repoName}`);
1545
1551
  shellExec(`underpost clone ${username}/${repoName}`);
1546
1552
  if (!fs.existsSync(`./${repoName}`)) {
1547
1553
  throw new Error(`privateEngineRepoFactory: clone failed for ${username}/${repoName}`);
1548
1554
  }
1549
- shellExec(`mv ./${repoName} ./engine-private`);
1550
-
1551
- return { ephemeral: true };
1555
+ if (repoName !== 'engine-private') shellExec(`mv ./${repoName} ./engine-private`);
1552
1556
  },
1553
1557
 
1554
1558
  /**
package/src/cli/run.js CHANGED
@@ -699,6 +699,10 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
699
699
  );
700
700
 
701
701
  if (isDeployRunnerContext(path, options)) {
702
+ // Backup app/services repositories with repo-backup configured
703
+ shellExec(
704
+ `${baseCommand} db ${deployId} ${clusterFlag} --repo-backup --dev --primary-pod --git --force-clone --preserveUUID ${options.namespace ? ` --ns ${options.namespace}` : ''}`,
705
+ );
702
706
  shellExec(
703
707
  `${baseCommand} deploy${clusterFlag}${cmdString} --replicas ${replicas} --disable-update-proxy ${deployId} ${env} --versions ${versions}${
704
708
  options.namespace ? ` --namespace ${options.namespace}` : ''
package/src/index.js CHANGED
@@ -44,7 +44,7 @@ class Underpost {
44
44
  * @type {String}
45
45
  * @memberof Underpost
46
46
  */
47
- static version = 'v3.2.2';
47
+ static version = 'v3.2.4';
48
48
 
49
49
  /**
50
50
  * Required Node.js major version
@@ -38,59 +38,54 @@ class BackUp {
38
38
  */
39
39
  static callback = async function (deployList, options = { git: false }) {
40
40
  const firstDeployId = deployList && deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
41
- const { ephemeral } = Underpost.repo.privateEngineRepoFactory(firstDeployId || undefined);
42
- try {
43
- loadCronDeployEnv();
44
- if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
45
- deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
41
+ loadCronDeployEnv();
42
+ if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
43
+ deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
46
44
 
47
- logger.info('init backups callback', deployList);
48
- await logger.setUpInfo();
45
+ logger.info('init backups callback', deployList);
46
+ await logger.setUpInfo();
49
47
 
50
- const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : options.kubeadm ? ' --kubeadm' : '';
48
+ const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : options.kubeadm ? ' --kubeadm' : '';
51
49
 
52
- for (const _deployId of deployList.split(',')) {
53
- const deployId = _deployId.trim();
54
- if (!deployId) continue;
50
+ for (const _deployId of deployList.split(',')) {
51
+ const deployId = _deployId.trim();
52
+ if (!deployId) continue;
55
53
 
56
- const dbCommand = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod${clusterFlag} ${deployId}`;
57
- const repoCommand = `node bin db --repo-backup${clusterFlag} ${deployId}`;
54
+ const dbCommand = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod${clusterFlag} ${deployId}`;
55
+ const repoCommand = `node bin db --repo-backup${clusterFlag} ${deployId}`;
58
56
 
59
- // Pass GITHUB_TOKEN and GITHUB_USERNAME ephemerally through the SSH command
60
- // so git operations can push backups without relying on host env files.
61
- const envPrefix = [
62
- process.env.GITHUB_TOKEN ? `GITHUB_TOKEN=${process.env.GITHUB_TOKEN}` : '',
63
- process.env.GITHUB_USERNAME ? `GITHUB_USERNAME=${process.env.GITHUB_USERNAME}` : '',
64
- ]
65
- .filter(Boolean)
66
- .join(' ');
67
- const prefixCmd = (cmd) => (envPrefix ? `${envPrefix} ${cmd}` : cmd);
57
+ // Pass GITHUB_TOKEN and GITHUB_USERNAME ephemerally through the SSH command
58
+ // so git operations can push backups without relying on host env files.
59
+ const envPrefix = [
60
+ process.env.GITHUB_TOKEN ? `GITHUB_TOKEN=${process.env.GITHUB_TOKEN}` : '',
61
+ process.env.GITHUB_USERNAME ? `GITHUB_USERNAME=${process.env.GITHUB_USERNAME}` : '',
62
+ ]
63
+ .filter(Boolean)
64
+ .join(' ');
65
+ const prefixCmd = (cmd) => (envPrefix ? `${envPrefix} ${cmd}` : cmd);
68
66
 
69
- try {
70
- logger.info('Executing database export via SSH for', deployId);
71
- await Underpost.ssh.sshRemoteRunner(prefixCmd(dbCommand), {
72
- remote: true,
73
- useSudo: true,
74
- cd: '/home/dd/engine',
75
- });
76
- } catch (err) {
77
- logger.error(`Error during database export for ${deployId}:`, err);
78
- }
67
+ try {
68
+ logger.info('Executing database export via SSH for', deployId);
69
+ await Underpost.ssh.sshRemoteRunner(prefixCmd(dbCommand), {
70
+ remote: true,
71
+ useSudo: true,
72
+ cd: '/home/dd/engine',
73
+ });
74
+ } catch (err) {
75
+ logger.error(`Error during database export for ${deployId}:`, err);
76
+ }
79
77
 
80
- // Repository backup: Cron container → SSH to host → host finds pod → kubectl exec git backup
81
- try {
82
- logger.info('Executing repository backup via SSH for', deployId);
83
- await Underpost.ssh.sshRemoteRunner(prefixCmd(repoCommand), {
84
- remote: true,
85
- useSudo: true,
86
- cd: '/home/dd/engine',
87
- });
88
- } catch (err) {
89
- logger.error(`Error during repository backup for ${deployId}:`, err);
90
- }
78
+ // Repository backup: Cron container → SSH to host → host finds pod → kubectl exec git backup
79
+ try {
80
+ logger.info('Executing repository backup via SSH for', deployId);
81
+ await Underpost.ssh.sshRemoteRunner(prefixCmd(repoCommand), {
82
+ remote: true,
83
+ useSudo: true,
84
+ cd: '/home/dd/engine',
85
+ });
86
+ } catch (err) {
87
+ logger.error(`Error during repository backup for ${deployId}:`, err);
91
88
  }
92
- } finally {
93
- if (ephemeral) Underpost.repo.cleanupPrivateEngineRepo();
94
89
  }
95
90
  };
96
91
  }
@@ -16,7 +16,7 @@ const volumeHostPath = '/home/dd';
16
16
  const enginePath = '/home/dd/engine';
17
17
  const cronVolumeName = 'underpost-cron-container-volume';
18
18
  const shareEnvVolumeName = 'underpost-share-env';
19
- const underpostContainerEnvPath = '/usr/lib/node_modules/underpost/.env';
19
+ const underpostContainerEnvDir = '/usr/lib/node_modules/underpost';
20
20
 
21
21
  /**
22
22
  * Generates a Kubernetes CronJob YAML manifest string.
@@ -105,9 +105,8 @@ spec:
105
105
  volumeMounts:
106
106
  - mountPath: ${enginePath}
107
107
  name: ${cronVolumeName}
108
- - mountPath: ${underpostContainerEnvPath}
108
+ - mountPath: ${underpostContainerEnvDir}
109
109
  name: ${shareEnvVolumeName}
110
- subPath: .env
111
110
  volumes:
112
111
  - hostPath:
113
112
  path: ${enginePath}
@@ -9,7 +9,6 @@ import { awaitDeployMonitor } from './conf.js';
9
9
  import { actionInitLog, loggerFactory } from './logger.js';
10
10
  import { shellCd, shellExec } from './process.js';
11
11
  import Underpost from '../index.js';
12
- import isInsideContainer from 'is-inside-container';
13
12
  const logger = loggerFactory(import.meta);
14
13
 
15
14
  /**
@@ -197,7 +196,7 @@ class UnderpostStartUp {
197
196
  shellExec(`node bin env ${deployId} ${env}`);
198
197
  shellExec(`npm ${runCmd} ${deployId}`, { async: true });
199
198
  await awaitDeployMonitor(true);
200
- if (env === 'production' && isInsideContainer()) Underpost.secret.globalSecretClean();
199
+ if (env === 'production' && Underpost.env.isInsideContainer()) Underpost.secret.globalSecretClean();
201
200
  Underpost.env.set('container-status', `${deployId}-${env}-running-deployment`);
202
201
  },
203
202
  };