@underpostnet/underpost 3.0.3 → 3.1.1
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/{.env.production → .env.example} +20 -2
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +22 -7
- package/.github/workflows/publish.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -3
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +3 -2
- package/.vscode/extensions.json +9 -8
- package/.vscode/settings.json +3 -2
- package/CHANGELOG.md +160 -1
- package/CLI-HELP.md +71 -52
- package/README.md +2 -2
- package/bin/build.js +4 -1
- package/bin/deploy.js +150 -208
- package/bin/file.js +2 -1
- package/bin/vs.js +3 -3
- package/conf.js +30 -13
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +52 -52
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/manifests/pv-pvc-dd.yaml +1 -1
- package/package.json +48 -43
- package/scripts/k3s-node-setup.sh +1 -1
- package/src/api/document/document.service.js +1 -1
- package/src/api/file/file.controller.js +3 -1
- package/src/api/file/file.service.js +28 -5
- package/src/api/user/user.router.js +10 -5
- package/src/api/user/user.service.js +7 -7
- package/src/cli/baremetal.js +6 -10
- package/src/cli/cloud-init.js +0 -3
- package/src/cli/db.js +54 -71
- package/src/cli/deploy.js +64 -12
- package/src/cli/env.js +5 -5
- package/src/cli/fs.js +0 -2
- package/src/cli/image.js +0 -3
- package/src/cli/index.js +27 -13
- package/src/cli/monitor.js +5 -6
- package/src/cli/repository.js +329 -35
- package/src/cli/run.js +118 -69
- package/src/cli/secrets.js +1 -3
- package/src/cli/ssh.js +1 -1
- package/src/client/components/core/AgGrid.js +20 -5
- package/src/client/components/core/Content.js +22 -3
- package/src/client/components/core/Docs.js +21 -4
- package/src/client/components/core/FileExplorer.js +71 -4
- package/src/client/components/core/Input.js +1 -1
- package/src/client/components/core/Modal.js +20 -6
- package/src/client/public/default/sitemap +3 -3
- package/src/client/public/test/sitemap +3 -3
- package/src/client.build.js +0 -3
- package/src/client.dev.js +0 -3
- package/src/db/DataBaseProvider.js +17 -2
- package/src/db/mariadb/MariaDB.js +14 -9
- package/src/db/mongo/MongooseDB.js +17 -1
- package/src/index.js +1 -1
- package/src/proxy.js +0 -3
- package/src/runtime/express/Express.js +7 -1
- package/src/runtime/lampp/Lampp.js +6 -13
- package/src/server/auth.js +6 -9
- package/src/server/backup.js +2 -3
- package/src/server/client-build-docs.js +178 -3
- package/src/server/client-build-live.js +9 -18
- package/src/server/client-build.js +175 -38
- package/src/server/client-dev-server.js +14 -13
- package/src/server/conf.js +357 -149
- package/src/server/cron.js +2 -1
- package/src/server/dns.js +28 -12
- package/src/server/downloader.js +0 -2
- package/src/server/logger.js +27 -9
- package/src/server/peer.js +0 -2
- package/src/server/process.js +1 -50
- package/src/server/proxy.js +4 -8
- package/src/server/runtime.js +5 -8
- package/src/server/ssr.js +0 -3
- package/src/server/start.js +5 -5
- package/src/server/tls.js +0 -2
- package/src/server.js +0 -4
- package/.env.development +0 -43
- package/.env.test +0 -43
package/src/cli/repository.js
CHANGED
|
@@ -4,18 +4,27 @@
|
|
|
4
4
|
* @namespace UnderpostRepository
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { commitData } from '../client/components/core/CommonJs.js';
|
|
8
7
|
import dotenv from 'dotenv';
|
|
8
|
+
import { commitData } from '../client/components/core/CommonJs.js';
|
|
9
9
|
import { pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
10
10
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
11
11
|
import fs from 'fs-extra';
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
getNpmRootPath,
|
|
14
|
+
Config,
|
|
15
|
+
loadConf,
|
|
16
|
+
readConfJson,
|
|
17
|
+
getConfFilePath,
|
|
18
|
+
loadReplicas,
|
|
19
|
+
loadConfServerJson,
|
|
20
|
+
getDataDeploy,
|
|
21
|
+
buildReplicaId,
|
|
22
|
+
writeEnv,
|
|
23
|
+
} from '../server/conf.js';
|
|
24
|
+
import { buildClient } from '../server/client-build.js';
|
|
14
25
|
import { DefaultConf } from '../../conf.js';
|
|
15
26
|
import Underpost from '../index.js';
|
|
16
27
|
|
|
17
|
-
dotenv.config();
|
|
18
|
-
|
|
19
28
|
const logger = loggerFactory(import.meta);
|
|
20
29
|
|
|
21
30
|
const diffCmd = `--no-pager show -U0 -w --word-diff=color --word-diff-regex='[^[:space:]]' --color=always`;
|
|
@@ -37,8 +46,8 @@ class UnderpostRepository {
|
|
|
37
46
|
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
38
47
|
* @memberof UnderpostRepository
|
|
39
48
|
*/
|
|
40
|
-
clone(gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`, options = { bare: false,
|
|
41
|
-
const gExtension = options.
|
|
49
|
+
clone(gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`, options = { bare: false, g8: false }) {
|
|
50
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
42
51
|
const repoName = gitUri.split('/').pop();
|
|
43
52
|
if (fs.existsSync(`./${repoName}`)) fs.removeSync(`./${repoName}`);
|
|
44
53
|
shellExec(
|
|
@@ -54,16 +63,16 @@ class UnderpostRepository {
|
|
|
54
63
|
* Pulls updates from a GitHub repository.
|
|
55
64
|
* @param {string} [repoPath='./'] - The local path to the repository.
|
|
56
65
|
* @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
|
|
57
|
-
* @param {object} [options={
|
|
66
|
+
* @param {object} [options={ g8: false }] - Pulling options.
|
|
58
67
|
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
59
68
|
* @memberof UnderpostRepository
|
|
60
69
|
*/
|
|
61
70
|
pull(
|
|
62
71
|
repoPath = './',
|
|
63
72
|
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
|
|
64
|
-
options = {
|
|
73
|
+
options = { g8: false },
|
|
65
74
|
) {
|
|
66
|
-
const gExtension = options.
|
|
75
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
67
76
|
shellExec(
|
|
68
77
|
`cd ${repoPath} && git pull https://${
|
|
69
78
|
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
@@ -118,15 +127,26 @@ class UnderpostRepository {
|
|
|
118
127
|
changelogBuild: false,
|
|
119
128
|
changelogMinVersion: '',
|
|
120
129
|
changelogNoHash: false,
|
|
130
|
+
b: false,
|
|
121
131
|
},
|
|
122
132
|
) {
|
|
123
133
|
if (!repoPath) repoPath = '.';
|
|
124
134
|
|
|
135
|
+
if (options.b) {
|
|
136
|
+
const currentBranch = shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
137
|
+
stdout: true,
|
|
138
|
+
silent: true,
|
|
139
|
+
disableLog: true,
|
|
140
|
+
}).trim();
|
|
141
|
+
if (options.copy) pbcopy(currentBranch);
|
|
142
|
+
else console.log(currentBranch);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
if (options.changelog !== undefined || options.changelogBuild) {
|
|
126
147
|
const ciIntegrationPrefix = 'ci(package-pwa-microservices-';
|
|
127
148
|
const ciIntegrationVersionPrefix = 'New release v:';
|
|
128
149
|
const releaseMatch = 'New release v:';
|
|
129
|
-
|
|
130
150
|
// Helper: parse [<tag>] commits into grouped sections
|
|
131
151
|
const buildSectionChangelog = (commits) => {
|
|
132
152
|
const groups = {};
|
|
@@ -261,11 +281,26 @@ class UnderpostRepository {
|
|
|
261
281
|
|
|
262
282
|
let commits;
|
|
263
283
|
if (!hasExplicitCount) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
284
|
+
let boundaryIndex = -1;
|
|
285
|
+
|
|
286
|
+
// New boundary: deploy hash stored in config via `underpost config set LAST_CI_DEPLOY_HASH`
|
|
287
|
+
const lastDeployHash = shellExec('underpost config get LAST_CI_DEPLOY_HASH --plain', {
|
|
288
|
+
stdout: true,
|
|
289
|
+
silent: true,
|
|
290
|
+
disableLog: true,
|
|
291
|
+
}).trim();
|
|
292
|
+
if (lastDeployHash && lastDeployHash !== 'undefined' && lastDeployHash !== 'null') {
|
|
293
|
+
boundaryIndex = allCommits.findIndex((c) => c.fullHash === lastDeployHash || c.hash === lastDeployHash);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Fallback: old CI integration commit boundary
|
|
297
|
+
if (boundaryIndex < 0) {
|
|
298
|
+
boundaryIndex = allCommits.findIndex(
|
|
299
|
+
(c) => c.message.startsWith(ciIntegrationPrefix) && !c.message.match(ciIntegrationVersionPrefix),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
commits = boundaryIndex >= 0 ? allCommits.slice(0, boundaryIndex) : allCommits;
|
|
269
304
|
} else {
|
|
270
305
|
commits = allCommits;
|
|
271
306
|
}
|
|
@@ -360,7 +395,7 @@ class UnderpostRepository {
|
|
|
360
395
|
* Pushes commits to a remote GitHub repository.
|
|
361
396
|
* @param {string} [repoPath='./'] - The local path to the repository.
|
|
362
397
|
* @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
|
|
363
|
-
* @param {object} [options={ f: false,
|
|
398
|
+
* @param {object} [options={ f: false, g8: false }] - Push options.
|
|
364
399
|
* @param {boolean} [options.f=false] - If true, forces the push.
|
|
365
400
|
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
366
401
|
* @memberof UnderpostRepository
|
|
@@ -368,9 +403,9 @@ class UnderpostRepository {
|
|
|
368
403
|
push(
|
|
369
404
|
repoPath = './',
|
|
370
405
|
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
|
|
371
|
-
options = { f: false,
|
|
406
|
+
options = { f: false, g8: false },
|
|
372
407
|
) {
|
|
373
|
-
const gExtension = options.
|
|
408
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
374
409
|
shellExec(
|
|
375
410
|
`cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}${gExtension}${
|
|
376
411
|
options?.f === true ? ' --force' : ''
|
|
@@ -403,6 +438,7 @@ class UnderpostRepository {
|
|
|
403
438
|
* @param {boolean} [options.cleanTemplate=false] - If true, cleans the pwa-microservices-template build directory.
|
|
404
439
|
* @param {boolean} [options.build=false] - If true, builds the deployment to pwa-microservices-template (requires deployId).
|
|
405
440
|
* @param {boolean} [options.syncConf=false] - If true, syncs configuration to private repositories (requires deployId).
|
|
441
|
+
* @param {boolean} [options.syncStart=false] - If true, syncs start scripts in deploy ID package.json with root package.json.
|
|
406
442
|
* @param {boolean} [options.defaultConf=false] - If true, updates the default configuration file (requires deployId).
|
|
407
443
|
* @param {string} [options.confWorkflowId=''] - If provided, uses this configuration workflow ID.
|
|
408
444
|
* @returns {Promise<boolean>} A promise that resolves when the initialization is complete.
|
|
@@ -420,6 +456,7 @@ class UnderpostRepository {
|
|
|
420
456
|
cleanTemplate: false,
|
|
421
457
|
build: false,
|
|
422
458
|
syncConf: false,
|
|
459
|
+
syncStart: false,
|
|
423
460
|
defaultConf: false,
|
|
424
461
|
confWorkflowId: '',
|
|
425
462
|
},
|
|
@@ -448,6 +485,13 @@ class UnderpostRepository {
|
|
|
448
485
|
|
|
449
486
|
if (options.deployId) {
|
|
450
487
|
let deployId = options.deployId;
|
|
488
|
+
|
|
489
|
+
// Handle sync-start operation (before dd- prefix normalization to support 'dd' special case)
|
|
490
|
+
if (options.syncStart) {
|
|
491
|
+
shellExec(`node bin/deploy sync-start ${deployId}`);
|
|
492
|
+
return resolve(true);
|
|
493
|
+
}
|
|
494
|
+
|
|
451
495
|
if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
|
|
452
496
|
// Handle purge operation
|
|
453
497
|
if (options.purge) {
|
|
@@ -547,7 +591,6 @@ class UnderpostRepository {
|
|
|
547
591
|
const npmRoot = getNpmRootPath();
|
|
548
592
|
const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
|
|
549
593
|
const destFolder = `./${projectName}`;
|
|
550
|
-
logger.info('Note: This process may take several minutes to complete');
|
|
551
594
|
logger.info('build app', { destFolder });
|
|
552
595
|
if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
|
|
553
596
|
fs.mkdirSync(destFolder, { recursive: true });
|
|
@@ -572,6 +615,215 @@ class UnderpostRepository {
|
|
|
572
615
|
});
|
|
573
616
|
},
|
|
574
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Builds client assets, single replicas, and/or syncs environment ports.
|
|
620
|
+
* @param {string} [deployId='dd-default'] - The deployment ID.
|
|
621
|
+
* @param {string} [subConf=''] - The sub-configuration for the build.
|
|
622
|
+
* @param {string} [host=''] - Comma-separated hosts to filter the build.
|
|
623
|
+
* @param {string} [path=''] - Comma-separated paths to filter the build.
|
|
624
|
+
* @param {object} [options] - Build options.
|
|
625
|
+
* @param {boolean} [options.syncEnvPort=false] - If true, syncs environment port assignments across all deploy IDs.
|
|
626
|
+
* @param {boolean} [options.singleReplica=false] - If true, builds single replica folders instead of full client.
|
|
627
|
+
* @returns {Promise<boolean>} A promise that resolves when the build is complete.
|
|
628
|
+
* @memberof UnderpostRepository
|
|
629
|
+
*/
|
|
630
|
+
client(
|
|
631
|
+
deployId = 'dd-default',
|
|
632
|
+
subConf = '',
|
|
633
|
+
host = '',
|
|
634
|
+
path = '',
|
|
635
|
+
options = {
|
|
636
|
+
syncEnvPort: false,
|
|
637
|
+
singleReplica: false,
|
|
638
|
+
},
|
|
639
|
+
) {
|
|
640
|
+
return new Promise(async (resolve, reject) => {
|
|
641
|
+
try {
|
|
642
|
+
// Handle syncEnvPort operation
|
|
643
|
+
if (options.syncEnvPort) {
|
|
644
|
+
const dataDeploy = await getDataDeploy({ disableSyncEnvPort: true });
|
|
645
|
+
const dataEnv = [
|
|
646
|
+
{ env: 'production', port: 3000 },
|
|
647
|
+
{ env: 'development', port: 4000 },
|
|
648
|
+
{ env: 'test', port: 5000 },
|
|
649
|
+
];
|
|
650
|
+
let portOffset = 0;
|
|
651
|
+
const singleReplicaPortOffsets = {};
|
|
652
|
+
for (const deployIdObj of dataDeploy) {
|
|
653
|
+
const { deployId } = deployIdObj;
|
|
654
|
+
const baseConfPath = fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
655
|
+
? `./engine-private/replica`
|
|
656
|
+
: `./engine-private/conf`;
|
|
657
|
+
|
|
658
|
+
const effectivePortOffset =
|
|
659
|
+
singleReplicaPortOffsets[deployId] !== undefined ? singleReplicaPortOffsets[deployId] : portOffset;
|
|
660
|
+
|
|
661
|
+
for (const envInstanceObj of dataEnv) {
|
|
662
|
+
const envPath = `${baseConfPath}/${deployId}/.env.${envInstanceObj.env}`;
|
|
663
|
+
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
664
|
+
envObj.PORT = `${envInstanceObj.port + effectivePortOffset}`;
|
|
665
|
+
writeEnv(envPath, envObj);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
if (singleReplicaPortOffsets[deployId] !== undefined) continue;
|
|
669
|
+
|
|
670
|
+
const serverConf = loadReplicas(
|
|
671
|
+
deployId,
|
|
672
|
+
loadConfServerJson(`${baseConfPath}/${deployId}/conf.server.json`),
|
|
673
|
+
);
|
|
674
|
+
for (const host of Object.keys(serverConf)) {
|
|
675
|
+
let deferredSingleReplicaSlots = [];
|
|
676
|
+
for (const path of Object.keys(serverConf[host])) {
|
|
677
|
+
if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
678
|
+
deferredSingleReplicaSlots.push({
|
|
679
|
+
replicas: serverConf[host][path].replicas,
|
|
680
|
+
peer: !!serverConf[host][path].peer,
|
|
681
|
+
});
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
portOffset++;
|
|
685
|
+
if (serverConf[host][path].peer) portOffset++;
|
|
686
|
+
}
|
|
687
|
+
for (const slot of deferredSingleReplicaSlots) {
|
|
688
|
+
for (const replica of slot.replicas) {
|
|
689
|
+
const replicaDeployId = buildReplicaId({ deployId, replica });
|
|
690
|
+
singleReplicaPortOffsets[replicaDeployId] = portOffset;
|
|
691
|
+
portOffset++;
|
|
692
|
+
if (slot.peer) portOffset++;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return resolve(true);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Handle singleReplica operation
|
|
701
|
+
if (options.singleReplica) {
|
|
702
|
+
const replicaPath = path;
|
|
703
|
+
if (!deployId || !host || !replicaPath) {
|
|
704
|
+
logger.error('client --single-replica requires deploy-id, host, and path arguments');
|
|
705
|
+
return reject(false);
|
|
706
|
+
}
|
|
707
|
+
const serverConf = loadReplicas(
|
|
708
|
+
deployId,
|
|
709
|
+
loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
if (serverConf[host][replicaPath].replicas) {
|
|
713
|
+
{
|
|
714
|
+
let replicaIndex = -1;
|
|
715
|
+
for (const replica of serverConf[host][replicaPath].replicas) {
|
|
716
|
+
replicaIndex++;
|
|
717
|
+
const replicaDeployId = `${deployId}-${serverConf[host][replicaPath].replicas[replicaIndex].slice(1)}`;
|
|
718
|
+
await fs.copy(`./engine-private/conf/${deployId}`, `./engine-private/replica/${replicaDeployId}`);
|
|
719
|
+
fs.writeFileSync(
|
|
720
|
+
`./engine-private/replica/${replicaDeployId}/package.json`,
|
|
721
|
+
fs
|
|
722
|
+
.readFileSync(`./engine-private/replica/${replicaDeployId}/package.json`, 'utf8')
|
|
723
|
+
.replaceAll(`${deployId}`, `${replicaDeployId}`),
|
|
724
|
+
'utf8',
|
|
725
|
+
);
|
|
726
|
+
const replicaFolder = `./engine-private/replica/${replicaDeployId}`;
|
|
727
|
+
for (const envFile of ['.env.production', '.env.development', '.env.test']) {
|
|
728
|
+
const envFilePath = `${replicaFolder}/${envFile}`;
|
|
729
|
+
if (fs.existsSync(envFilePath)) {
|
|
730
|
+
fs.writeFileSync(
|
|
731
|
+
envFilePath,
|
|
732
|
+
fs
|
|
733
|
+
.readFileSync(envFilePath, 'utf8')
|
|
734
|
+
.replaceAll(`DEPLOY_ID=${deployId}`, `DEPLOY_ID=${replicaDeployId}`),
|
|
735
|
+
'utf8',
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
{
|
|
742
|
+
let replicaIndex = -1;
|
|
743
|
+
for (const replica of serverConf[host][replicaPath].replicas) {
|
|
744
|
+
replicaIndex++;
|
|
745
|
+
const replicaDeployId = `${deployId}-${serverConf[host][replicaPath].replicas[replicaIndex].slice(1)}`;
|
|
746
|
+
let replicaServerConf = JSON.parse(
|
|
747
|
+
fs.readFileSync(`./engine-private/replica/${replicaDeployId}/conf.server.json`, 'utf8'),
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
const singleReplicaConf = replicaServerConf[host][replicaPath];
|
|
751
|
+
singleReplicaConf.replicas = undefined;
|
|
752
|
+
singleReplicaConf.singleReplica = undefined;
|
|
753
|
+
|
|
754
|
+
replicaServerConf = {};
|
|
755
|
+
replicaServerConf[host] = {};
|
|
756
|
+
replicaServerConf[host][replica] = singleReplicaConf;
|
|
757
|
+
|
|
758
|
+
fs.writeFileSync(
|
|
759
|
+
`./engine-private/replica/${replicaDeployId}/conf.server.json`,
|
|
760
|
+
JSON.stringify(replicaServerConf, null, 4),
|
|
761
|
+
'utf8',
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return resolve(true);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Handle buildFullClient operation (default)
|
|
770
|
+
{
|
|
771
|
+
const { deployId: resolvedDeployId } = loadConf(deployId, subConf ?? '');
|
|
772
|
+
|
|
773
|
+
let argHost = host ? host.split(',') : [];
|
|
774
|
+
let argPath = path ? path.split(',') : [];
|
|
775
|
+
let deployIdSingleReplicas = [];
|
|
776
|
+
let singleReplicaHosts = [];
|
|
777
|
+
const serverConf = resolvedDeployId
|
|
778
|
+
? readConfJson(resolvedDeployId, 'server', { loadReplicas: true })
|
|
779
|
+
: Config.default.server;
|
|
780
|
+
const confFilePath = resolvedDeployId ? getConfFilePath(resolvedDeployId, 'server') : null;
|
|
781
|
+
const originalConfBackup = confFilePath ? fs.readFileSync(confFilePath, 'utf8') : null;
|
|
782
|
+
for (const host of Object.keys(serverConf)) {
|
|
783
|
+
for (const path of Object.keys(serverConf[host])) {
|
|
784
|
+
if (argHost.length && argPath.length && (!argHost.includes(host) || !argPath.includes(path))) {
|
|
785
|
+
delete serverConf[host][path];
|
|
786
|
+
} else {
|
|
787
|
+
serverConf[host][path].liteBuild = false;
|
|
788
|
+
serverConf[host][path].minifyBuild = process.env.NODE_ENV === 'production' ? true : false;
|
|
789
|
+
if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
790
|
+
singleReplicaHosts.push({ host, path });
|
|
791
|
+
deployIdSingleReplicas = deployIdSingleReplicas.concat(
|
|
792
|
+
serverConf[host][path].replicas.map((replica) =>
|
|
793
|
+
buildReplicaId({ deployId: resolvedDeployId, replica }),
|
|
794
|
+
),
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (confFilePath) fs.writeFileSync(confFilePath, JSON.stringify(serverConf, null, 4), 'utf-8');
|
|
801
|
+
await buildClient();
|
|
802
|
+
if (confFilePath && originalConfBackup) fs.writeFileSync(confFilePath, originalConfBackup, 'utf-8');
|
|
803
|
+
|
|
804
|
+
if (singleReplicaHosts.length > 0) {
|
|
805
|
+
for (const { host, path } of singleReplicaHosts) {
|
|
806
|
+
await Underpost.repo.client(resolvedDeployId, '', host, path, {
|
|
807
|
+
singleReplica: true,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
shellExec(`node bin env ${resolvedDeployId} ${process.env.NODE_ENV}`);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
for (const replicaDeployId of deployIdSingleReplicas) {
|
|
814
|
+
shellExec(`node bin env ${replicaDeployId} ${process.env.NODE_ENV}`);
|
|
815
|
+
await Underpost.repo.client(replicaDeployId);
|
|
816
|
+
}
|
|
817
|
+
return resolve(true);
|
|
818
|
+
}
|
|
819
|
+
} catch (error) {
|
|
820
|
+
console.log(error);
|
|
821
|
+
logger.error(error, error.stack);
|
|
822
|
+
return reject(false);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
},
|
|
826
|
+
|
|
575
827
|
/**
|
|
576
828
|
* Gets a list of deleted files from a Git repository.
|
|
577
829
|
* @param {string} [path='.'] - The path to the repository.
|
|
@@ -720,25 +972,14 @@ Prevent build private config repo.`,
|
|
|
720
972
|
DefaultConf.client = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.client.json`, 'utf8'));
|
|
721
973
|
DefaultConf.server = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
722
974
|
DefaultConf.ssr = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.ssr.json`, 'utf8'));
|
|
723
|
-
// DefaultConf.cron = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.cron.json`, 'utf8'));
|
|
724
|
-
|
|
725
|
-
for (const host of Object.keys(DefaultConf.server)) {
|
|
726
|
-
for (const path of Object.keys(DefaultConf.server[host])) {
|
|
727
|
-
DefaultConf.server[host][path].db = defaultServer.db;
|
|
728
|
-
DefaultConf.server[host][path].mailer = defaultServer.mailer;
|
|
729
|
-
|
|
730
|
-
delete DefaultConf.server[host][path]._wp_client;
|
|
731
|
-
delete DefaultConf.server[host][path]._wp_git;
|
|
732
|
-
delete DefaultConf.server[host][path]._wp_directory;
|
|
733
|
-
delete DefaultConf.server[host][path].wp;
|
|
734
|
-
delete DefaultConf.server[host][path].git;
|
|
735
|
-
delete DefaultConf.server[host][path].directory;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
975
|
} else
|
|
739
976
|
logger.warn(
|
|
740
977
|
`Deploy ID configuration not found: ./engine-private/conf/${deployId}, using default configuration.`,
|
|
741
978
|
);
|
|
979
|
+
|
|
980
|
+
// Serialize the configuration into the conf.*.js manifest file.
|
|
981
|
+
// env: references from JSON configs are preserved as 'env:KEY' strings.
|
|
982
|
+
// At runtime, resolveConfSecrets() in conf.js resolves them via process.env.
|
|
742
983
|
const sepRender = '/**/';
|
|
743
984
|
const confRawPaths = fs.readFileSync('./conf.js', 'utf8').split(sepRender);
|
|
744
985
|
confRawPaths[1] = `${JSON.stringify(DefaultConf)};`;
|
|
@@ -924,6 +1165,59 @@ Prevent build private config repo.`,
|
|
|
924
1165
|
|
|
925
1166
|
return copiedFiles;
|
|
926
1167
|
},
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Dispatches a GitHub Actions workflow using gh CLI or curl fallback.
|
|
1171
|
+
* @param {object} options - Dispatch options.
|
|
1172
|
+
* @param {string} options.repo - The GitHub repository (e.g., "owner/repo").
|
|
1173
|
+
* @param {string} options.workflowFile - The workflow file name (e.g., "engine-core.cd.yml").
|
|
1174
|
+
* @param {string} [options.ref='master'] - The git ref to dispatch against.
|
|
1175
|
+
* @param {object} [options.inputs={}] - Key-value inputs for the workflow_dispatch event.
|
|
1176
|
+
* @memberof UnderpostRepository
|
|
1177
|
+
*/
|
|
1178
|
+
dispatchWorkflow(options = { repo: '', workflowFile: '', ref: 'master', inputs: {} }) {
|
|
1179
|
+
const { repo, workflowFile, ref, inputs } = options;
|
|
1180
|
+
const ghAvailable = shellExec('command -v gh 2>/dev/null', {
|
|
1181
|
+
stdout: true,
|
|
1182
|
+
silent: true,
|
|
1183
|
+
disableLog: true,
|
|
1184
|
+
}).trim();
|
|
1185
|
+
|
|
1186
|
+
if (ghAvailable) {
|
|
1187
|
+
let cmd = `gh workflow run ${workflowFile} --repo ${repo} --ref ${ref}`;
|
|
1188
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
1189
|
+
if (value !== undefined && value !== '') {
|
|
1190
|
+
const escaped = String(value).replace(/'/g, "'\\''");
|
|
1191
|
+
cmd += ` -f ${key}='${escaped}'`;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
shellExec(cmd);
|
|
1195
|
+
} else {
|
|
1196
|
+
let token = process.env.GITHUB_TOKEN;
|
|
1197
|
+
if (!token) {
|
|
1198
|
+
const envPath = `${getNpmRootPath()}/underpost/.env`;
|
|
1199
|
+
if (fs.existsSync(envPath) && fs.statSync(envPath).isFile()) {
|
|
1200
|
+
const envVars = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
1201
|
+
token = envVars.GITHUB_TOKEN;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if (!token) {
|
|
1205
|
+
logger.error('GITHUB_TOKEN is required for workflow dispatch (gh CLI not available)');
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
const payload = { ref };
|
|
1209
|
+
if (Object.keys(inputs).length > 0) payload.inputs = inputs;
|
|
1210
|
+
const payloadJson = JSON.stringify(payload).replace(/'/g, "'\\''");
|
|
1211
|
+
shellExec(
|
|
1212
|
+
`curl -s -f -X POST ` +
|
|
1213
|
+
`-H "Accept: application/vnd.github.v3+json" ` +
|
|
1214
|
+
`-H "Authorization: token ${token}" ` +
|
|
1215
|
+
`"https://api.github.com/repos/${repo}/actions/workflows/${workflowFile}/dispatches" ` +
|
|
1216
|
+
`-d '${payloadJson}'`,
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
logger.info('Dispatched workflow', `${repo} -> ${workflowFile}`, inputs.job ? `(job: ${inputs.job})` : '');
|
|
1220
|
+
},
|
|
927
1221
|
};
|
|
928
1222
|
}
|
|
929
1223
|
|