underpost 3.2.14 → 3.2.22
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/CHANGELOG.md +94 -1
- package/CLI-HELP.md +99 -30
- package/README.md +2 -2
- package/bin/build.js +12 -1
- package/bin/build.template.js +4 -2
- 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/package.json +1 -1
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/test-monitor.sh +250 -0
- package/src/cli/deploy.js +200 -54
- package/src/cli/env.js +1 -4
- package/src/cli/index.js +47 -0
- package/src/cli/monitor.js +269 -72
- package/src/cli/release.js +21 -6
- package/src/cli/repository.js +21 -4
- package/src/cli/run.js +44 -4
- package/src/client/components/core/PanelForm.js +44 -44
- package/src/db/mongo/MongooseDB.js +2 -1
- package/src/index.js +1 -1
- package/src/server/conf.js +91 -18
- package/src/server/ipfs-client.js +5 -3
- package/src/server/runtime-status.js +235 -0
- package/src/server/start.js +26 -9
- package/test/deploy-monitor.test.js +132 -69
package/src/cli/run.js
CHANGED
|
@@ -120,6 +120,7 @@ const logger = loggerFactory(import.meta);
|
|
|
120
120
|
* @property {boolean} skipFullBuild - Whether to skip the full client bundle build during deployment (supported by: sync, template-deploy).
|
|
121
121
|
* @property {boolean} pullBundle - Whether to pull the bundle before running. Use together with --skip-full-build to skip the local build entirely (supported by: sync, template-deploy).
|
|
122
122
|
* @property {boolean} remove - Whether to remove/teardown resources instead of creating them (e.g. delete-expose for k3s proxy devices in dev-cluster).
|
|
123
|
+
* @property {boolean} test - Whether to enable test/generic-purpose mode (e.g. use self-signed TLS instead of cert-manager).
|
|
123
124
|
* @memberof UnderpostRun
|
|
124
125
|
*/
|
|
125
126
|
const DEFAULT_OPTION = {
|
|
@@ -188,6 +189,7 @@ const DEFAULT_OPTION = {
|
|
|
188
189
|
skipFullBuild: false,
|
|
189
190
|
pullBundle: false,
|
|
190
191
|
remove: false,
|
|
192
|
+
test: false,
|
|
191
193
|
};
|
|
192
194
|
|
|
193
195
|
/**
|
|
@@ -223,7 +225,7 @@ class UnderpostRun {
|
|
|
223
225
|
shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
|
|
224
226
|
|
|
225
227
|
shellExec(
|
|
226
|
-
`${baseCommand} cluster${options.dev ? ' --dev' : ''} --
|
|
228
|
+
`${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --service-host ${mongoHosts.join(
|
|
227
229
|
',',
|
|
228
230
|
)} --pull-image`,
|
|
229
231
|
);
|
|
@@ -265,6 +267,16 @@ class UnderpostRun {
|
|
|
265
267
|
logger.info(hostListenResult.renderHosts);
|
|
266
268
|
},
|
|
267
269
|
|
|
270
|
+
/**
|
|
271
|
+
* @method etc-hosts
|
|
272
|
+
* @description Modifies the `/etc/hosts` file to add entries for local access to services,
|
|
273
|
+
* based on the provided path input.
|
|
274
|
+
* @param {string} path - The input value, identifier, or path for the operation (used to specify the entries to add to /etc/hosts).
|
|
275
|
+
*/
|
|
276
|
+
'etc-hosts': (path = '', options = DEFAULT_OPTION) => {
|
|
277
|
+
etcHostFactory(path.split(','));
|
|
278
|
+
},
|
|
279
|
+
|
|
268
280
|
/**
|
|
269
281
|
* @method ipfs-expose
|
|
270
282
|
* @description Exposes IPFS Cluster services on specified ports for local access.
|
|
@@ -997,8 +1009,19 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
|
|
|
997
1009
|
// pathRewritePolicy,
|
|
998
1010
|
});
|
|
999
1011
|
if (options.tls) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1012
|
+
if (options.test) {
|
|
1013
|
+
const sslDir = `./engine-private/ssl/${_host}`;
|
|
1014
|
+
const nameSafe = _host.replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
1015
|
+
fs.mkdirpSync(sslDir);
|
|
1016
|
+
shellExec(`bash ./scripts/ssl.sh "${sslDir}" "${_host}"`);
|
|
1017
|
+
shellExec(`kubectl delete secret ${_host} -n ${options.namespace} --ignore-not-found`);
|
|
1018
|
+
shellExec(
|
|
1019
|
+
`kubectl create secret tls ${_host} --cert="${sslDir}/${nameSafe}.pem" --key="${sslDir}/${nameSafe}-key.pem" -n ${options.namespace}`,
|
|
1020
|
+
);
|
|
1021
|
+
} else {
|
|
1022
|
+
shellExec(`sudo kubectl delete Certificate ${_host} -n ${options.namespace} --ignore-not-found`);
|
|
1023
|
+
proxyYaml += Underpost.deploy.buildCertManagerCertificate({ ...options, host: _host });
|
|
1024
|
+
}
|
|
1002
1025
|
}
|
|
1003
1026
|
// console.log(proxyYaml);
|
|
1004
1027
|
shellExec(`kubectl delete HTTPProxy ${_host} --namespace ${options.namespace} --ignore-not-found`);
|
|
@@ -1067,6 +1090,7 @@ EOF
|
|
|
1067
1090
|
// Examples images:
|
|
1068
1091
|
// `underpost/underpost-engine:${Underpost.version}`
|
|
1069
1092
|
// `localhost/rockylinux9-underpost:${Underpost.version}`
|
|
1093
|
+
if (options.imageName) _image = options.imageName;
|
|
1070
1094
|
if (!_image) _image = `underpost/underpost-engine:${Underpost.version}`;
|
|
1071
1095
|
|
|
1072
1096
|
if (_image && !_image.startsWith('localhost'))
|
|
@@ -1162,12 +1186,16 @@ EOF
|
|
|
1162
1186
|
`,
|
|
1163
1187
|
{ disableLog: true },
|
|
1164
1188
|
);
|
|
1189
|
+
// Custom instances run a bare binary (no `underpost start` / internal
|
|
1190
|
+
// HTTP endpoint): Kubernetes readiness is the running signal and
|
|
1191
|
+
// container-status is read via exec. See `Deploy custom instance to K8S.md`.
|
|
1165
1192
|
const { ready, readyPods } = await Underpost.monitor.monitorReadyRunner(
|
|
1166
1193
|
_deployId,
|
|
1167
1194
|
env,
|
|
1168
1195
|
targetTraffic,
|
|
1169
1196
|
ignorePods,
|
|
1170
1197
|
options.namespace,
|
|
1198
|
+
{ readyGate: 'kubernetes', statusTransport: 'exec' },
|
|
1171
1199
|
);
|
|
1172
1200
|
|
|
1173
1201
|
if (!ready) {
|
|
@@ -1177,7 +1205,7 @@ EOF
|
|
|
1177
1205
|
shellExec(
|
|
1178
1206
|
`${baseCommand} run${baseClusterCommand} --namespace ${options.namespace}` +
|
|
1179
1207
|
`${options.nodeName ? ` --node-name ${options.nodeName}` : ''}` +
|
|
1180
|
-
`${options.tls ? ` --tls` : ''}` +
|
|
1208
|
+
`${options.tls ? ` --tls ${options.test ? '--test' : ''}` : ''}` +
|
|
1181
1209
|
` instance-promote '${path}'`,
|
|
1182
1210
|
);
|
|
1183
1211
|
}
|
|
@@ -2685,6 +2713,18 @@ EOF`;
|
|
|
2685
2713
|
}
|
|
2686
2714
|
},
|
|
2687
2715
|
|
|
2716
|
+
/**
|
|
2717
|
+
* @method build-cluster-deployment-manifests
|
|
2718
|
+
* @description Builds deployment manifests for both production and development environments using `node bin deploy --build-manifest`, syncing them, and setting replicas to 1 for the `dd` deployment.
|
|
2719
|
+
* @param {string} path - Unused.
|
|
2720
|
+
* @param {Object} options - The default underpost runner options for customizing workflow.
|
|
2721
|
+
* @memberof UnderpostRun
|
|
2722
|
+
*/
|
|
2723
|
+
'build-cluster-deployment-manifests': (path = '', options = DEFAULT_OPTION) => {
|
|
2724
|
+
shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd development`);
|
|
2725
|
+
shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd production --cert`);
|
|
2726
|
+
},
|
|
2727
|
+
|
|
2688
2728
|
/**
|
|
2689
2729
|
* @method monitor-ui
|
|
2690
2730
|
* @description Installs and enables the Cockpit KVM Dashboard (cockpit, cockpit-machines, libvirt)
|
|
@@ -73,7 +73,7 @@ class PanelForm {
|
|
|
73
73
|
parentIdModal: undefined,
|
|
74
74
|
route: 'home',
|
|
75
75
|
htmlFormHeader: async () => '',
|
|
76
|
-
firsUpdateEvent: async () => {
|
|
76
|
+
firsUpdateEvent: async () => {},
|
|
77
77
|
share: {
|
|
78
78
|
copyLink: false,
|
|
79
79
|
copySourceMd: false,
|
|
@@ -196,12 +196,12 @@ class PanelForm {
|
|
|
196
196
|
<img
|
|
197
197
|
class="abs center"
|
|
198
198
|
style="${renderCssAttr({
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
style: {
|
|
200
|
+
width: '100px',
|
|
201
|
+
height: '100px',
|
|
202
|
+
opacity: 0.2,
|
|
203
|
+
},
|
|
204
|
+
})}"
|
|
205
205
|
src="${defaultUrlImage}"
|
|
206
206
|
/>
|
|
207
207
|
`,
|
|
@@ -382,15 +382,15 @@ class PanelForm {
|
|
|
382
382
|
// It will be filtered from the tags array to keep visibility control separate from content tags
|
|
383
383
|
const tags = data.tags
|
|
384
384
|
? uniqueArray(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
385
|
+
data.tags
|
|
386
|
+
.replaceAll('/', ',')
|
|
387
|
+
.replaceAll('-', ',')
|
|
388
|
+
.replaceAll(' ', ',')
|
|
389
|
+
.split(',')
|
|
390
|
+
.map((t) => t.trim())
|
|
391
|
+
.filter((t) => t)
|
|
392
|
+
.concat(prefixTags),
|
|
393
|
+
)
|
|
394
394
|
: prefixTags;
|
|
395
395
|
let originObj, originFileObj, indexOriginObj;
|
|
396
396
|
if (editId) {
|
|
@@ -432,8 +432,8 @@ class PanelForm {
|
|
|
432
432
|
// In edit mode, null means user cleared the file - we need to tell server to remove it
|
|
433
433
|
const isFileCleared = data.fileId === null && editId;
|
|
434
434
|
await (async () => {
|
|
435
|
-
// When file is null
|
|
436
|
-
if (!file && !isFileCleared) return;
|
|
435
|
+
// When file is null, no markdown content, and not clearing a file, skip upload
|
|
436
|
+
if (!file && !isFileCleared && !hasMdContent) return;
|
|
437
437
|
// When user cleared file in edit mode, set fileId=null so server removes the reference
|
|
438
438
|
if (isFileCleared) {
|
|
439
439
|
fileId = null;
|
|
@@ -489,8 +489,8 @@ class PanelForm {
|
|
|
489
489
|
message: documentMessage,
|
|
490
490
|
data: documentData,
|
|
491
491
|
} = originObj && indexFormDoc === 0
|
|
492
|
-
|
|
493
|
-
|
|
492
|
+
? await DocumentService.put({ id: originObj._id, body })
|
|
493
|
+
: await DocumentService.post({
|
|
494
494
|
body,
|
|
495
495
|
});
|
|
496
496
|
const newDoc = {
|
|
@@ -518,12 +518,12 @@ class PanelForm {
|
|
|
518
518
|
fileId: {
|
|
519
519
|
fileBlob: file
|
|
520
520
|
? {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
521
|
+
data: {
|
|
522
|
+
data: await getDataFromInputFile(file),
|
|
523
|
+
},
|
|
524
|
+
mimetype: file.type,
|
|
525
|
+
name: file.name,
|
|
526
|
+
}
|
|
527
527
|
: undefined,
|
|
528
528
|
filePlain: undefined,
|
|
529
529
|
},
|
|
@@ -742,36 +742,36 @@ class PanelForm {
|
|
|
742
742
|
<div
|
|
743
743
|
class="in fll ssr-shimmer-search-box"
|
|
744
744
|
style="${renderCssAttr({
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
745
|
+
style: {
|
|
746
|
+
width: '80%',
|
|
747
|
+
height: '30px',
|
|
748
|
+
top: '-13px',
|
|
749
|
+
left: '10px',
|
|
750
|
+
},
|
|
751
|
+
})}"
|
|
752
752
|
></div>
|
|
753
753
|
</div>`,
|
|
754
754
|
createdAt: html`<div class="fl">
|
|
755
755
|
<div
|
|
756
756
|
class="in fll ssr-shimmer-search-box"
|
|
757
757
|
style="${renderCssAttr({
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
758
|
+
style: {
|
|
759
|
+
width: '50%',
|
|
760
|
+
height: '30px',
|
|
761
|
+
left: '-5px',
|
|
762
|
+
},
|
|
763
|
+
})}"
|
|
764
764
|
></div>
|
|
765
765
|
</div>`,
|
|
766
766
|
mdFileId: html`<div class="fl section-mp">
|
|
767
767
|
<div
|
|
768
768
|
class="in fll ssr-shimmer-search-box"
|
|
769
769
|
style="${renderCssAttr({
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
770
|
+
style: {
|
|
771
|
+
width: '80%',
|
|
772
|
+
height: '30px',
|
|
773
|
+
},
|
|
774
|
+
})}"
|
|
775
775
|
></div>
|
|
776
776
|
</div>`.repeat(random(2, 4)),
|
|
777
777
|
ssr: true,
|
|
@@ -86,7 +86,8 @@ class MongooseDBService {
|
|
|
86
86
|
|
|
87
87
|
const user = config.user || process.env.DB_USER || '';
|
|
88
88
|
const password = config.password || process.env.DB_PASSWORD || '';
|
|
89
|
-
const
|
|
89
|
+
const hasExplicitReplicaSet = !!(config.replicaSet || process.env.DB_REPLICA_SET);
|
|
90
|
+
const directConnection = hosts.length === 1 && !hasExplicitReplicaSet;
|
|
90
91
|
const replicaSet = directConnection
|
|
91
92
|
? ''
|
|
92
93
|
: config.replicaSet || process.env.DB_REPLICA_SET || MONGODB_DEFAULT_REPLICA_SET;
|
package/src/index.js
CHANGED
package/src/server/conf.js
CHANGED
|
@@ -1086,13 +1086,9 @@ const buildPortProxyRouter = (
|
|
|
1086
1086
|
|
|
1087
1087
|
if (Object.keys(router).length === 0) return router;
|
|
1088
1088
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
`./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`,
|
|
1093
|
-
'utf8',
|
|
1094
|
-
),
|
|
1095
|
-
);
|
|
1089
|
+
const devApiConfPath = `./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`;
|
|
1090
|
+
if (options.devProxyContext === true && process.env.NODE_ENV === 'development' && fs.existsSync(devApiConfPath)) {
|
|
1091
|
+
const confDevApiServer = JSON.parse(fs.readFileSync(devApiConfPath, 'utf8'));
|
|
1096
1092
|
let devApiHosts = [];
|
|
1097
1093
|
let origins = [];
|
|
1098
1094
|
for (const _host of Object.keys(confDevApiServer))
|
|
@@ -1524,11 +1520,12 @@ const buildCliDoc = (program, oldVersion, newVersion) => {
|
|
|
1524
1520
|
if (name === 'help') continue;
|
|
1525
1521
|
const cmdHelp = parseHelp(help(name));
|
|
1526
1522
|
details +=
|
|
1527
|
-
`\n###
|
|
1523
|
+
`\n### underpost ${name}\n\n` +
|
|
1528
1524
|
(cmdHelp.description ? `${cmdHelp.description.replace(/\s+/g, ' ')}\n\n` : '') +
|
|
1529
1525
|
`**Usage:** \`${cmdHelp.usage}\`\n` +
|
|
1530
1526
|
detailSection(cmdHelp.sections, 'Arguments', ['Argument', 'Description']) +
|
|
1531
|
-
detailSection(cmdHelp.sections, 'Options', ['Option', 'Description'])
|
|
1527
|
+
detailSection(cmdHelp.sections, 'Options', ['Option', 'Description']) +
|
|
1528
|
+
`\n---\n`;
|
|
1532
1529
|
}
|
|
1533
1530
|
|
|
1534
1531
|
const md = `${index}${details}`.replaceAll(oldVersion, newVersion);
|
|
@@ -1859,6 +1856,7 @@ const syncPrivateConf = (deployId, extraPaths = []) => {
|
|
|
1859
1856
|
if (!fs.existsSync(privateRepoPath)) {
|
|
1860
1857
|
shellExec(`cd .. && underpost clone ${privateGitUri}`, { silent: true });
|
|
1861
1858
|
} else {
|
|
1859
|
+
shellExec(`git config --global --add safe.directory '${dir.resolve(privateRepoPath)}'`);
|
|
1862
1860
|
shellExec(`cd ${privateRepoPath} && git checkout . && git clean -f -d && underpost pull . ${privateGitUri}`, {
|
|
1863
1861
|
silent: true,
|
|
1864
1862
|
});
|
|
@@ -1944,15 +1942,9 @@ const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-te
|
|
|
1944
1942
|
)
|
|
1945
1943
|
).filter((p) => !p.startsWith('.git'));
|
|
1946
1944
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
} else {
|
|
1951
|
-
shellExec(`cd ${toPath} && git reset && git checkout . && git clean -f -d`);
|
|
1952
|
-
shellExec(`node bin pull ${toPath} ${githubUsername}/pwa-microservices-template`);
|
|
1953
|
-
shellExec(`sudo rm -rf ${toPath}/engine-private`);
|
|
1954
|
-
shellExec(`sudo rm -rf ${toPath}/logs`);
|
|
1955
|
-
}
|
|
1945
|
+
fs.removeSync(`${githubUsername}/pwa-microservices-template`);
|
|
1946
|
+
shellExec(`cd .. && node engine/bin clone ${githubUsername}/pwa-microservices-template`);
|
|
1947
|
+
|
|
1956
1948
|
shellExec(`cd ${toPath} && git config core.filemode false`);
|
|
1957
1949
|
|
|
1958
1950
|
for (const copyPath of sourceFiles) {
|
|
@@ -2029,6 +2021,85 @@ const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-te
|
|
|
2029
2021
|
);
|
|
2030
2022
|
};
|
|
2031
2023
|
|
|
2024
|
+
const updatePrivateTemplateRepo = async () => {
|
|
2025
|
+
const templatePath = '/home/dd/pwa-microservices-template';
|
|
2026
|
+
shellExec(`sudo rm -rf ${templatePath}
|
|
2027
|
+
cd /home/dd/engine && npm run build:template
|
|
2028
|
+
cd /home/dd
|
|
2029
|
+
underpost clone --bare underpostnet/pwa-microservices-template-private
|
|
2030
|
+
sudo rm -rf ${templatePath}/.git
|
|
2031
|
+
mv ./pwa-microservices-template-private.git ${templatePath}/.git
|
|
2032
|
+
cd ${templatePath}
|
|
2033
|
+
npm install --omit=dev --ignore-scripts
|
|
2034
|
+
git init
|
|
2035
|
+
git config user.name 'underpostnet'
|
|
2036
|
+
git config user.email 'development@underpost.net'
|
|
2037
|
+
git add .`);
|
|
2038
|
+
const hasChanges = shellExec(`node bin cmt ${templatePath} --has-changes`, {
|
|
2039
|
+
stdout: true,
|
|
2040
|
+
silent: true,
|
|
2041
|
+
disableLog: true,
|
|
2042
|
+
}).trim();
|
|
2043
|
+
if (hasChanges === '1') {
|
|
2044
|
+
shellExec(
|
|
2045
|
+
`cd ${templatePath} && git commit -m 'Update template' && underpost push . underpostnet/pwa-microservices-template-private`,
|
|
2046
|
+
);
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
/**
|
|
2051
|
+
* @method updatePrivateEngineTestRepo
|
|
2052
|
+
* @description Publishes a deploy id's freshly assembled template to its private
|
|
2053
|
+
* **test** source repo `engine-test-<idPart>` (separate from the production
|
|
2054
|
+
* `engine-<idPart>`). A pod started with `underpost start --build --private-test-repo`
|
|
2055
|
+
* clones this repo, so work-in-progress engine source can be tested end to end
|
|
2056
|
+
* without touching the production source. Mirrors {@link updatePrivateTemplateRepo}
|
|
2057
|
+
* but per-deploy-id and against the test repo.
|
|
2058
|
+
*
|
|
2059
|
+
* Assumes the deploy id template has already been assembled at the template path
|
|
2060
|
+
* (run `node bin/build <deployId>` first, or use `node bin/build <deployId> --update-private`).
|
|
2061
|
+
* @param {string} deployId - Concrete deploy id (e.g. `dd-core`).
|
|
2062
|
+
* @returns {Promise<void>}
|
|
2063
|
+
* @memberof ServerConfBuilder
|
|
2064
|
+
*/
|
|
2065
|
+
const updatePrivateEngineTestRepo = async (deployId) => {
|
|
2066
|
+
const username = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
2067
|
+
const repoName = `engine-test-${deployId.split('-')[1]}`;
|
|
2068
|
+
const templatePath = '/home/dd/pwa-microservices-template';
|
|
2069
|
+
if (!fs.existsSync(templatePath))
|
|
2070
|
+
throw new Error(`updatePrivateEngineTestRepo: assemble the template first (node bin/build ${deployId})`);
|
|
2071
|
+
|
|
2072
|
+
// Detach the assembled working tree from any engine-build git history.
|
|
2073
|
+
shellExec(`sudo rm -rf ${templatePath}/.git`);
|
|
2074
|
+
|
|
2075
|
+
// Adopt the test repo's existing history when present (so the push is a delta);
|
|
2076
|
+
// otherwise publish a fresh history on first push.
|
|
2077
|
+
shellExec(`cd /home/dd && sudo rm -rf ./${repoName}.git && underpost clone --bare ${username}/${repoName}`, {
|
|
2078
|
+
silent: true,
|
|
2079
|
+
disableLog: true,
|
|
2080
|
+
silentOnError: true,
|
|
2081
|
+
});
|
|
2082
|
+
if (fs.existsSync(`/home/dd/${repoName}.git`)) shellExec(`mv /home/dd/${repoName}.git ${templatePath}/.git`);
|
|
2083
|
+
|
|
2084
|
+
// `git init` converts the moved bare repo into a normal work-tree repo (bare
|
|
2085
|
+
// clones have no work tree, so `git add` would fail), and bootstraps a fresh
|
|
2086
|
+
// repo on first publish. Idempotent — mirrors updatePrivateTemplateRepo.
|
|
2087
|
+
shellExec(`cd ${templatePath}
|
|
2088
|
+
git init
|
|
2089
|
+
git config user.name '${username}'
|
|
2090
|
+
git config user.email 'development@underpost.net'
|
|
2091
|
+
git add .`);
|
|
2092
|
+
|
|
2093
|
+
const hasChanges = shellExec(`node bin cmt ${templatePath} --has-changes`, {
|
|
2094
|
+
stdout: true,
|
|
2095
|
+
silent: true,
|
|
2096
|
+
disableLog: true,
|
|
2097
|
+
}).trim();
|
|
2098
|
+
if (hasChanges === '1')
|
|
2099
|
+
shellExec(`cd ${templatePath} && git commit -m 'Update ${repoName}' && underpost push . ${username}/${repoName}`);
|
|
2100
|
+
else logger.info('No changes to publish', { repoName });
|
|
2101
|
+
};
|
|
2102
|
+
|
|
2032
2103
|
export {
|
|
2033
2104
|
Config,
|
|
2034
2105
|
loadConf,
|
|
@@ -2080,4 +2151,6 @@ export {
|
|
|
2080
2151
|
syncPrivateConf,
|
|
2081
2152
|
syncDeployIdSources,
|
|
2082
2153
|
buildTemplate,
|
|
2154
|
+
updatePrivateTemplateRepo,
|
|
2155
|
+
updatePrivateEngineTestRepo,
|
|
2083
2156
|
};
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import stringify from 'fast-json-stable-stringify';
|
|
14
14
|
import { loggerFactory } from './logger.js';
|
|
15
|
+
import Underpost from '../index.js';
|
|
15
16
|
const logger = loggerFactory(import.meta);
|
|
16
17
|
const DEFAULT_IPFS_HTTP_TIMEOUT_MS = Number(process.env.IPFS_HTTP_TIMEOUT_MS || 10000);
|
|
17
18
|
const getRequestTimeoutMs = (kind = 'kubo') => {
|
|
@@ -46,21 +47,22 @@ const fetchWithTimeout = async (url, options = {}, { kind = 'kubo', label = url
|
|
|
46
47
|
* @returns {string}
|
|
47
48
|
*/
|
|
48
49
|
const getIpfsApiUrl = () =>
|
|
49
|
-
process.env.IPFS_API_URL ||
|
|
50
|
+
process.env.IPFS_API_URL ||
|
|
51
|
+
`http://${process.env.NODE_ENV === 'development' && !Underpost.env.isInsideContainer() ? 'localhost' : 'ipfs-cluster'}:5001`;
|
|
50
52
|
/**
|
|
51
53
|
* Base URL of the IPFS Cluster REST API (port 9094).
|
|
52
54
|
* @returns {string}
|
|
53
55
|
*/
|
|
54
56
|
const getClusterApiUrl = () =>
|
|
55
57
|
process.env.IPFS_CLUSTER_API_URL ||
|
|
56
|
-
`http://${process.env.NODE_ENV === 'development' ? 'localhost' : 'ipfs-cluster'}:9094`;
|
|
58
|
+
`http://${process.env.NODE_ENV === 'development' && !Underpost.env.isInsideContainer() ? 'localhost' : 'ipfs-cluster'}:9094`;
|
|
57
59
|
/**
|
|
58
60
|
* Base URL of the IPFS HTTP Gateway (port 8080).
|
|
59
61
|
* @returns {string}
|
|
60
62
|
*/
|
|
61
63
|
const getGatewayUrl = () =>
|
|
62
64
|
process.env.IPFS_GATEWAY_URL ||
|
|
63
|
-
`http://${process.env.NODE_ENV === 'development' ? 'localhost' : 'ipfs-cluster'}:8080`;
|
|
65
|
+
`http://${process.env.NODE_ENV === 'development' && !Underpost.env.isInsideContainer() ? 'localhost' : 'ipfs-cluster'}:8080`;
|
|
64
66
|
// ─────────────────────────────────────────────────────────
|
|
65
67
|
// Core: add content
|
|
66
68
|
// ─────────────────────────────────────────────────────────
|