cyberia 3.0.3 → 3.2.5
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 -4
- package/.github/workflows/engine-cyberia.cd.yml +43 -10
- package/.github/workflows/engine-cyberia.ci.yml +48 -26
- package/.github/workflows/ghpkg.ci.yml +5 -5
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/hardhat.ci.yml +82 -0
- package/.github/workflows/npmpkg.ci.yml +60 -14
- package/.github/workflows/publish.ci.yml +26 -7
- package/.github/workflows/publish.cyberia.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -7
- package/.github/workflows/pwa-microservices-template-test.ci.yml +4 -4
- package/.github/workflows/release.cd.yml +14 -8
- package/.vscode/extensions.json +9 -8
- package/.vscode/settings.json +3 -2
- package/CHANGELOG.md +643 -1
- package/CLI-HELP.md +132 -57
- package/Dockerfile +4 -2
- package/README.md +347 -22
- package/WHITE-PAPER.md +1540 -0
- package/bin/build.js +21 -12
- package/bin/cyberia.js +2640 -106
- package/bin/deploy.js +258 -372
- package/bin/file.js +5 -1
- package/bin/index.js +2640 -106
- package/bin/vs.js +3 -3
- package/conf.js +169 -105
- package/deployment.yaml +236 -20
- package/hardhat/.env.example +31 -0
- package/hardhat/README.md +531 -0
- package/hardhat/WHITE-PAPER.md +1540 -0
- package/hardhat/contracts/ObjectLayerToken.sol +391 -0
- package/hardhat/deployments/.gitkeep +0 -0
- package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
- package/hardhat/hardhat.config.js +136 -0
- package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
- package/hardhat/networks/besu-object-layer.network.json +138 -0
- package/hardhat/package-lock.json +4323 -0
- package/hardhat/package.json +36 -0
- package/hardhat/scripts/deployObjectLayerToken.js +98 -0
- package/hardhat/test/ObjectLayerToken.js +592 -0
- package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
- package/hardhat/types/ethers-contracts/common.ts +92 -0
- package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
- package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
- package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
- package/hardhat/types/ethers-contracts/index.ts +6 -0
- package/jsdoc.dd-cyberia.json +68 -0
- package/jsdoc.json +65 -49
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +562 -0
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +297 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +132 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
- package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/manifests/pv-pvc-dd.yaml +1 -1
- package/nodemon.json +1 -1
- package/package.json +60 -48
- package/proxy.yaml +118 -10
- package/pv-pvc.yaml +132 -0
- package/scripts/k3s-node-setup.sh +1 -1
- package/scripts/ports-ls.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +47 -1
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +17 -2
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
- package/src/api/cyberia-map/cyberia-map.model.js +30 -0
- package/src/api/cyberia-map/cyberia-map.router.js +40 -0
- package/src/api/cyberia-map/cyberia-map.service.js +74 -0
- package/src/api/document/document.service.js +1 -1
- package/src/api/file/file.controller.js +3 -1
- package/src/api/file/file.ref.json +18 -0
- package/src/api/file/file.service.js +28 -5
- package/src/api/ipfs/ipfs.controller.js +4 -25
- package/src/api/ipfs/ipfs.model.js +43 -34
- package/src/api/ipfs/ipfs.router.js +8 -13
- package/src/api/ipfs/ipfs.service.js +56 -104
- package/src/api/object-layer/README.md +347 -22
- package/src/api/object-layer/object-layer.controller.js +6 -2
- package/src/api/object-layer/object-layer.model.js +12 -8
- package/src/api/object-layer/object-layer.router.js +698 -42
- package/src/api/object-layer/object-layer.service.js +119 -37
- package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +1 -2
- package/src/api/user/user.router.js +10 -5
- package/src/api/user/user.service.js +15 -14
- package/src/cli/baremetal.js +6 -10
- package/src/cli/cloud-init.js +0 -3
- package/src/cli/cluster.js +7 -7
- package/src/cli/db.js +723 -857
- package/src/cli/deploy.js +215 -105
- package/src/cli/env.js +34 -5
- package/src/cli/fs.js +5 -4
- package/src/cli/image.js +0 -3
- package/src/cli/index.js +83 -15
- package/src/cli/kubectl.js +211 -0
- package/src/cli/monitor.js +5 -6
- package/src/cli/release.js +284 -0
- package/src/cli/repository.js +708 -62
- package/src/cli/run.js +371 -151
- package/src/cli/secrets.js +73 -2
- package/src/cli/ssh.js +1 -1
- package/src/cli/test.js +3 -3
- package/src/client/Cryptokoyn.index.js +3 -4
- package/src/client/CyberiaPortal.index.js +3 -4
- package/src/client/Default.index.js +3 -4
- package/src/client/Itemledger.index.js +4 -963
- package/src/client/Underpost.index.js +3 -4
- package/src/client/components/core/AgGrid.js +20 -5
- package/src/client/components/core/Alert.js +2 -2
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/CalendarCore.js +2 -2
- package/src/client/components/core/Content.js +22 -3
- package/src/client/components/core/Docs.js +30 -6
- package/src/client/components/core/DropDown.js +137 -17
- package/src/client/components/core/FileExplorer.js +71 -4
- package/src/client/components/core/Input.js +1 -1
- package/src/client/components/core/Keyboard.js +2 -2
- package/src/client/components/core/LogIn.js +2 -2
- package/src/client/components/core/LogOut.js +2 -2
- package/src/client/components/core/Modal.js +20 -7
- package/src/client/components/core/Panel.js +0 -1
- package/src/client/components/core/PanelForm.js +19 -19
- package/src/client/components/core/RichText.js +1 -2
- package/src/client/components/core/SocketIo.js +82 -29
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +143 -95
- package/src/client/components/core/Webhook.js +40 -7
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
- package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
- package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +217 -30
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +44 -2
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -4
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +104 -9
- package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/LogInDefault.js +3 -3
- package/src/client/components/default/LogOutDefault.js +2 -2
- package/src/client/components/default/MenuDefault.js +5 -5
- package/src/client/components/default/SocketIoDefault.js +3 -51
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/LogInItemledger.js +3 -3
- package/src/client/components/itemledger/LogOutItemledger.js +2 -2
- package/src/client/components/itemledger/MenuItemledger.js +3 -3
- package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/CssUnderpost.js +59 -0
- package/src/client/components/underpost/LogInUnderpost.js +6 -3
- package/src/client/components/underpost/LogOutUnderpost.js +4 -2
- package/src/client/components/underpost/MenuUnderpost.js +104 -18
- package/src/client/components/underpost/RoutesUnderpost.js +2 -0
- package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
- package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
- package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
- package/src/client/public/cryptokoyn/microdata.json +85 -0
- package/src/client/public/cryptokoyn/site.webmanifest +57 -0
- package/src/client/public/cryptokoyn/sitemap +3 -3
- package/src/client/public/default/sitemap +3 -3
- package/src/client/public/itemledger/browserconfig.xml +2 -2
- package/src/client/public/itemledger/manifest.webmanifest +4 -4
- package/src/client/public/itemledger/microdata.json +71 -0
- package/src/client/public/itemledger/sitemap +3 -3
- package/src/client/public/itemledger/yandex-browser-manifest.json +2 -2
- package/src/client/public/test/sitemap +3 -3
- package/src/client/services/core/core.service.js +20 -8
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
- package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
- package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
- package/src/client/services/instance/instance.management.js +2 -2
- package/src/client/services/ipfs/ipfs.service.js +3 -23
- package/src/client/services/object-layer/object-layer.management.js +3 -3
- package/src/client/services/object-layer/object-layer.service.js +21 -0
- package/src/client/services/user/user.management.js +2 -2
- package/src/client/ssr/body/404.js +15 -11
- package/src/client/ssr/body/500.js +15 -11
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/head/PwaItemledger.js +60 -0
- package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
- package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
- package/src/client/ssr/pages/Test.js +11 -10
- 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/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
- package/src/grpc/cyberia/README.md +326 -0
- package/src/grpc/cyberia/grpc-server.js +530 -0
- package/src/index.js +24 -1
- package/src/proxy.js +0 -3
- package/src/runtime/express/Dockerfile +4 -0
- package/src/runtime/express/Express.js +33 -10
- package/src/runtime/lampp/Dockerfile +13 -2
- package/src/runtime/lampp/Lampp.js +33 -17
- package/src/runtime/wp/Dockerfile +68 -0
- package/src/runtime/wp/Wp.js +639 -0
- package/src/server/auth.js +36 -15
- package/src/server/backup.js +39 -12
- package/src/server/besu-genesis-generator.js +1630 -0
- package/src/server/client-build-docs.js +133 -17
- package/src/server/client-build-live.js +9 -18
- package/src/server/client-build.js +229 -101
- package/src/server/client-dev-server.js +14 -13
- package/src/server/client-formatted.js +109 -57
- package/src/server/conf.js +391 -164
- package/src/server/cron.js +27 -24
- package/src/server/dns.js +29 -12
- package/src/server/downloader.js +0 -2
- package/src/server/ipfs-client.js +24 -1
- package/src/server/logger.js +27 -9
- package/src/server/object-layer.js +217 -103
- package/src/server/peer.js +8 -2
- package/src/server/process.js +1 -50
- package/src/server/proxy.js +4 -8
- package/src/server/runtime.js +30 -9
- package/src/server/semantic-layer-generator-floor.js +359 -0
- package/src/server/semantic-layer-generator-skin.js +1294 -0
- package/src/server/semantic-layer-generator.js +116 -555
- package/src/server/ssr.js +0 -3
- package/src/server/start.js +19 -12
- package/src/server/tls.js +0 -2
- package/src/server.js +0 -4
- package/src/ws/IoInterface.js +1 -10
- package/src/ws/IoServer.js +14 -33
- package/src/ws/core/channels/core.ws.chat.js +65 -20
- package/src/ws/core/channels/core.ws.mailer.js +113 -32
- package/src/ws/core/channels/core.ws.stream.js +90 -31
- package/src/ws/core/core.ws.connection.js +12 -33
- package/src/ws/core/core.ws.emit.js +10 -26
- package/src/ws/core/core.ws.server.js +25 -58
- package/src/ws/default/channels/default.ws.main.js +53 -12
- package/src/ws/default/default.ws.connection.js +26 -13
- package/src/ws/default/default.ws.server.js +30 -12
- package/.env.development +0 -43
- package/.env.test +0 -43
- package/hardhat/contracts/CryptoKoyn.sol +0 -59
- package/hardhat/contracts/ItemLedger.sol +0 -73
- package/hardhat/contracts/Lock.sol +0 -34
- package/hardhat/hardhat.config.cjs +0 -45
- package/hardhat/ignition/modules/Lock.js +0 -18
- package/hardhat/networks/cryptokoyn-itemledger.network.json +0 -29
- package/hardhat/scripts/deployCryptokoyn.cjs +0 -25
- package/hardhat/scripts/deployItemledger.cjs +0 -25
- package/hardhat/test/Lock.js +0 -126
- package/hardhat/white-paper.md +0 -581
- package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
- package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
- package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
- package/src/client/components/default/ElementsDefault.js +0 -38
- package/src/client/components/itemledger/CommonItemledger.js +0 -29
- package/src/client/components/itemledger/ElementsItemledger.js +0 -38
- package/src/client/components/underpost/CommonUnderpost.js +0 -29
- package/src/client/components/underpost/ElementsUnderpost.js +0 -38
- package/src/ws/core/management/core.ws.chat.js +0 -8
- package/src/ws/core/management/core.ws.mailer.js +0 -16
- package/src/ws/core/management/core.ws.stream.js +0 -8
- package/src/ws/default/management/default.ws.main.js +0 -8
- package/white-paper.md +0 -581
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}@` : ''
|
|
@@ -88,6 +97,7 @@ class UnderpostRepository {
|
|
|
88
97
|
* @param {boolean} [options.cached=false] - If true, commits only staged changes.
|
|
89
98
|
* @param {number} [options.log=0] - If greater than 0, shows the last N commits with diffs.
|
|
90
99
|
* @param {boolean} [options.lastMsg=0] - If greater than 0, copies or show the last last single n commit message to clipboard.
|
|
100
|
+
* @param {boolean} [options.unpush=false] - If true with --log, automatically detects unpushed commits ahead of remote and uses that count.
|
|
91
101
|
* @param {string} [options.deployId=''] - An optional deploy ID to include in the commit message.
|
|
92
102
|
* @param {string} [options.hashes=''] - If provided with diff option, shows the diff between two hashes.
|
|
93
103
|
* @param {string} [options.extension=''] - If provided with diff option, filters the diff by this file extension.
|
|
@@ -118,15 +128,64 @@ class UnderpostRepository {
|
|
|
118
128
|
changelogBuild: false,
|
|
119
129
|
changelogMinVersion: '',
|
|
120
130
|
changelogNoHash: false,
|
|
131
|
+
unpush: false,
|
|
132
|
+
b: false,
|
|
133
|
+
p: undefined,
|
|
134
|
+
bc: '',
|
|
135
|
+
isRemoteRepo: '',
|
|
121
136
|
},
|
|
122
137
|
) {
|
|
123
138
|
if (!repoPath) repoPath = '.';
|
|
124
139
|
|
|
140
|
+
if (options.isRemoteRepo) {
|
|
141
|
+
const accessible = Underpost.repo.isRemoteRepo(options.isRemoteRepo);
|
|
142
|
+
console.log(accessible);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (options.bc) {
|
|
147
|
+
console.log(
|
|
148
|
+
shellExec(`cd ${repoPath} && git for-each-ref --contains ${options.bc} --format='%(refname:short)'`, {
|
|
149
|
+
stdout: true,
|
|
150
|
+
silent: true,
|
|
151
|
+
disableLog: true,
|
|
152
|
+
}).trim(),
|
|
153
|
+
);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (options.p !== undefined) {
|
|
158
|
+
const branch =
|
|
159
|
+
options.p === true
|
|
160
|
+
? shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
161
|
+
stdout: true,
|
|
162
|
+
silent: true,
|
|
163
|
+
disableLog: true,
|
|
164
|
+
}).trim()
|
|
165
|
+
: options.p;
|
|
166
|
+
console.log(
|
|
167
|
+
shellExec(`cd ${repoPath} && git --no-pager reflog show refs/heads/${branch}`, {
|
|
168
|
+
stdout: true,
|
|
169
|
+
silent: true,
|
|
170
|
+
disableLog: true,
|
|
171
|
+
}).trim(),
|
|
172
|
+
);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (options.b) {
|
|
177
|
+
const currentBranch = shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
178
|
+
stdout: true,
|
|
179
|
+
silent: true,
|
|
180
|
+
disableLog: true,
|
|
181
|
+
}).trim();
|
|
182
|
+
if (options.copy) pbcopy(currentBranch);
|
|
183
|
+
else console.log(currentBranch);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
125
187
|
if (options.changelog !== undefined || options.changelogBuild) {
|
|
126
|
-
const ciIntegrationPrefix = 'ci(package-pwa-microservices-';
|
|
127
|
-
const ciIntegrationVersionPrefix = 'New release v:';
|
|
128
188
|
const releaseMatch = 'New release v:';
|
|
129
|
-
|
|
130
189
|
// Helper: parse [<tag>] commits into grouped sections
|
|
131
190
|
const buildSectionChangelog = (commits) => {
|
|
132
191
|
const groups = {};
|
|
@@ -253,24 +312,13 @@ class UnderpostRepository {
|
|
|
253
312
|
fs.writeFileSync(changelogPath, `# Changelog\n\n${changelog}`);
|
|
254
313
|
logger.info('CHANGELOG.md built at', changelogPath);
|
|
255
314
|
} else {
|
|
256
|
-
// --changelog [latest-n]: print changelog of last N commits
|
|
315
|
+
// --changelog [latest-n]: print changelog of last N commits (default: 1)
|
|
257
316
|
const hasExplicitCount =
|
|
258
317
|
options.changelog !== undefined && options.changelog !== true && !isNaN(parseInt(options.changelog));
|
|
259
|
-
const scanLimit = hasExplicitCount ? parseInt(options.changelog) :
|
|
318
|
+
const scanLimit = hasExplicitCount ? parseInt(options.changelog) : 1;
|
|
260
319
|
const allCommits = fetchHistory(scanLimit);
|
|
261
320
|
|
|
262
|
-
|
|
263
|
-
if (!hasExplicitCount) {
|
|
264
|
-
// No explicit count: find commits up to the last CI integration boundary
|
|
265
|
-
const ciIndex = allCommits.findIndex(
|
|
266
|
-
(c) => c.message.startsWith(ciIntegrationPrefix) && !c.message.match(ciIntegrationVersionPrefix),
|
|
267
|
-
);
|
|
268
|
-
commits = ciIndex >= 0 ? allCommits.slice(0, ciIndex) : allCommits;
|
|
269
|
-
} else {
|
|
270
|
-
commits = allCommits;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const sections = buildVersionSections(commits);
|
|
321
|
+
const sections = buildVersionSections(allCommits);
|
|
274
322
|
let changelog = renderSections(sections);
|
|
275
323
|
console.log(changelog || `No changelog entries found.\n`);
|
|
276
324
|
}
|
|
@@ -297,17 +345,25 @@ class UnderpostRepository {
|
|
|
297
345
|
else console.log('Diff command:', _diffCmd);
|
|
298
346
|
return;
|
|
299
347
|
}
|
|
300
|
-
if (options.log) {
|
|
301
|
-
|
|
348
|
+
if (options.log || options.unpush) {
|
|
349
|
+
if (options.unpush) {
|
|
350
|
+
const { count, hasUnpushed } = Underpost.repo.getUnpushedCount(repoPath);
|
|
351
|
+
if (!hasUnpushed) {
|
|
352
|
+
logger.warn('No unpushed commits found');
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
options.log = count;
|
|
356
|
+
}
|
|
357
|
+
const history = Underpost.repo.getHistory(options.log, repoPath);
|
|
302
358
|
const chainCmd = history
|
|
303
359
|
.reverse()
|
|
304
|
-
.map((commitData, i) => `${i === 0 ? '' : ' && '}git ${diffCmd} ${commitData.hash}`)
|
|
360
|
+
.map((commitData, i) => `${i === 0 ? '' : ' && '}git -C ${repoPath} ${diffCmd} ${commitData.hash}`)
|
|
305
361
|
.join('');
|
|
306
362
|
if (history[0]) {
|
|
307
363
|
let index = history.length;
|
|
308
364
|
for (const commit of history) {
|
|
309
365
|
console.log(
|
|
310
|
-
shellExec(`git show -s --format=%ci ${commit.hash}`, {
|
|
366
|
+
shellExec(`cd ${repoPath} && git show -s --format=%ci ${commit.hash}`, {
|
|
311
367
|
stdout: true,
|
|
312
368
|
silent: true,
|
|
313
369
|
disableLog: true,
|
|
@@ -316,7 +372,7 @@ class UnderpostRepository {
|
|
|
316
372
|
console.log(`${index}`.magenta, commit.hash.yellow, commit.message);
|
|
317
373
|
index--;
|
|
318
374
|
console.log(
|
|
319
|
-
shellExec(`git show --name-status --pretty="" ${commit.hash}`, {
|
|
375
|
+
shellExec(`cd ${repoPath} && git show --name-status --pretty="" ${commit.hash}`, {
|
|
320
376
|
stdout: true,
|
|
321
377
|
silent: true,
|
|
322
378
|
disableLog: true,
|
|
@@ -360,7 +416,7 @@ class UnderpostRepository {
|
|
|
360
416
|
* Pushes commits to a remote GitHub repository.
|
|
361
417
|
* @param {string} [repoPath='./'] - The local path to the repository.
|
|
362
418
|
* @param {string} [gitUri=`${process.env.GITHUB_USERNAME}/pwa-microservices-template`] - The URI of the GitHub repository.
|
|
363
|
-
* @param {object} [options={ f: false,
|
|
419
|
+
* @param {object} [options={ f: false, g8: false }] - Push options.
|
|
364
420
|
* @param {boolean} [options.f=false] - If true, forces the push.
|
|
365
421
|
* @param {boolean} [options.g8=false] - If true, uses the .g8 extension.
|
|
366
422
|
* @memberof UnderpostRepository
|
|
@@ -368,9 +424,9 @@ class UnderpostRepository {
|
|
|
368
424
|
push(
|
|
369
425
|
repoPath = './',
|
|
370
426
|
gitUri = `${process.env.GITHUB_USERNAME}/pwa-microservices-template`,
|
|
371
|
-
options = { f: false,
|
|
427
|
+
options = { f: false, g8: false },
|
|
372
428
|
) {
|
|
373
|
-
const gExtension = options.
|
|
429
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
374
430
|
shellExec(
|
|
375
431
|
`cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}${gExtension}${
|
|
376
432
|
options?.f === true ? ' --force' : ''
|
|
@@ -403,6 +459,7 @@ class UnderpostRepository {
|
|
|
403
459
|
* @param {boolean} [options.cleanTemplate=false] - If true, cleans the pwa-microservices-template build directory.
|
|
404
460
|
* @param {boolean} [options.build=false] - If true, builds the deployment to pwa-microservices-template (requires deployId).
|
|
405
461
|
* @param {boolean} [options.syncConf=false] - If true, syncs configuration to private repositories (requires deployId).
|
|
462
|
+
* @param {boolean} [options.syncStart=false] - If true, syncs start scripts in deploy ID package.json with root package.json.
|
|
406
463
|
* @param {boolean} [options.defaultConf=false] - If true, updates the default configuration file (requires deployId).
|
|
407
464
|
* @param {string} [options.confWorkflowId=''] - If provided, uses this configuration workflow ID.
|
|
408
465
|
* @returns {Promise<boolean>} A promise that resolves when the initialization is complete.
|
|
@@ -420,6 +477,7 @@ class UnderpostRepository {
|
|
|
420
477
|
cleanTemplate: false,
|
|
421
478
|
build: false,
|
|
422
479
|
syncConf: false,
|
|
480
|
+
syncStart: false,
|
|
423
481
|
defaultConf: false,
|
|
424
482
|
confWorkflowId: '',
|
|
425
483
|
},
|
|
@@ -448,6 +506,13 @@ class UnderpostRepository {
|
|
|
448
506
|
|
|
449
507
|
if (options.deployId) {
|
|
450
508
|
let deployId = options.deployId;
|
|
509
|
+
|
|
510
|
+
// Handle sync-start operation (before dd- prefix normalization to support 'dd' special case)
|
|
511
|
+
if (options.syncStart) {
|
|
512
|
+
shellExec(`node bin/deploy sync-start ${deployId}`);
|
|
513
|
+
return resolve(true);
|
|
514
|
+
}
|
|
515
|
+
|
|
451
516
|
if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
|
|
452
517
|
// Handle purge operation
|
|
453
518
|
if (options.purge) {
|
|
@@ -531,14 +596,9 @@ class UnderpostRepository {
|
|
|
531
596
|
logger.info(`Created repository directory: ${repo.path}`);
|
|
532
597
|
}
|
|
533
598
|
|
|
534
|
-
// Initialize git repository
|
|
535
|
-
shellExec(`cd ${repo.path} && git init`, { disableLog: false });
|
|
536
|
-
logger.info(`Initialized git repository in: ${repo.path}`);
|
|
537
|
-
|
|
538
|
-
// Add remote origin
|
|
539
599
|
const remoteUrl = `https://${token ? `${token}@` : ''}github.com/${username}/${repo.name}.git`;
|
|
540
|
-
|
|
541
|
-
logger.info(`
|
|
600
|
+
UnderpostRepository.API.initLocalRepo({ path: repo.path, origin: remoteUrl });
|
|
601
|
+
logger.info(`Initialized git repository with remote: ${repo.name}`);
|
|
542
602
|
}
|
|
543
603
|
}
|
|
544
604
|
return resolve(true);
|
|
@@ -547,7 +607,6 @@ class UnderpostRepository {
|
|
|
547
607
|
const npmRoot = getNpmRootPath();
|
|
548
608
|
const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
|
|
549
609
|
const destFolder = `./${projectName}`;
|
|
550
|
-
logger.info('Note: This process may take several minutes to complete');
|
|
551
610
|
logger.info('build app', { destFolder });
|
|
552
611
|
if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
|
|
553
612
|
fs.mkdirSync(destFolder, { recursive: true });
|
|
@@ -558,7 +617,8 @@ class UnderpostRepository {
|
|
|
558
617
|
fs.readFileSync(`${underpostRoot}/.dockerignore`, 'utf8'),
|
|
559
618
|
'utf8',
|
|
560
619
|
);
|
|
561
|
-
|
|
620
|
+
UnderpostRepository.API.initLocalRepo({ path: destFolder });
|
|
621
|
+
shellExec(`cd ${destFolder} && git add . && git commit -m "Base template implementation"`);
|
|
562
622
|
}
|
|
563
623
|
shellExec(`cd ${destFolder} && npm run build`);
|
|
564
624
|
shellExec(`cd ${destFolder} && npm run dev`);
|
|
@@ -572,6 +632,216 @@ class UnderpostRepository {
|
|
|
572
632
|
});
|
|
573
633
|
},
|
|
574
634
|
|
|
635
|
+
/**
|
|
636
|
+
* Builds client assets, single replicas, and/or syncs environment ports.
|
|
637
|
+
* @param {string} [deployId='dd-default'] - The deployment ID.
|
|
638
|
+
* @param {string} [subConf=''] - The sub-configuration for the build.
|
|
639
|
+
* @param {string} [host=''] - Comma-separated hosts to filter the build.
|
|
640
|
+
* @param {string} [path=''] - Comma-separated paths to filter the build.
|
|
641
|
+
* @param {object} [options] - Build options.
|
|
642
|
+
* @param {boolean} [options.syncEnvPort=false] - If true, syncs environment port assignments across all deploy IDs.
|
|
643
|
+
* @param {boolean} [options.singleReplica=false] - If true, builds single replica folders instead of full client.
|
|
644
|
+
* @param {boolean} [options.buildZip=false] - If true, creates zip files of the builds.
|
|
645
|
+
* @param {boolean} [options.liteBuild=false] - If true, skips full build (default is full build).
|
|
646
|
+
* @param {boolean} [options.iconsBuild=false] - If true, builds icons.
|
|
647
|
+
* @returns {Promise<boolean>} A promise that resolves when the build is complete.
|
|
648
|
+
* @memberof UnderpostRepository
|
|
649
|
+
*/
|
|
650
|
+
client(
|
|
651
|
+
deployId = 'dd-default',
|
|
652
|
+
subConf = '',
|
|
653
|
+
host = '',
|
|
654
|
+
path = '',
|
|
655
|
+
options = {
|
|
656
|
+
syncEnvPort: false,
|
|
657
|
+
singleReplica: false,
|
|
658
|
+
buildZip: false,
|
|
659
|
+
liteBuild: false,
|
|
660
|
+
iconsBuild: false,
|
|
661
|
+
},
|
|
662
|
+
) {
|
|
663
|
+
return new Promise(async (resolve, reject) => {
|
|
664
|
+
try {
|
|
665
|
+
// Handle singleReplica operation (must run before syncEnvPort to ensure replica dirs exist)
|
|
666
|
+
if (options.singleReplica) {
|
|
667
|
+
const replicaPath = path;
|
|
668
|
+
if (!deployId || !host || !replicaPath) {
|
|
669
|
+
logger.error('client --single-replica requires deploy-id, host, and path arguments');
|
|
670
|
+
return reject(false);
|
|
671
|
+
}
|
|
672
|
+
const serverConf = loadReplicas(
|
|
673
|
+
deployId,
|
|
674
|
+
loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
if (serverConf[host][replicaPath].replicas) {
|
|
678
|
+
{
|
|
679
|
+
let replicaIndex = -1;
|
|
680
|
+
for (const replica of serverConf[host][replicaPath].replicas) {
|
|
681
|
+
replicaIndex++;
|
|
682
|
+
const replicaDeployId = `${deployId}-${serverConf[host][replicaPath].replicas[replicaIndex].slice(1)}`;
|
|
683
|
+
await fs.copy(`./engine-private/conf/${deployId}`, `./engine-private/replica/${replicaDeployId}`);
|
|
684
|
+
fs.writeFileSync(
|
|
685
|
+
`./engine-private/replica/${replicaDeployId}/package.json`,
|
|
686
|
+
fs
|
|
687
|
+
.readFileSync(`./engine-private/replica/${replicaDeployId}/package.json`, 'utf8')
|
|
688
|
+
.replaceAll(`${deployId}`, `${replicaDeployId}`),
|
|
689
|
+
'utf8',
|
|
690
|
+
);
|
|
691
|
+
const replicaFolder = `./engine-private/replica/${replicaDeployId}`;
|
|
692
|
+
for (const envFile of ['.env.production', '.env.development', '.env.test']) {
|
|
693
|
+
const envFilePath = `${replicaFolder}/${envFile}`;
|
|
694
|
+
if (fs.existsSync(envFilePath)) {
|
|
695
|
+
fs.writeFileSync(
|
|
696
|
+
envFilePath,
|
|
697
|
+
fs
|
|
698
|
+
.readFileSync(envFilePath, 'utf8')
|
|
699
|
+
.replaceAll(`DEPLOY_ID=${deployId}`, `DEPLOY_ID=${replicaDeployId}`),
|
|
700
|
+
'utf8',
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
{
|
|
707
|
+
let replicaIndex = -1;
|
|
708
|
+
for (const replica of serverConf[host][replicaPath].replicas) {
|
|
709
|
+
replicaIndex++;
|
|
710
|
+
const replicaDeployId = `${deployId}-${serverConf[host][replicaPath].replicas[replicaIndex].slice(1)}`;
|
|
711
|
+
let replicaServerConf = JSON.parse(
|
|
712
|
+
fs.readFileSync(`./engine-private/replica/${replicaDeployId}/conf.server.json`, 'utf8'),
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
const singleReplicaConf = replicaServerConf[host][replicaPath];
|
|
716
|
+
singleReplicaConf.replicas = undefined;
|
|
717
|
+
singleReplicaConf.singleReplica = undefined;
|
|
718
|
+
|
|
719
|
+
replicaServerConf = {};
|
|
720
|
+
replicaServerConf[host] = {};
|
|
721
|
+
replicaServerConf[host][replica] = singleReplicaConf;
|
|
722
|
+
|
|
723
|
+
fs.writeFileSync(
|
|
724
|
+
`./engine-private/replica/${replicaDeployId}/conf.server.json`,
|
|
725
|
+
JSON.stringify(replicaServerConf, null, 4),
|
|
726
|
+
'utf8',
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (!options.syncEnvPort) return resolve(true);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Handle syncEnvPort operation
|
|
735
|
+
if (options.syncEnvPort) {
|
|
736
|
+
const dataDeploy = await getDataDeploy({ disableSyncEnvPort: true });
|
|
737
|
+
const dataEnv = [
|
|
738
|
+
{ env: 'production', port: 3000 },
|
|
739
|
+
{ env: 'development', port: 4000 },
|
|
740
|
+
{ env: 'test', port: 5000 },
|
|
741
|
+
];
|
|
742
|
+
let portOffset = 0;
|
|
743
|
+
const singleReplicaPortOffsets = {};
|
|
744
|
+
for (const deployIdObj of dataDeploy) {
|
|
745
|
+
const { deployId } = deployIdObj;
|
|
746
|
+
const baseConfPath = fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
747
|
+
? `./engine-private/replica`
|
|
748
|
+
: `./engine-private/conf`;
|
|
749
|
+
|
|
750
|
+
const effectivePortOffset =
|
|
751
|
+
singleReplicaPortOffsets[deployId] !== undefined ? singleReplicaPortOffsets[deployId] : portOffset;
|
|
752
|
+
|
|
753
|
+
let skipDeploy = false;
|
|
754
|
+
for (const envInstanceObj of dataEnv) {
|
|
755
|
+
const envPath = `${baseConfPath}/${deployId}/.env.${envInstanceObj.env}`;
|
|
756
|
+
if (!fs.existsSync(envPath)) {
|
|
757
|
+
logger.warn(`Skipping ${deployId}: ${envPath} not found`);
|
|
758
|
+
skipDeploy = true;
|
|
759
|
+
break;
|
|
760
|
+
}
|
|
761
|
+
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
762
|
+
envObj.PORT = `${envInstanceObj.port + effectivePortOffset}`;
|
|
763
|
+
writeEnv(envPath, envObj);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (skipDeploy) continue;
|
|
767
|
+
if (singleReplicaPortOffsets[deployId] !== undefined) continue;
|
|
768
|
+
|
|
769
|
+
const serverConf = loadReplicas(
|
|
770
|
+
deployId,
|
|
771
|
+
loadConfServerJson(`${baseConfPath}/${deployId}/conf.server.json`),
|
|
772
|
+
);
|
|
773
|
+
for (const host of Object.keys(serverConf)) {
|
|
774
|
+
let deferredSingleReplicaSlots = [];
|
|
775
|
+
for (const path of Object.keys(serverConf[host])) {
|
|
776
|
+
if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
777
|
+
deferredSingleReplicaSlots.push({
|
|
778
|
+
replicas: serverConf[host][path].replicas,
|
|
779
|
+
peer: !!serverConf[host][path].peer,
|
|
780
|
+
});
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
portOffset++;
|
|
784
|
+
if (serverConf[host][path].peer) portOffset++;
|
|
785
|
+
}
|
|
786
|
+
for (const slot of deferredSingleReplicaSlots) {
|
|
787
|
+
for (const replica of slot.replicas) {
|
|
788
|
+
const replicaDeployId = buildReplicaId({ deployId, replica });
|
|
789
|
+
singleReplicaPortOffsets[replicaDeployId] = portOffset;
|
|
790
|
+
portOffset++;
|
|
791
|
+
if (slot.peer) portOffset++;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return resolve(true);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Handle buildFullClient operation (default)
|
|
800
|
+
{
|
|
801
|
+
const { deployId: resolvedDeployId } = loadConf(deployId, subConf ?? '');
|
|
802
|
+
|
|
803
|
+
let argHost = host ? host.split(',') : [];
|
|
804
|
+
let argPath = path ? path.split(',') : [];
|
|
805
|
+
let deployIdSingleReplicas = [];
|
|
806
|
+
let singleReplicaHosts = [];
|
|
807
|
+
const serverConf = resolvedDeployId
|
|
808
|
+
? readConfJson(resolvedDeployId, 'server', { loadReplicas: true })
|
|
809
|
+
: Config.default.server;
|
|
810
|
+
const confFilePath = resolvedDeployId ? getConfFilePath(resolvedDeployId, 'server') : null;
|
|
811
|
+
const originalConfBackup = confFilePath ? fs.readFileSync(confFilePath, 'utf8') : null;
|
|
812
|
+
for (const host of Object.keys(serverConf)) {
|
|
813
|
+
for (const path of Object.keys(serverConf[host])) {
|
|
814
|
+
if (argHost.length && argPath.length && (!argHost.includes(host) || !argPath.includes(path))) {
|
|
815
|
+
delete serverConf[host][path];
|
|
816
|
+
} else {
|
|
817
|
+
if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
818
|
+
singleReplicaHosts.push({ host, path });
|
|
819
|
+
deployIdSingleReplicas = deployIdSingleReplicas.concat(
|
|
820
|
+
serverConf[host][path].replicas.map((replica) =>
|
|
821
|
+
buildReplicaId({ deployId: resolvedDeployId, replica }),
|
|
822
|
+
),
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
await buildClient({
|
|
829
|
+
buildZip: options.buildZip || false,
|
|
830
|
+
fullBuild: options.liteBuild ? false : true,
|
|
831
|
+
iconsBuild: options.iconsBuild || false,
|
|
832
|
+
});
|
|
833
|
+
for (const replicaDeployId of deployIdSingleReplicas) await Underpost.repo.client(replicaDeployId);
|
|
834
|
+
|
|
835
|
+
return resolve(true);
|
|
836
|
+
}
|
|
837
|
+
} catch (error) {
|
|
838
|
+
console.log(error);
|
|
839
|
+
logger.error(error, error.stack);
|
|
840
|
+
return reject(false);
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
},
|
|
844
|
+
|
|
575
845
|
/**
|
|
576
846
|
* Gets a list of deleted files from a Git repository.
|
|
577
847
|
* @param {string} [path='.'] - The path to the repository.
|
|
@@ -650,8 +920,8 @@ Prevent build private config repo.`,
|
|
|
650
920
|
* @returns {Array<{hash: string, message: string, files: string}>} An array of commit objects with hash, message, and files.
|
|
651
921
|
* @memberof UnderpostRepository
|
|
652
922
|
*/
|
|
653
|
-
getHistory(sinceCommit = 1) {
|
|
654
|
-
return shellExec(`git log -1 --pretty=format:"%h %s" -n ${sinceCommit}`, {
|
|
923
|
+
getHistory(sinceCommit = 1, repoPath = '.') {
|
|
924
|
+
return shellExec(`cd ${repoPath} && git log -1 --pretty=format:"%h %s" -n ${sinceCommit}`, {
|
|
655
925
|
stdout: true,
|
|
656
926
|
silent: true,
|
|
657
927
|
disableLog: true,
|
|
@@ -666,7 +936,7 @@ Prevent build private config repo.`,
|
|
|
666
936
|
})
|
|
667
937
|
.filter((line) => line.hash)
|
|
668
938
|
.map((line) => {
|
|
669
|
-
line.files = shellExec(`git show --name-status --pretty="" ${line.hash}`, {
|
|
939
|
+
line.files = shellExec(`cd ${repoPath} && git show --name-status --pretty="" ${line.hash}`, {
|
|
670
940
|
stdout: true,
|
|
671
941
|
silent: true,
|
|
672
942
|
disableLog: true,
|
|
@@ -720,25 +990,14 @@ Prevent build private config repo.`,
|
|
|
720
990
|
DefaultConf.client = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.client.json`, 'utf8'));
|
|
721
991
|
DefaultConf.server = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
|
|
722
992
|
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
993
|
} else
|
|
739
994
|
logger.warn(
|
|
740
995
|
`Deploy ID configuration not found: ./engine-private/conf/${deployId}, using default configuration.`,
|
|
741
996
|
);
|
|
997
|
+
|
|
998
|
+
// Serialize the configuration into the conf.*.js manifest file.
|
|
999
|
+
// env: references from JSON configs are preserved as 'env:KEY' strings.
|
|
1000
|
+
// At runtime, resolveConfSecrets() in conf.js resolves them via process.env.
|
|
742
1001
|
const sepRender = '/**/';
|
|
743
1002
|
const confRawPaths = fs.readFileSync('./conf.js', 'utf8').split(sepRender);
|
|
744
1003
|
confRawPaths[1] = `${JSON.stringify(DefaultConf)};`;
|
|
@@ -924,6 +1183,393 @@ Prevent build private config repo.`,
|
|
|
924
1183
|
|
|
925
1184
|
return copiedFiles;
|
|
926
1185
|
},
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Dispatches a GitHub Actions workflow using gh CLI or curl fallback.
|
|
1189
|
+
* @param {object} options - Dispatch options.
|
|
1190
|
+
* @param {string} options.repo - The GitHub repository (e.g., "owner/repo").
|
|
1191
|
+
* @param {string} options.workflowFile - The workflow file name (e.g., "engine-core.cd.yml").
|
|
1192
|
+
* @param {string} [options.ref='master'] - The git ref to dispatch against.
|
|
1193
|
+
* @param {object} [options.inputs={}] - Key-value inputs for the workflow_dispatch event.
|
|
1194
|
+
* @memberof UnderpostRepository
|
|
1195
|
+
*/
|
|
1196
|
+
dispatchWorkflow(options = { repo: '', workflowFile: '', ref: 'master', inputs: {} }) {
|
|
1197
|
+
const { repo, workflowFile, ref, inputs } = options;
|
|
1198
|
+
const ghAvailable = shellExec('command -v gh 2>/dev/null', {
|
|
1199
|
+
stdout: true,
|
|
1200
|
+
silent: true,
|
|
1201
|
+
disableLog: true,
|
|
1202
|
+
}).trim();
|
|
1203
|
+
|
|
1204
|
+
if (ghAvailable) {
|
|
1205
|
+
let cmd = `gh workflow run ${workflowFile} --repo ${repo} --ref ${ref}`;
|
|
1206
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
1207
|
+
if (value !== undefined && value !== '') {
|
|
1208
|
+
const escaped = String(value).replace(/'/g, "'\\''");
|
|
1209
|
+
cmd += ` -f ${key}='${escaped}'`;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
shellExec(cmd);
|
|
1213
|
+
} else {
|
|
1214
|
+
let token = process.env.GITHUB_TOKEN;
|
|
1215
|
+
if (!token) {
|
|
1216
|
+
const envPath = `${getNpmRootPath()}/underpost/.env`;
|
|
1217
|
+
if (fs.existsSync(envPath) && fs.statSync(envPath).isFile()) {
|
|
1218
|
+
const envVars = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
1219
|
+
token = envVars.GITHUB_TOKEN;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (!token) {
|
|
1223
|
+
logger.error('GITHUB_TOKEN is required for workflow dispatch (gh CLI not available)');
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const payload = { ref };
|
|
1227
|
+
if (Object.keys(inputs).length > 0) payload.inputs = inputs;
|
|
1228
|
+
const payloadJson = JSON.stringify(payload).replace(/'/g, "'\\''");
|
|
1229
|
+
shellExec(
|
|
1230
|
+
`curl -s -f -X POST ` +
|
|
1231
|
+
`-H "Accept: application/vnd.github.v3+json" ` +
|
|
1232
|
+
`-H "Authorization: token ${token}" ` +
|
|
1233
|
+
`"https://api.github.com/repos/${repo}/actions/workflows/${workflowFile}/dispatches" ` +
|
|
1234
|
+
`-d '${payloadJson}'`,
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
logger.info('Dispatched workflow', `${repo} -> ${workflowFile}`, inputs.job ? `(job: ${inputs.job})` : '');
|
|
1238
|
+
},
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Returns metadata about unpushed commits in a git repository.
|
|
1242
|
+
* Fetches from origin, then counts commits ahead of the remote branch.
|
|
1243
|
+
* @param {string} [repoPath='.'] - Path to the git repository.
|
|
1244
|
+
* @param {number} [fallback=1] - Value to return as `count` when no unpushed commits are detected.
|
|
1245
|
+
* @returns {{ count: number, branch: string, hasUnpushed: boolean }} Unpush metadata.
|
|
1246
|
+
* @memberof UnderpostRepository
|
|
1247
|
+
*/
|
|
1248
|
+
/**
|
|
1249
|
+
* Checks whether a remote Git repository URL is reachable.
|
|
1250
|
+
* Uses `git ls-remote` with `|| true` so the process always exits 0.
|
|
1251
|
+
* Injects `GITHUB_TOKEN` into GitHub HTTPS URLs when available.
|
|
1252
|
+
* @param {string} url - Full HTTPS clone URL to test (e.g. "https://github.com/org/repo.git").
|
|
1253
|
+
* @returns {boolean} `true` when the remote responded with at least one ref hash.
|
|
1254
|
+
* @memberof UnderpostRepository
|
|
1255
|
+
*/
|
|
1256
|
+
resolveAuthUrl(url) {
|
|
1257
|
+
if (!url) return url;
|
|
1258
|
+
// Normalize short form "owner/repo" → full GitHub HTTPS URL
|
|
1259
|
+
let normalized = url;
|
|
1260
|
+
if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('git@')) {
|
|
1261
|
+
normalized = `https://github.com/${url}`;
|
|
1262
|
+
}
|
|
1263
|
+
if (process.env.GITHUB_TOKEN && normalized.startsWith('https://github.com/')) {
|
|
1264
|
+
return normalized.replace(
|
|
1265
|
+
'https://github.com/',
|
|
1266
|
+
`https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/`,
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
return normalized;
|
|
1270
|
+
},
|
|
1271
|
+
|
|
1272
|
+
isRemoteRepo(url) {
|
|
1273
|
+
if (!url) return false;
|
|
1274
|
+
const authUrl = Underpost.repo.resolveAuthUrl(url);
|
|
1275
|
+
// GIT_TERMINAL_PROMPT=0 prevents git from hanging on credential prompts inside containers.
|
|
1276
|
+
const raw = shellExec(`GIT_TERMINAL_PROMPT=0 git ls-remote "${authUrl}" HEAD 2>&1 || true`, {
|
|
1277
|
+
stdout: true,
|
|
1278
|
+
silent: true,
|
|
1279
|
+
disableLog: true,
|
|
1280
|
+
});
|
|
1281
|
+
logger.info('isRemoteRepo', { url, raw: (raw || '').slice(0, 120) });
|
|
1282
|
+
return typeof raw === 'string' && /^[0-9a-f]{40}\t/m.test(raw);
|
|
1283
|
+
},
|
|
1284
|
+
|
|
1285
|
+
getUnpushedCount(repoPath = '.', fallback = 1) {
|
|
1286
|
+
const branch = shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
1287
|
+
stdout: true,
|
|
1288
|
+
silent: true,
|
|
1289
|
+
disableLog: true,
|
|
1290
|
+
}).trim();
|
|
1291
|
+
shellExec(`cd ${repoPath} && git fetch origin 2>/dev/null`, { silent: true, disableLog: true });
|
|
1292
|
+
const raw = shellExec(`cd ${repoPath} && git rev-list --count origin/${branch}..HEAD 2>/dev/null`, {
|
|
1293
|
+
stdout: true,
|
|
1294
|
+
silent: true,
|
|
1295
|
+
disableLog: true,
|
|
1296
|
+
}).trim();
|
|
1297
|
+
const count = parseInt(raw);
|
|
1298
|
+
const hasUnpushed = !isNaN(count) && count > 0;
|
|
1299
|
+
return { count: hasUnpushed ? count : fallback, branch, hasUnpushed };
|
|
1300
|
+
},
|
|
1301
|
+
|
|
1302
|
+
/**
|
|
1303
|
+
* Sanitizes a markdown changelog string into a compact message format.
|
|
1304
|
+
* Strips date headers, converts section tags to `[tag]` prefixes, removes bullet markers and special characters.
|
|
1305
|
+
* @param {string} message - The raw markdown changelog output.
|
|
1306
|
+
* @returns {string} The sanitized single-line or multi-line compact message.
|
|
1307
|
+
* @memberof UnderpostRepository
|
|
1308
|
+
*/
|
|
1309
|
+
sanitizeChangelogMessage(message) {
|
|
1310
|
+
if (!message) return '';
|
|
1311
|
+
return message
|
|
1312
|
+
.replace(/^##\s+\d{4}-\d{2}-\d{2}\s*/gm, '')
|
|
1313
|
+
.replace(/^###\s+(\S+)\s*/gm, '[$1] ')
|
|
1314
|
+
.replace(/^- /gm, '')
|
|
1315
|
+
.replaceAll('"', '')
|
|
1316
|
+
.replaceAll('`', '')
|
|
1317
|
+
.split('\n')
|
|
1318
|
+
.map((l) => l.trim())
|
|
1319
|
+
.filter(Boolean)
|
|
1320
|
+
.join('\n')
|
|
1321
|
+
.trim()
|
|
1322
|
+
.replaceAll('] - ', '] ');
|
|
1323
|
+
},
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* Manages a cron-backup Git repository: clone, pull, commit, or push.
|
|
1327
|
+
* Resolves the repository path as `../<repoName>` relative to the CWD.
|
|
1328
|
+
* Requires the `GITHUB_USERNAME` environment variable to be set.
|
|
1329
|
+
* @param {object} params
|
|
1330
|
+
* @param {string} params.repoName - Repository name (e.g. `engine-cyberia-cron-backups`).
|
|
1331
|
+
* @param {'clone'|'pull'|'commit'|'push'} params.operation - Git operation to perform.
|
|
1332
|
+
* @param {string} [params.message=''] - Commit message (used by the `commit` operation).
|
|
1333
|
+
* @param {boolean} [params.forceClone=false] - Remove existing clone before re-cloning.
|
|
1334
|
+
* @returns {boolean} `true` on success, `false` if GITHUB_USERNAME is unset or on error.
|
|
1335
|
+
* @memberof UnderpostRepository
|
|
1336
|
+
*/
|
|
1337
|
+
/**
|
|
1338
|
+
* Initializes a git repository at the given path and configures user identity
|
|
1339
|
+
* from environment variables (`GITHUB_USERNAME` / `GITHUB_EMAIL`).
|
|
1340
|
+
* Safe to call on an already-initialized repo — only runs `git init` when
|
|
1341
|
+
* `.git` is absent and always ensures user.name / user.email are set.
|
|
1342
|
+
* @param {object} opts
|
|
1343
|
+
* @param {string} opts.path - Absolute or relative path to the repository.
|
|
1344
|
+
* @param {string} [opts.origin] - If provided, sets or updates git remote `origin`.
|
|
1345
|
+
* @memberof UnderpostRepository
|
|
1346
|
+
*/
|
|
1347
|
+
initLocalRepo({ path: repoPath, origin }) {
|
|
1348
|
+
const gitUsername = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
1349
|
+
const gitEmail = process.env.GITHUB_EMAIL || `development@underpost.net`;
|
|
1350
|
+
|
|
1351
|
+
if (!fs.existsSync(`${repoPath}/.git`)) {
|
|
1352
|
+
shellExec(`cd "${repoPath}" && git init`);
|
|
1353
|
+
}
|
|
1354
|
+
shellExec(`cd "${repoPath}" && git config user.name '${gitUsername}'`);
|
|
1355
|
+
shellExec(`cd "${repoPath}" && git config user.email '${gitEmail}'`);
|
|
1356
|
+
|
|
1357
|
+
if (origin) {
|
|
1358
|
+
const currentRemote = shellExec(`cd "${repoPath}" && git remote get-url origin 2>/dev/null || true`, {
|
|
1359
|
+
stdout: true,
|
|
1360
|
+
silent: true,
|
|
1361
|
+
}).trim();
|
|
1362
|
+
if (!currentRemote) {
|
|
1363
|
+
shellExec(`cd "${repoPath}" && git remote add origin "${origin}"`);
|
|
1364
|
+
} else if (currentRemote !== origin) {
|
|
1365
|
+
shellExec(`cd "${repoPath}" && git remote set-url origin "${origin}"`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
|
|
1370
|
+
manageBackupRepo({ repoName, operation, message = '', forceClone = false }) {
|
|
1371
|
+
try {
|
|
1372
|
+
const username = process.env.GITHUB_USERNAME;
|
|
1373
|
+
if (!username) {
|
|
1374
|
+
logger.error('GITHUB_USERNAME environment variable not set');
|
|
1375
|
+
return false;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
const repoPath = `../${repoName}`;
|
|
1379
|
+
|
|
1380
|
+
switch (operation) {
|
|
1381
|
+
case 'clone':
|
|
1382
|
+
if (forceClone && fs.existsSync(repoPath)) {
|
|
1383
|
+
logger.info(`Force clone: removing existing repository: ${repoName}`);
|
|
1384
|
+
fs.removeSync(repoPath);
|
|
1385
|
+
}
|
|
1386
|
+
if (!fs.existsSync(repoPath)) {
|
|
1387
|
+
shellExec(`cd .. && underpost clone ${username}/${repoName}`);
|
|
1388
|
+
logger.info(`Cloned repository: ${repoName}`);
|
|
1389
|
+
}
|
|
1390
|
+
break;
|
|
1391
|
+
|
|
1392
|
+
case 'pull':
|
|
1393
|
+
if (fs.existsSync(repoPath)) {
|
|
1394
|
+
shellExec(`cd ${repoPath} && git checkout . && git clean -f -d`);
|
|
1395
|
+
shellExec(`cd ${repoPath} && underpost pull . ${username}/${repoName}`, { silent: true });
|
|
1396
|
+
logger.info(`Pulled repository: ${repoName}`);
|
|
1397
|
+
}
|
|
1398
|
+
break;
|
|
1399
|
+
|
|
1400
|
+
case 'commit':
|
|
1401
|
+
if (fs.existsSync(repoPath)) {
|
|
1402
|
+
shellExec(`cd ${repoPath} && git add .`);
|
|
1403
|
+
shellExec(`underpost cmt ${repoPath} backup '' '${message}'`);
|
|
1404
|
+
logger.info(`Committed to repository: ${repoName}`, { message });
|
|
1405
|
+
}
|
|
1406
|
+
break;
|
|
1407
|
+
|
|
1408
|
+
case 'push':
|
|
1409
|
+
if (fs.existsSync(repoPath)) {
|
|
1410
|
+
shellExec(`cd ${repoPath} && underpost push . ${username}/${repoName}`, { silent: true });
|
|
1411
|
+
logger.info(`Pushed repository: ${repoName}`);
|
|
1412
|
+
}
|
|
1413
|
+
break;
|
|
1414
|
+
|
|
1415
|
+
default:
|
|
1416
|
+
logger.warn(`Unknown git operation: ${operation}`);
|
|
1417
|
+
return false;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
return true;
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
logger.error(`Git operation failed`, { repoName, operation, error: error.message });
|
|
1423
|
+
return false;
|
|
1424
|
+
}
|
|
1425
|
+
},
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Runtime-type → in-pod site-root directory resolver.
|
|
1429
|
+
* Maps each known runtime to the filesystem path where its repository lives inside the container.
|
|
1430
|
+
* @param {string} runtime - The runtime identifier (e.g. 'wp').
|
|
1431
|
+
* @param {string} host - The virtual-host name.
|
|
1432
|
+
* @returns {string|null} Absolute path inside the pod, or null if the runtime has no known mapping.
|
|
1433
|
+
* @memberof UnderpostRepository
|
|
1434
|
+
*/
|
|
1435
|
+
runtimeSiteRoot(runtime, host) {
|
|
1436
|
+
const runtimePaths = {
|
|
1437
|
+
wp: `/opt/lampp/htdocs/wp/${host}`,
|
|
1438
|
+
};
|
|
1439
|
+
return runtimePaths[runtime] || null;
|
|
1440
|
+
},
|
|
1441
|
+
|
|
1442
|
+
/**
|
|
1443
|
+
* Backs up all repositories defined in a deployment's conf.server.json by executing
|
|
1444
|
+
* git commit+push inside the running deployment pod via `kubectl exec`.
|
|
1445
|
+
*
|
|
1446
|
+
* Scans every `server[host][path]` entry for a `repository` field. For each match
|
|
1447
|
+
* the runtime-specific site root is resolved and a git backup script is executed
|
|
1448
|
+
* inside the pod. GITHUB_TOKEN and GITHUB_USERNAME are injected as ephemeral
|
|
1449
|
+
* environment variables in the exec command — never persisted to the pod filesystem.
|
|
1450
|
+
*
|
|
1451
|
+
* @param {object} opts
|
|
1452
|
+
* @param {string} opts.deployId - Deployment ID (used to read conf.server.json and find pods).
|
|
1453
|
+
* @param {string} [opts.namespace='default'] - Kubernetes namespace.
|
|
1454
|
+
* @param {string} [opts.env='production'] - Deployment environment.
|
|
1455
|
+
* @returns {void}
|
|
1456
|
+
* @memberof UnderpostRepository
|
|
1457
|
+
*/
|
|
1458
|
+
backupPodRepositories({ deployId, namespace = 'default', env = 'production' }) {
|
|
1459
|
+
const confServer = readConfJson(deployId, 'server', { resolve: true });
|
|
1460
|
+
const githubToken = process.env.GITHUB_TOKEN || '';
|
|
1461
|
+
const githubUsername = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
1462
|
+
|
|
1463
|
+
if (!githubToken) {
|
|
1464
|
+
logger.warn('backupPodRepositories: GITHUB_TOKEN not available — git push will fail');
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// Resolve the active blue/green traffic colour so we target the correct pod
|
|
1468
|
+
const traffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace });
|
|
1469
|
+
if (!traffic) {
|
|
1470
|
+
logger.warn(`backupPodRepositories: could not resolve current traffic for ${deployId} — skipping`);
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// Find a running pod that matches the active traffic colour
|
|
1475
|
+
const pods = Underpost.kubectl.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
|
|
1476
|
+
const runningPod = pods.find((p) => p.STATUS === 'Running');
|
|
1477
|
+
if (!runningPod) {
|
|
1478
|
+
logger.warn(`backupPodRepositories: no running ${traffic} pod found for ${deployId} in namespace ${namespace}`);
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
const podName = runningPod.NAME;
|
|
1482
|
+
|
|
1483
|
+
for (const host of Object.keys(confServer)) {
|
|
1484
|
+
for (const routePath of Object.keys(confServer[host])) {
|
|
1485
|
+
const entry = confServer[host][routePath];
|
|
1486
|
+
if (!entry.repository) continue;
|
|
1487
|
+
|
|
1488
|
+
const siteRoot = Underpost.repo.runtimeSiteRoot(entry.runtime, host);
|
|
1489
|
+
if (!siteRoot) {
|
|
1490
|
+
logger.warn(`backupPodRepositories: no site-root mapping for runtime '${entry.runtime}' (${host})`);
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
const repoName = entry.repository.split('/').pop().split('.')[0];
|
|
1495
|
+
|
|
1496
|
+
// Build the backup script — secrets are injected as env vars in the exec,
|
|
1497
|
+
// never written to filesystem. The shell process inherits them ephemerally.
|
|
1498
|
+
const backupScript = [
|
|
1499
|
+
`export GITHUB_TOKEN='${githubToken.replace(/'/g, "'\\''")}'`,
|
|
1500
|
+
`export GITHUB_USERNAME='${githubUsername.replace(/'/g, "'\\''")}'`,
|
|
1501
|
+
`git config --global --add safe.directory '${siteRoot}' 2>/dev/null || true`,
|
|
1502
|
+
`cd '${siteRoot}' && git add -A && git commit -m 'backup $(date -u +%Y-%m-%dT%H:%M:%SZ)' || true`,
|
|
1503
|
+
`cd '${siteRoot}' && underpost push . ${githubUsername}/${repoName}`,
|
|
1504
|
+
`cd /home/dd/engine && node bin secret underpost --global-clean`,
|
|
1505
|
+
].join(' && ');
|
|
1506
|
+
|
|
1507
|
+
try {
|
|
1508
|
+
logger.info(`backupPodRepositories: backing up ${host} (${entry.runtime}) in pod ${podName}`);
|
|
1509
|
+
Underpost.kubectl.exec({ podName, namespace, command: backupScript });
|
|
1510
|
+
logger.info(`backupPodRepositories: git push done for ${host}`);
|
|
1511
|
+
} catch (err) {
|
|
1512
|
+
logger.error(`backupPodRepositories: backup failed for ${host}`, err.message);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
},
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* Clones the deploy-specific private repository into `./engine-private`
|
|
1520
|
+
* when it does not already exist on disk. Returns `{ ephemeral: true }`
|
|
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.
|
|
1524
|
+
*
|
|
1525
|
+
* @param {string} [deployId] - Deploy ID (e.g. `dd-core`) used to derive
|
|
1526
|
+
* the repo name `engine-{component}-private`. Falls back to
|
|
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.
|
|
1532
|
+
* @memberof UnderpostRepository
|
|
1533
|
+
*/
|
|
1534
|
+
privateEngineRepoFactory(deployId, options = { force: false }) {
|
|
1535
|
+
if (fs.existsSync('./engine-private') && !options.force) return;
|
|
1536
|
+
|
|
1537
|
+
if (options.force && fs.existsSync('./engine-private')) {
|
|
1538
|
+
fs.removeSync('./engine-private');
|
|
1539
|
+
logger.info('engine-private removed (force re-clone)');
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
const effectiveDeployId = deployId || process.env.DEFAULT_DEPLOY_ID;
|
|
1543
|
+
|
|
1544
|
+
const username = process.env.GITHUB_USERNAME;
|
|
1545
|
+
if (!username) {
|
|
1546
|
+
throw new Error('privateEngineRepoFactory: GITHUB_USERNAME not set');
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
const repoName = effectiveDeployId ? `engine-${effectiveDeployId.split('-')[1]}-private` : 'engine-private';
|
|
1550
|
+
logger.info(`engine-private missing — cloning ${username}/${repoName}`);
|
|
1551
|
+
shellExec(`underpost clone ${username}/${repoName}`);
|
|
1552
|
+
if (!fs.existsSync(`./${repoName}`)) {
|
|
1553
|
+
throw new Error(`privateEngineRepoFactory: clone failed for ${username}/${repoName}`);
|
|
1554
|
+
}
|
|
1555
|
+
if (repoName !== 'engine-private') shellExec(`mv ./${repoName} ./engine-private`);
|
|
1556
|
+
},
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* Removes the ephemeral `engine-private/` clone created by
|
|
1560
|
+
* `privateEngineRepoFactory()`. No-op if the directory does not exist.
|
|
1561
|
+
* @memberof UnderpostRepository
|
|
1562
|
+
*/
|
|
1563
|
+
cleanupPrivateEngineRepo() {
|
|
1564
|
+
if (fs.existsSync('./engine-private')) {
|
|
1565
|
+
fs.removeSync('./engine-private');
|
|
1566
|
+
logger.info('engine-private ephemeral clone removed');
|
|
1567
|
+
}
|
|
1568
|
+
if (fs.existsSync('/home/dd/engine-private')) {
|
|
1569
|
+
fs.removeSync('/home/dd/engine-private');
|
|
1570
|
+
logger.info('engine-private in /home/dd removed');
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
927
1573
|
};
|
|
928
1574
|
}
|
|
929
1575
|
|