underpost 3.2.8 → 3.2.10
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/.github/workflows/npmpkg.ci.yml +1 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +1 -0
- package/.vscode/settings.json +10 -5
- package/CHANGELOG.md +223 -2
- package/CLI-HELP.md +36 -7
- package/README.md +38 -9
- package/bin/build.js +27 -11
- package/bin/deploy.js +20 -21
- package/bin/file.js +32 -13
- package/bin/index.js +2 -1
- package/bin/vs.js +1 -1
- package/bump.config.js +26 -0
- package/conf.js +20 -4
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
- package/manifests/kind-config-dev.yaml +8 -0
- package/manifests/mongodb/pv-pvc.yaml +44 -8
- package/manifests/mongodb/statefulset.yaml +55 -68
- package/package.json +40 -25
- package/scripts/k3s-node-setup.sh +30 -11
- package/scripts/nat-iptables.sh +103 -18
- package/src/api/core/core.router.js +19 -14
- package/src/api/core/core.service.js +5 -5
- package/src/api/default/default.router.js +22 -18
- package/src/api/default/default.service.js +5 -5
- package/src/api/document/document.router.js +28 -23
- package/src/api/document/document.service.js +100 -23
- package/src/api/file/file.router.js +19 -13
- package/src/api/file/file.service.js +9 -7
- package/src/api/test/test.router.js +17 -12
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +5 -4
- package/src/api/user/user.router.js +297 -288
- package/src/api/user/user.service.js +100 -35
- package/src/cli/baremetal.js +20 -11
- package/src/cli/cluster.js +243 -55
- package/src/cli/db.js +106 -62
- package/src/cli/deploy.js +297 -154
- package/src/cli/fs.js +19 -3
- package/src/cli/index.js +37 -9
- package/src/cli/ipfs.js +4 -6
- package/src/cli/kubectl.js +4 -1
- package/src/cli/lxd.js +217 -135
- package/src/cli/release.js +289 -131
- package/src/cli/repository.js +91 -34
- package/src/cli/run.js +297 -56
- package/src/cli/test.js +9 -3
- package/src/client/Default.index.js +9 -3
- package/src/client/components/core/Auth.js +19 -5
- package/src/client/components/core/Docs.js +6 -34
- package/src/client/components/core/FileExplorer.js +6 -6
- package/src/client/components/core/Modal.js +65 -2
- package/src/client/components/core/PanelForm.js +56 -52
- package/src/client/components/core/Recover.js +4 -4
- package/src/client/components/core/Worker.js +170 -350
- package/src/client/services/default/default.management.js +20 -25
- package/src/client/services/user/guest.service.js +10 -3
- package/src/client/sw/core.sw.js +174 -112
- package/src/db/DataBaseProvider.js +120 -20
- package/src/db/mongo/MongoBootstrap.js +587 -0
- package/src/db/mongo/MongooseDB.js +126 -22
- package/src/index.js +1 -1
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/wp/Wp.js +8 -5
- package/src/server/auth.js +2 -2
- package/src/server/client-build-docs.js +1 -1
- package/src/server/client-build.js +94 -129
- package/src/server/conf.js +20 -65
- package/src/server/data-query.js +32 -20
- package/src/server/dns.js +22 -0
- package/src/server/process.js +180 -19
- package/src/server/runtime.js +1 -1
- package/src/server/start.js +26 -7
- package/src/server/valkey.js +9 -2
- package/src/ws/IoInterface.js +16 -16
- package/src/ws/core/channels/core.ws.chat.js +11 -11
- package/src/ws/core/channels/core.ws.mailer.js +29 -29
- package/src/ws/core/channels/core.ws.stream.js +19 -19
- package/src/ws/core/core.ws.connection.js +8 -8
- package/src/ws/core/core.ws.server.js +6 -5
- package/src/ws/default/channels/default.ws.main.js +10 -10
- package/src/ws/default/default.ws.connection.js +4 -4
- package/src/ws/default/default.ws.server.js +4 -3
- package/typedoc.json +10 -1
- package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
- package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
- /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
- /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
- /package/src/client/ssr/{pages → views}/Test.js +0 -0
package/src/cli/release.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import fs from 'fs-extra';
|
|
13
|
+
import path from 'path';
|
|
13
14
|
import dotenv from 'dotenv';
|
|
14
15
|
import { pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
15
16
|
import { loggerFactory } from '../server/logger.js';
|
|
@@ -18,6 +19,222 @@ import Underpost from '../index.js';
|
|
|
18
19
|
|
|
19
20
|
const logger = loggerFactory(import.meta);
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Files that must never be touched by a version bump, even if they happen to contain a
|
|
24
|
+
* literal version string. CHANGELOG is historical; logs/ and node_modules/ are runtime
|
|
25
|
+
* outputs; engine-private/.env.* and secrets must not be rewritten by regex.
|
|
26
|
+
*/
|
|
27
|
+
const VERSION_BUMP_SKIP = [
|
|
28
|
+
/(^|\/)CHANGELOG\.md$/i,
|
|
29
|
+
/(^|\/)logs\//,
|
|
30
|
+
/(^|\/)node_modules\//,
|
|
31
|
+
/(^|\/)\.git\//,
|
|
32
|
+
/(^|\/)engine-private\/.*\.env(\..*)?$/,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const isSkipped = (filePath) => VERSION_BUMP_SKIP.some((re) => re.test(filePath));
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Declarative regex-based version bump targets for files bumpp cannot handle natively
|
|
39
|
+
* (image tags, doc strings, README badges, build-args, etc.).
|
|
40
|
+
*
|
|
41
|
+
* Each target is one of:
|
|
42
|
+
* - { file: 'path/to/file', patterns: RegExp | RegExp[] }
|
|
43
|
+
* - { dir: 'dir', match: RegExp, patterns: RegExp | RegExp[], recursive?: boolean }
|
|
44
|
+
*
|
|
45
|
+
* Every pattern MUST have exactly one capture group: the prefix to preserve. The replacement
|
|
46
|
+
* is `$1<newVersion>`. Use a lookahead `(?=...)` to anchor a trailing context (closing quote,
|
|
47
|
+
* backtick, etc.) without consuming it. This keeps the callback signature trivial.
|
|
48
|
+
*
|
|
49
|
+
* The walker iterates targets, applies patterns to each matching file, and reports the
|
|
50
|
+
* number of substitutions per file. Files that match nothing are silently skipped.
|
|
51
|
+
*/
|
|
52
|
+
const buildVersionBumpTargets = () => [
|
|
53
|
+
// ── src/index.js — anchored to the static class field, never a bare replaceAll. ──
|
|
54
|
+
{
|
|
55
|
+
file: 'src/index.js',
|
|
56
|
+
patterns: /(static version = ['"]v)\d+\.\d+\.\d+(?=['"])/g,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// ── README + CLI-HELP fallbacks. CLI-HELP is regenerated by `deploy cli-docs`, but bump
|
|
60
|
+
// it too so the file is consistent if the regeneration step is skipped. ──
|
|
61
|
+
{
|
|
62
|
+
file: 'README.md',
|
|
63
|
+
patterns: [
|
|
64
|
+
/(socket\.dev\/api\/badge\/npm\/package\/underpost\/)\d+\.\d+\.\d+/g,
|
|
65
|
+
/(socket\.dev\/npm\/package\/underpost\/overview\/)\d+\.\d+\.\d+/g,
|
|
66
|
+
/(ci\/cd cli v)\d+\.\d+\.\d+/gi,
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
file: 'CLI-HELP.md',
|
|
71
|
+
patterns: /(ci\/cd cli v)\d+\.\d+\.\d+/gi,
|
|
72
|
+
optional: true,
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// ── Cyberia + nexodev docs. ──
|
|
76
|
+
{
|
|
77
|
+
dir: 'src/client/public/cyberia-docs',
|
|
78
|
+
match: /\.md$/,
|
|
79
|
+
patterns: [
|
|
80
|
+
/(\*\*(?:Current )?[Vv]ersion:\*\* )\d+\.\d+\.\d+/g,
|
|
81
|
+
/(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
82
|
+
],
|
|
83
|
+
recursive: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
dir: 'src/client/public/nexodev/docs/references',
|
|
87
|
+
match: /\.md$/,
|
|
88
|
+
patterns: [
|
|
89
|
+
/(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
90
|
+
/(UNDERPOST_VERSION=)\d+\.\d+\.\d+/g,
|
|
91
|
+
/(ci\/cd cli v)\d+\.\d+\.\d+/gi,
|
|
92
|
+
// version tag bumps inside markdown narrative, e.g. `v3.2.9`, `v3.2.10`, …
|
|
93
|
+
/(`v)\d+\.\d+\.\d+(?=`)/g,
|
|
94
|
+
],
|
|
95
|
+
recursive: true,
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// ── Kubernetes manifests. Covers deployment.yaml + cronjob yamls under dd-cron. ──
|
|
99
|
+
{
|
|
100
|
+
dir: 'manifests/deployment',
|
|
101
|
+
match: /deployment\.yaml$/,
|
|
102
|
+
patterns: [
|
|
103
|
+
/(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
104
|
+
/(engine\.version: )\d+\.\d+\.\d+/g,
|
|
105
|
+
],
|
|
106
|
+
recursive: true,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
dir: 'manifests/cronjobs',
|
|
110
|
+
match: /\.yaml$/,
|
|
111
|
+
patterns: /(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
112
|
+
recursive: true,
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// ── GitHub Actions workflows. Single sweep across docker-image.*.ci.yml, engine-*.cd.yml,
|
|
116
|
+
// and the root docker-image.ci.yml — replaces previous three separate loops. ──
|
|
117
|
+
{
|
|
118
|
+
dir: '.github/workflows',
|
|
119
|
+
match: /\.(yml|yaml)$/,
|
|
120
|
+
patterns: [
|
|
121
|
+
/(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
122
|
+
/(underpost-engine:v)\d+\.\d+\.\d+/g,
|
|
123
|
+
/(type=raw,value=v)\d+\.\d+\.\d+/g,
|
|
124
|
+
/(UNDERPOST_VERSION=)\d+\.\d+\.\d+/g,
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// ── engine-private confs (gitignored — bumped only if present). ──
|
|
129
|
+
{
|
|
130
|
+
dir: 'engine-private/conf',
|
|
131
|
+
match: /(?:deployment\.yaml|conf\.instances\.json)$/,
|
|
132
|
+
patterns: [
|
|
133
|
+
/(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
134
|
+
/(underpost-engine:v)\d+\.\d+\.\d+/g,
|
|
135
|
+
/(engine\.version: )\d+\.\d+\.\d+/g,
|
|
136
|
+
/(v)\d+\.\d+\.\d+/g,
|
|
137
|
+
],
|
|
138
|
+
recursive: true,
|
|
139
|
+
optional: true,
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Walks a directory (optionally recursively) and returns paths matching `match`.
|
|
145
|
+
*/
|
|
146
|
+
const walkDir = (dir, match, recursive) => {
|
|
147
|
+
if (!fs.existsSync(dir)) return [];
|
|
148
|
+
const out = [];
|
|
149
|
+
const stack = [dir];
|
|
150
|
+
while (stack.length) {
|
|
151
|
+
const cur = stack.pop();
|
|
152
|
+
let entries;
|
|
153
|
+
try {
|
|
154
|
+
entries = fs.readdirSync(cur, { withFileTypes: true });
|
|
155
|
+
} catch {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
for (const ent of entries) {
|
|
159
|
+
const full = path.join(cur, ent.name);
|
|
160
|
+
if (ent.isDirectory()) {
|
|
161
|
+
if (recursive) stack.push(full);
|
|
162
|
+
} else if (ent.isFile() && match.test(ent.name) && !isSkipped(full)) {
|
|
163
|
+
out.push(full);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Applies an array of regex patterns to a single file in place. Substitutes only when the
|
|
172
|
+
* matched version literally equals `oldVersion` — narrative references to other versions
|
|
173
|
+
* (e.g. example tags `(v3.2.9, v3.2.10, …)`) are left untouched.
|
|
174
|
+
*
|
|
175
|
+
* Returns the substitution count (0 if nothing matched).
|
|
176
|
+
*/
|
|
177
|
+
const bumpFile = (filePath, patterns, oldVersion, newVersion, { dryRun }) => {
|
|
178
|
+
if (isSkipped(filePath)) return 0;
|
|
179
|
+
if (!fs.existsSync(filePath)) return 0;
|
|
180
|
+
const original = fs.readFileSync(filePath, 'utf8');
|
|
181
|
+
const regexes = Array.isArray(patterns) ? patterns : [patterns];
|
|
182
|
+
let updated = original;
|
|
183
|
+
let count = 0;
|
|
184
|
+
for (const re of regexes) {
|
|
185
|
+
updated = updated.replace(re, (m, prefix) => {
|
|
186
|
+
const matchedVersion = m.match(/\d+\.\d+\.\d+/)?.[0];
|
|
187
|
+
if (matchedVersion !== oldVersion) return m;
|
|
188
|
+
count += 1;
|
|
189
|
+
return `${prefix}${newVersion}`;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (count > 0 && updated !== original && !dryRun) {
|
|
193
|
+
fs.writeFileSync(filePath, updated, 'utf8');
|
|
194
|
+
}
|
|
195
|
+
return count;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Bumps every non-canonical file declared in VERSION_BUMP_TARGETS.
|
|
200
|
+
* Returns a per-file report so callers can print a summary.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} oldVersion - The version currently in the files (pre-bump).
|
|
203
|
+
* @param {string} newVersion - The version to write.
|
|
204
|
+
* @param {{dryRun?: boolean}} [opts]
|
|
205
|
+
* @returns {Array<{file: string, count: number}>}
|
|
206
|
+
*/
|
|
207
|
+
const bumpAuxiliaryFiles = (oldVersion, newVersion, { dryRun = false } = {}) => {
|
|
208
|
+
const report = [];
|
|
209
|
+
if (oldVersion === newVersion) return report;
|
|
210
|
+
for (const target of buildVersionBumpTargets()) {
|
|
211
|
+
const files = target.file ? [target.file] : walkDir(target.dir, target.match, target.recursive ?? false);
|
|
212
|
+
for (const file of files) {
|
|
213
|
+
if (target.optional && !fs.existsSync(file)) continue;
|
|
214
|
+
const count = bumpFile(file, target.patterns, oldVersion, newVersion, { dryRun });
|
|
215
|
+
if (count > 0) report.push({ file, count });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return report;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Prints a per-file change summary table.
|
|
223
|
+
*/
|
|
224
|
+
const printBumpReport = (report, { dryRun }) => {
|
|
225
|
+
const header = dryRun ? 'Version bump (dry-run) — would change:' : 'Version bump — files updated:';
|
|
226
|
+
logger.info(header);
|
|
227
|
+
if (!report.length) {
|
|
228
|
+
console.log(' (no files matched)');
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const maxLen = Math.max(...report.map((r) => r.file.length));
|
|
232
|
+
for (const { file, count } of report) {
|
|
233
|
+
console.log(` ${file.padEnd(maxLen + 2)} ${count} match${count === 1 ? '' : 'es'}`);
|
|
234
|
+
}
|
|
235
|
+
console.log(` Total: ${report.length} file(s), ${report.reduce((s, r) => s + r.count, 0)} substitution(s)`);
|
|
236
|
+
};
|
|
237
|
+
|
|
21
238
|
/**
|
|
22
239
|
* Kills any Node.js dev-server or nodemon processes that may hold file locks
|
|
23
240
|
* (e.g. overwriting package.json). Skips VSCode internals and the current process.
|
|
@@ -46,154 +263,94 @@ class UnderpostRelease {
|
|
|
46
263
|
* Builds a new version of the Underpost engine.
|
|
47
264
|
*
|
|
48
265
|
* Performs the full version build pipeline:
|
|
49
|
-
* 1. Loads production
|
|
50
|
-
* 2.
|
|
51
|
-
* 3.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
266
|
+
* 1. Loads production env and pulls latest code; kills dev servers on ports 4001-4003.
|
|
267
|
+
* 2. Builds and smoke-tests the pwa-microservices-template (aborts on error).
|
|
268
|
+
* 3. Canonical version files: delegates to `bumpp` (driven by `bump.config.js`). Bumps
|
|
269
|
+
* package.json, package-lock.json (root + packages['']), and every
|
|
270
|
+
* engine-private/conf/**\/package.json.
|
|
271
|
+
* 4. Auxiliary files: anchored-regex walker over VERSION_BUMP_TARGETS — covers
|
|
272
|
+
* src/index.js, README, cyberia-docs, nexodev docs, manifests (deployment + cronjobs),
|
|
273
|
+
* .github/workflows (image tags, type=raw, UNDERPOST_VERSION build-args), and
|
|
274
|
+
* engine-private confs (deployment.yaml + conf.instances.json). Files in
|
|
275
|
+
* VERSION_BUMP_SKIP (CHANGELOG, logs/, .env.*, node_modules/) are never touched.
|
|
276
|
+
* The walker only rewrites occurrences whose version literally equals the pre-bump
|
|
277
|
+
* version — narrative references like `(v3.2.9, v3.2.10, …)` are preserved.
|
|
278
|
+
* 5. Rebuilds CLI docs, deps, client bundle, deploy manifests, default confs.
|
|
279
|
+
* 6. Syncs cron setup-start scripts and builds the changelog.
|
|
280
|
+
*
|
|
281
|
+
* With `options.dryRun: true`, steps 3–4 print a per-file substitution report and exit
|
|
282
|
+
* without writing files or running steps 5–6.
|
|
57
283
|
*
|
|
58
284
|
* @method build
|
|
59
285
|
* @param {string} [newVersion] - The new version string to set. Defaults to current version if not provided.
|
|
60
|
-
* @param {
|
|
286
|
+
* @param {{dryRun?: boolean}} [options] - Commander options. `--dry-run` previews changes.
|
|
61
287
|
* @memberof UnderpostRelease
|
|
62
288
|
*/
|
|
63
|
-
async build(newVersion, options) {
|
|
289
|
+
async build(newVersion, options = {}) {
|
|
290
|
+
const dryRun = !!options.dryRun;
|
|
64
291
|
dotenv.config({ path: `./engine-private/conf/dd-cron/.env.production`, override: true });
|
|
65
292
|
shellCd(`/home/dd/engine`);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
shellExec(`node bin pull . ${process.env.GITHUB_USERNAME}/engine`);
|
|
69
|
-
shellExec(`npm run update:template`);
|
|
70
|
-
shellExec(`cd ../pwa-microservices-template && npm install && npm run build`);
|
|
71
|
-
console.log(fs.existsSync(`../pwa-microservices-template/engine-private/conf/dd-default`));
|
|
72
|
-
shellExec(`cd ../pwa-microservices-template && ENABLE_FILE_LOGS=true timeout 5s npm run dev`, {
|
|
73
|
-
async: true,
|
|
74
|
-
});
|
|
75
|
-
await timer(5500);
|
|
76
|
-
const templateRunnerResult = fs.readFileSync(`../pwa-microservices-template/logs/start.js/all.log`, 'utf8');
|
|
77
|
-
logger.info('Test template runner result');
|
|
78
|
-
console.log(templateRunnerResult);
|
|
79
|
-
if (!templateRunnerResult || templateRunnerResult.toLowerCase().match('error')) {
|
|
80
|
-
logger.error('Test template runner result failed');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
killDevServers();
|
|
84
|
-
shellCd(`/home/dd/engine`);
|
|
85
|
-
Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
|
|
293
|
+
|
|
294
|
+
// ── Resolve from/to versions up front. Used by every bump step + downstream commands. ──
|
|
86
295
|
const originPackageJson = JSON.parse(fs.readFileSync(`package.json`, 'utf8'));
|
|
87
|
-
if (!newVersion) newVersion = originPackageJson.version;
|
|
88
296
|
const { version } = originPackageJson;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const originPackageLockJson = JSON.parse(fs.readFileSync(`package-lock.json`, 'utf8'));
|
|
93
|
-
originPackageLockJson.version = newVersion;
|
|
94
|
-
originPackageLockJson.packages[''].version = newVersion;
|
|
95
|
-
fs.writeFileSync(`package-lock.json`, JSON.stringify(originPackageLockJson, null, 4), 'utf8');
|
|
297
|
+
if (!newVersion) newVersion = version;
|
|
298
|
+
logger.info(`Release build — bumping ${version} → ${newVersion}${dryRun ? ' (dry-run)' : ''}`);
|
|
96
299
|
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
}
|
|
300
|
+
if (!dryRun) {
|
|
301
|
+
killDevServers();
|
|
302
|
+
Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
|
|
303
|
+
shellExec(`node bin pull . ${process.env.GITHUB_USERNAME}/engine`);
|
|
304
|
+
shellExec(`npm run update:template`);
|
|
305
|
+
shellExec(`cd ../pwa-microservices-template && npm install && npm run build`);
|
|
306
|
+
console.log(fs.existsSync(`../pwa-microservices-template/engine-private/conf/dd-default`));
|
|
307
|
+
shellExec(`cd ../pwa-microservices-template && ENABLE_FILE_LOGS=true timeout 5s npm run dev`, {
|
|
308
|
+
async: true,
|
|
309
|
+
});
|
|
310
|
+
await timer(5500);
|
|
311
|
+
const templateRunnerResult = fs.readFileSync(`../pwa-microservices-template/logs/start.js/all.log`, 'utf8');
|
|
312
|
+
logger.info('Test template runner result');
|
|
313
|
+
console.log(templateRunnerResult);
|
|
314
|
+
if (!templateRunnerResult || templateRunnerResult.toLowerCase().match('error')) {
|
|
315
|
+
logger.error('Test template runner result failed');
|
|
316
|
+
return;
|
|
116
317
|
}
|
|
318
|
+
killDevServers();
|
|
319
|
+
shellCd(`/home/dd/engine`);
|
|
320
|
+
Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
|
|
117
321
|
}
|
|
118
322
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const wfPath = `./.github/workflows/${wf}`;
|
|
140
|
-
const updated = fs
|
|
141
|
-
.readFileSync(wfPath, 'utf8')
|
|
142
|
-
.replace(/underpost\/([^:'"]+):v[0-9]+\.[0-9]+\.[0-9]+/g, `underpost/$1:v${newVersion}`);
|
|
143
|
-
fs.writeFileSync(wfPath, updated, 'utf8');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Update version tag in all runtime docker image workflows (type=raw,value=v<version>).
|
|
147
|
-
for (const wf of fs.readdirSync(`./.github/workflows`)) {
|
|
148
|
-
if (!wf.match(/^docker-image\..+\.ci\.yml$/) || wf === 'docker-image.ci.yml') continue;
|
|
149
|
-
const wfPath = `./.github/workflows/${wf}`;
|
|
150
|
-
fs.writeFileSync(
|
|
151
|
-
wfPath,
|
|
152
|
-
fs.readFileSync(wfPath, 'utf8').replaceAll(`type=raw,value=v${version}`, `type=raw,value=v${newVersion}`),
|
|
153
|
-
'utf8',
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Update image version in all conf.instances.json files for underpost/* images.
|
|
158
|
-
if (fs.existsSync(`./engine-private/conf`)) {
|
|
159
|
-
const confFiles = await fs.readdir(`./engine-private/conf`, { recursive: true });
|
|
160
|
-
for (const relativePath of confFiles) {
|
|
161
|
-
const filePath = `./engine-private/conf/${relativePath.replaceAll('\\', '/')}`;
|
|
162
|
-
if (filePath.split('/').pop() !== 'conf.instances.json' || !fs.existsSync(filePath)) continue;
|
|
163
|
-
|
|
164
|
-
let instances;
|
|
165
|
-
try {
|
|
166
|
-
instances = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
167
|
-
} catch {
|
|
168
|
-
logger.warn(`Skipping invalid JSON file: ${filePath}`);
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (!Array.isArray(instances)) continue;
|
|
173
|
-
|
|
174
|
-
let updated = false;
|
|
175
|
-
for (const instance of instances) {
|
|
176
|
-
if (!instance || typeof instance !== 'object') continue;
|
|
177
|
-
if (!instance.image || typeof instance.image !== 'string') continue;
|
|
178
|
-
if (!instance.image.startsWith('underpost/')) continue;
|
|
323
|
+
// ── Canonical version files: delegate to bumpp (package.json, package-lock.json,
|
|
324
|
+
// engine-private/conf/**/package.json). bumpp owns lockfile semantics (root version
|
|
325
|
+
// + packages[''].version) so we don't reimplement them. ──
|
|
326
|
+
const { versionBump } = await import('bumpp');
|
|
327
|
+
const bumppResult = await versionBump({
|
|
328
|
+
release: newVersion,
|
|
329
|
+
files: [
|
|
330
|
+
'package.json',
|
|
331
|
+
'package-lock.json',
|
|
332
|
+
...(fs.existsSync('./engine-private/conf') ? ['engine-private/conf/**/package.json'] : []),
|
|
333
|
+
],
|
|
334
|
+
commit: false,
|
|
335
|
+
tag: false,
|
|
336
|
+
push: false,
|
|
337
|
+
confirm: false,
|
|
338
|
+
recursive: false,
|
|
339
|
+
ignoreScripts: true,
|
|
340
|
+
dry: dryRun,
|
|
341
|
+
});
|
|
342
|
+
logger.info(`bumpp updated ${bumppResult?.files?.length ?? 0} canonical file(s).`);
|
|
179
343
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
instance.image = nextImage;
|
|
184
|
-
updated = true;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
344
|
+
// ── Auxiliary files: anchored-regex walker over VERSION_BUMP_TARGETS. ──
|
|
345
|
+
const report = bumpAuxiliaryFiles(version, newVersion, { dryRun });
|
|
346
|
+
printBumpReport(report, { dryRun });
|
|
187
347
|
|
|
188
|
-
|
|
189
|
-
|
|
348
|
+
if (dryRun) {
|
|
349
|
+
logger.info('Dry-run complete. No files modified. Re-run without --dry-run to apply.');
|
|
350
|
+
return { from: version, to: newVersion, files: report.map((r) => r.file), dryRun: true };
|
|
190
351
|
}
|
|
191
352
|
|
|
192
|
-
|
|
193
|
-
`./src/index.js`,
|
|
194
|
-
fs.readFileSync(`./src/index.js`, 'utf8').replaceAll(`${version}`, `${newVersion}`),
|
|
195
|
-
'utf8',
|
|
196
|
-
);
|
|
353
|
+
// ── Downstream regenerations and manifest sync (unchanged from previous flow). ──
|
|
197
354
|
shellExec(`node bin/deploy cli-docs ${version} ${newVersion}`);
|
|
198
355
|
shellExec(`node bin/deploy update-dependencies`);
|
|
199
356
|
shellExec(`node bin/build dd`);
|
|
@@ -206,6 +363,7 @@ class UnderpostRelease {
|
|
|
206
363
|
shellExec(`sudo rm -rf ./engine-private/conf/dd-default`);
|
|
207
364
|
shellExec(`node bin cron --kubeadm --setup-start --git`); // --apply
|
|
208
365
|
shellExec(`node bin cmt --changelog-build`);
|
|
366
|
+
return { from: version, to: newVersion, files: report.map((r) => r.file), dryRun: false };
|
|
209
367
|
},
|
|
210
368
|
|
|
211
369
|
/**
|
package/src/cli/repository.js
CHANGED
|
@@ -401,6 +401,7 @@ class UnderpostRepository {
|
|
|
401
401
|
|
|
402
402
|
/**
|
|
403
403
|
* Retrieves the message of the last Git commit.
|
|
404
|
+
* @param {number} [skip=0] - Number of commits to skip from HEAD (0 = most recent).
|
|
404
405
|
* @returns {string} The last commit message.
|
|
405
406
|
* @memberof UnderpostRepository
|
|
406
407
|
*/
|
|
@@ -949,6 +950,7 @@ Prevent build private config repo.`,
|
|
|
949
950
|
/**
|
|
950
951
|
* Retrieves the Git commit history.
|
|
951
952
|
* @param {number} [sinceCommit=1] - The number of recent commits to retrieve.
|
|
953
|
+
* @param {string} [repoPath='.'] - The path to the repository.
|
|
952
954
|
* @returns {Array<{hash: string, message: string, files: string}>} An array of commit objects with hash, message, and files.
|
|
953
955
|
* @memberof UnderpostRepository
|
|
954
956
|
*/
|
|
@@ -1006,11 +1008,14 @@ Prevent build private config repo.`,
|
|
|
1006
1008
|
const host = 'default.net';
|
|
1007
1009
|
const path = '/';
|
|
1008
1010
|
DefaultConf.server[host][path].valkey = {
|
|
1009
|
-
port: 6379,
|
|
1010
|
-
host: '
|
|
1011
|
+
port: 'env:VALKEY_PORT:int:6379',
|
|
1012
|
+
host: 'env:VALKEY_HOST:127.0.0.1',
|
|
1011
1013
|
};
|
|
1012
|
-
|
|
1013
|
-
DefaultConf.server[host][path].db.
|
|
1014
|
+
DefaultConf.server[host][path].db.host = 'env:DB_HOST:mongodb://127.0.0.1:27017';
|
|
1015
|
+
DefaultConf.server[host][path].db.replicaSet = 'env:DB_REPLICA_SET:rs0';
|
|
1016
|
+
DefaultConf.server[host][path].db.authSource = 'env:DB_AUTH_SOURCE:admin';
|
|
1017
|
+
DefaultConf.server[host][path].db.user = 'env:DB_USER:';
|
|
1018
|
+
DefaultConf.server[host][path].db.password = 'env:DB_PASSWORD:';
|
|
1014
1019
|
defaultConf = true;
|
|
1015
1020
|
break;
|
|
1016
1021
|
}
|
|
@@ -1270,19 +1275,10 @@ Prevent build private config repo.`,
|
|
|
1270
1275
|
},
|
|
1271
1276
|
|
|
1272
1277
|
/**
|
|
1273
|
-
*
|
|
1274
|
-
*
|
|
1275
|
-
* @param {string}
|
|
1276
|
-
* @
|
|
1277
|
-
* @returns {{ count: number, branch: string, hasUnpushed: boolean }} Unpush metadata.
|
|
1278
|
-
* @memberof UnderpostRepository
|
|
1279
|
-
*/
|
|
1280
|
-
/**
|
|
1281
|
-
* Checks whether a remote Git repository URL is reachable.
|
|
1282
|
-
* Uses `git ls-remote` with `|| true` so the process always exits 0.
|
|
1283
|
-
* Injects `GITHUB_TOKEN` into GitHub HTTPS URLs when available.
|
|
1284
|
-
* @param {string} url - Full HTTPS clone URL to test (e.g. "https://github.com/org/repo.git").
|
|
1285
|
-
* @returns {boolean} `true` when the remote responded with at least one ref hash.
|
|
1278
|
+
* Resolves a Git remote URL, normalizing short-form owner/repo references to full
|
|
1279
|
+
* GitHub HTTPS URLs and injecting GITHUB_TOKEN when available.
|
|
1280
|
+
* @param {string} url - The repository URL or short-form (e.g. "owner/repo" or full HTTPS URL).
|
|
1281
|
+
* @returns {string} The resolved (and optionally authenticated) HTTPS URL.
|
|
1286
1282
|
* @memberof UnderpostRepository
|
|
1287
1283
|
*/
|
|
1288
1284
|
resolveAuthUrl(url) {
|
|
@@ -1300,20 +1296,36 @@ Prevent build private config repo.`,
|
|
|
1300
1296
|
}
|
|
1301
1297
|
return normalized;
|
|
1302
1298
|
},
|
|
1303
|
-
|
|
1299
|
+
/**
|
|
1300
|
+
* Checks whether a remote Git repository URL is reachable.
|
|
1301
|
+
* Uses `silentOnError` so a non-reachable remote returns false instead of throwing.
|
|
1302
|
+
* Injects `GITHUB_TOKEN` into GitHub HTTPS URLs when available.
|
|
1303
|
+
* @param {string} url - Full HTTPS clone URL to test (e.g. "https://github.com/org/repo.git").
|
|
1304
|
+
* @returns {boolean} `true` when the remote responded with at least one ref hash.
|
|
1305
|
+
* @memberof UnderpostRepository
|
|
1306
|
+
*/
|
|
1304
1307
|
isRemoteRepo(url) {
|
|
1305
1308
|
if (!url) return false;
|
|
1306
1309
|
const authUrl = Underpost.repo.resolveAuthUrl(url);
|
|
1307
1310
|
// GIT_TERMINAL_PROMPT=0 prevents git from hanging on credential prompts inside containers.
|
|
1308
|
-
const raw = shellExec(`GIT_TERMINAL_PROMPT=0 git ls-remote "${authUrl}" HEAD 2>&1
|
|
1311
|
+
const raw = shellExec(`GIT_TERMINAL_PROMPT=0 git ls-remote "${authUrl}" HEAD 2>&1`, {
|
|
1309
1312
|
stdout: true,
|
|
1310
1313
|
silent: true,
|
|
1311
1314
|
disableLog: true,
|
|
1315
|
+
silentOnError: true,
|
|
1312
1316
|
});
|
|
1313
1317
|
logger.info('isRemoteRepo', { url, raw: (raw || '').slice(0, 120) });
|
|
1314
1318
|
return typeof raw === 'string' && /^[0-9a-f]{40}\t/m.test(raw);
|
|
1315
1319
|
},
|
|
1316
1320
|
|
|
1321
|
+
/**
|
|
1322
|
+
* Returns metadata about unpushed commits in a git repository.
|
|
1323
|
+
* Fetches from origin, then counts commits ahead of the remote branch.
|
|
1324
|
+
* @param {string} [repoPath='.'] - Path to the git repository.
|
|
1325
|
+
* @param {number} [fallback=1] - Value to return as `count` when no unpushed commits are detected.
|
|
1326
|
+
* @returns {{ count: number, branch: string, hasUnpushed: boolean }} Unpush metadata.
|
|
1327
|
+
* @memberof UnderpostRepository
|
|
1328
|
+
*/
|
|
1317
1329
|
getUnpushedCount(repoPath = '.', fallback = 1) {
|
|
1318
1330
|
const branch = shellExec(`cd ${repoPath} && git branch --show-current`, {
|
|
1319
1331
|
stdout: true,
|
|
@@ -1353,19 +1365,6 @@ Prevent build private config repo.`,
|
|
|
1353
1365
|
.trim()
|
|
1354
1366
|
.replaceAll('] - ', '] ');
|
|
1355
1367
|
},
|
|
1356
|
-
|
|
1357
|
-
/**
|
|
1358
|
-
* Manages a cron-backup Git repository: clone, pull, commit, or push.
|
|
1359
|
-
* Resolves the repository path as `../<repoName>` relative to the CWD.
|
|
1360
|
-
* Requires the `GITHUB_USERNAME` environment variable to be set.
|
|
1361
|
-
* @param {object} params
|
|
1362
|
-
* @param {string} params.repoName - Repository name (e.g. `engine-cyberia-cron-backups`).
|
|
1363
|
-
* @param {'clone'|'pull'|'commit'|'push'} params.operation - Git operation to perform.
|
|
1364
|
-
* @param {string} [params.message=''] - Commit message (used by the `commit` operation).
|
|
1365
|
-
* @param {boolean} [params.forceClone=false] - Remove existing clone before re-cloning.
|
|
1366
|
-
* @returns {boolean} `true` on success, `false` if GITHUB_USERNAME is unset or on error.
|
|
1367
|
-
* @memberof UnderpostRepository
|
|
1368
|
-
*/
|
|
1369
1368
|
/**
|
|
1370
1369
|
* Initializes a git repository at the given path and configures user identity
|
|
1371
1370
|
* from environment variables (`GITHUB_USERNAME` / `GITHUB_EMAIL`).
|
|
@@ -1387,9 +1386,10 @@ Prevent build private config repo.`,
|
|
|
1387
1386
|
shellExec(`cd "${repoPath}" && git config user.email '${gitEmail}'`);
|
|
1388
1387
|
|
|
1389
1388
|
if (origin) {
|
|
1390
|
-
const currentRemote = shellExec(`cd "${repoPath}" && git remote get-url origin
|
|
1389
|
+
const currentRemote = shellExec(`cd "${repoPath}" && git remote get-url origin`, {
|
|
1391
1390
|
stdout: true,
|
|
1392
1391
|
silent: true,
|
|
1392
|
+
silentOnError: true,
|
|
1393
1393
|
}).trim();
|
|
1394
1394
|
if (!currentRemote) {
|
|
1395
1395
|
shellExec(`cd "${repoPath}" && git remote add origin "${origin}"`);
|
|
@@ -1398,7 +1398,18 @@ Prevent build private config repo.`,
|
|
|
1398
1398
|
}
|
|
1399
1399
|
}
|
|
1400
1400
|
},
|
|
1401
|
-
|
|
1401
|
+
/**
|
|
1402
|
+
* Manages a cron-backup Git repository: clone, pull, commit, or push.
|
|
1403
|
+
* Resolves the repository path as `../<repoName>` relative to the CWD.
|
|
1404
|
+
* Requires the `GITHUB_USERNAME` environment variable to be set.
|
|
1405
|
+
* @param {object} params
|
|
1406
|
+
* @param {string} params.repoName - Repository name (e.g. `engine-cyberia-cron-backups`).
|
|
1407
|
+
* @param {'clone'|'pull'|'commit'|'push'} params.operation - Git operation to perform.
|
|
1408
|
+
* @param {string} [params.message=''] - Commit message (used by the `commit` operation).
|
|
1409
|
+
* @param {boolean} [params.forceClone=false] - Remove existing clone before re-cloning.
|
|
1410
|
+
* @returns {boolean} `true` on success, `false` if GITHUB_USERNAME is unset or on error.
|
|
1411
|
+
* @memberof UnderpostRepository
|
|
1412
|
+
*/
|
|
1402
1413
|
manageBackupRepo({ repoName, operation, message = '', forceClone = false }) {
|
|
1403
1414
|
try {
|
|
1404
1415
|
const username = process.env.GITHUB_USERNAME;
|
|
@@ -1602,6 +1613,52 @@ Prevent build private config repo.`,
|
|
|
1602
1613
|
logger.info('engine-private in /home/dd removed');
|
|
1603
1614
|
}
|
|
1604
1615
|
},
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* Resolves the GitHub repository for a given instance runtime by scanning
|
|
1619
|
+
* every `conf.instances.json` listed in `./engine-private/deploy/dd.router`.
|
|
1620
|
+
*
|
|
1621
|
+
* Resolution order:
|
|
1622
|
+
* 1. If `runtime` is falsy, returns `${GITHUB_USERNAME}/engine`.
|
|
1623
|
+
* 2. Iterates each deploy ID found in `dd.router` and looks for an instance
|
|
1624
|
+
* whose `runtime` field matches the supplied value.
|
|
1625
|
+
* 3. When a match is found, returns `instance.metadata.repository`.
|
|
1626
|
+
* 4. Falls back to `${GITHUB_USERNAME}/engine` when no match is found.
|
|
1627
|
+
*
|
|
1628
|
+
* @param {string} [runtime=''] - The runtime identifier to look up (e.g. `'cyberia-server'`, `'cyberia-client'`).
|
|
1629
|
+
* @returns {string} The resolved `owner/repo` string.
|
|
1630
|
+
* @memberof UnderpostRepository
|
|
1631
|
+
*/
|
|
1632
|
+
resolveInstanceRepo(runtime = '') {
|
|
1633
|
+
const fallback = `${process.env.GITHUB_USERNAME}/engine`;
|
|
1634
|
+
if (!runtime) return fallback;
|
|
1635
|
+
const ddRouter = './engine-private/deploy/dd.router';
|
|
1636
|
+
const deployIds = fs.existsSync(ddRouter)
|
|
1637
|
+
? fs
|
|
1638
|
+
.readFileSync(ddRouter, 'utf8')
|
|
1639
|
+
.split(',')
|
|
1640
|
+
.map((s) => s.trim())
|
|
1641
|
+
.filter(Boolean)
|
|
1642
|
+
: [];
|
|
1643
|
+
for (const deployId of deployIds) {
|
|
1644
|
+
const confPath = `./engine-private/conf/${deployId}/conf.instances.json`;
|
|
1645
|
+
if (!fs.existsSync(confPath)) continue;
|
|
1646
|
+
try {
|
|
1647
|
+
const instances = JSON.parse(fs.readFileSync(confPath, 'utf8'));
|
|
1648
|
+
const match = instances.find((i) => i && i.runtime === runtime);
|
|
1649
|
+
if (match && match.metadata && match.metadata.repository) {
|
|
1650
|
+
logger.info(`[resolveInstanceRepo] resolved from ${confPath}`, {
|
|
1651
|
+
runtime,
|
|
1652
|
+
repo: match.metadata.repository,
|
|
1653
|
+
});
|
|
1654
|
+
return match.metadata.repository;
|
|
1655
|
+
}
|
|
1656
|
+
} catch (err) {
|
|
1657
|
+
logger.warn(`[resolveInstanceRepo] failed to parse ${confPath}: ${err.message}`);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return fallback;
|
|
1661
|
+
},
|
|
1605
1662
|
};
|
|
1606
1663
|
}
|
|
1607
1664
|
|