underpost 3.2.11 → 3.2.14

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/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  <div align="center">
18
18
 
19
- [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![](https://data.jsdelivr.com/v1/package/npm/underpost/badge)](https://www.jsdelivr.com/package/npm/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/3.2.11)](https://socket.dev/npm/package/underpost/overview/3.2.11) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
19
+ [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![](https://data.jsdelivr.com/v1/package/npm/underpost/badge)](https://www.jsdelivr.com/package/npm/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/3.2.14)](https://socket.dev/npm/package/underpost/overview/3.2.14) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
20
20
 
21
21
  </div>
22
22
 
@@ -41,7 +41,7 @@ The project covers:
41
41
 
42
42
  ### Architectural roles (Cyberia stack)
43
43
 
44
- When the platform is hosting the Cyberia MMO extension, three runtime processes participate. Their boundaries are non-overlapping and their startup order is **strictly sequential** — not parallel.
44
+ When the platform is hosting the Cyberia MMO extension, three independent runtime processes participate. Their boundaries are non-overlapping.
45
45
 
46
46
  | Process | Role |
47
47
  | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -49,13 +49,9 @@ When the platform is hosting the Cyberia MMO extension, three runtime processes
49
49
  | **cyberia-server** (Go) | Authoritative simulation runtime: tick advancement, AOI replication, input command processing, snapshot generation. |
50
50
  | **cyberia-client** (C → WebAssembly) | Presentation runtime: rendering, UI, input capture, prediction, reconciliation, interpolation, client-side presentation defaults. |
51
51
 
52
- **Startup order sequential, in order:**
52
+ The ecosystem is **playable only when all three are running and healthy**. Each service is supervised independently and owns its own monitor/reconnector. If any one is unhealthy, the game enters standby and resumes automatically once all three are healthy again.
53
53
 
54
- 1. **Persistent backend / sidecar data layer** — databases, content backend (engine-cyberia), static asset backend, config and data services.
55
- 2. **cyberia-server** — authoritative Go simulation runtime; dials engine-cyberia gRPC at boot to load world configuration.
56
- 3. **cyberia-client** — rendering, UI, prediction, interpolation, reconciliation; connects to cyberia-server via WebSocket.
57
-
58
- <a target="_top" href="https://github.com/underpostnet/engine-cyberia/blob/master/src/client/public/cyberia-docs/ARCHITECTURE.md">See Detailed architecture.</a>
54
+ <a target="_top" href="https://github.com/underpostnet/engine-cyberia/blob/master/src/client/public/cyberia-docs/ARCHITECTURE.md">See detailed Cyberia architecture.</a>
59
55
 
60
56
  ## Create a new project
61
57
 
@@ -90,46 +86,52 @@ npm run dev
90
86
  <a target="_top" href="https://www.nexodev.org/docs?cid=src">See Docs.</a>
91
87
 
92
88
  <!-- cli-index-start -->
93
- ## underpost ci/cd cli v3.2.11
94
-
95
- ### Usage: `underpost [options] [command]`
96
- ```
97
- Options:
98
- -V, --version output the version number
99
- -h, --help display help for command
100
-
101
- Commands:
102
- new [options] [app-name] Initializes a new Underpost project, service, or configuration.
103
- client [options] [deploy-id] [sub-conf] [host] [path] Builds client assets, single replicas, and/or syncs environment ports.
104
- start [options] <deploy-id> [env] Initiates application servers, build pipelines, or other defined services based on the deployment ID.
105
- clone [options] <uri> Clones a specified GitHub repository into the current directory.
106
- pull [options] <path> <uri> Pulls the latest changes from a specified GitHub repository.
107
- cmt [options] [path] [commit-type] [module-tag] [message] Manages commits to a GitHub repository, supporting various commit types and options.
108
- push [options] <path> <uri> Pushes committed changes from a local repository to a remote GitHub repository.
109
- env [deploy-id] [env] [subConf] Sets environment variables and configurations related to a specific deployment ID.
110
- static [options] Manages static build of page, bundles, and documentation with comprehensive customization options.
111
- config [options] <operator> [key] [value] Manages Underpost configurations using various operators.
112
- root Displays the root path of the npm installation.
113
- ip [options] [ips] Displays the current public machine IP addresses.
114
- cluster [options] [pod-name] Manages Kubernetes clusters, defaulting to Kind cluster initialization.
115
- deploy [options] [deploy-list] [env] Manages application deployments, defaulting to deploying development pods.
116
- secret [options] <platform> Manages secrets for various platforms.
117
- image [options] Manages Docker images, including building, saving, and loading into Kubernetes clusters.
118
- install Quickly imports Underpost npm dependencies by copying them.
119
- db [options] [deploy-list] Manages database operations with support for MariaDB and MongoDB, including import/export, multi-pod targeting, and Git integration.
120
- metadata [options] [deploy-id] [host] [path] Manages cluster metadata operations, including import and export.
121
- cron [options] [deploy-list] [job-list] Manages cron jobs: execute jobs directly or generate and apply K8s CronJob manifests.
122
- fs [options] [path] Manages file storage, defaulting to file upload operations.
123
- test [options] [deploy-list] Manages and runs tests, defaulting to the current Underpost default test suite.
124
- monitor [options] <deploy-id> [env] Manages health server monitoring for specified deployments.
125
- ssh [options] Manages SSH credentials and sessions for remote access to cluster nodes or services.
126
- run [options] <runner-id> [path] Runs specified scripts using various runners.
127
- lxd [options] [vm-id] Manages LXD virtual machines as K3s nodes (control plane or workers).
128
- baremetal [options] [workflow-id] Manages baremetal server operations, including installation, database setup, commissioning, and user management.
129
- release [options] [version] Release orchestrator for building new versions and deploying releases of the Underpost CLI.
130
- help [command] display help for command
131
-
132
- ```
89
+ ## Underpost CLI
90
+
91
+ > underpost ci/cd cli v3.2.14
92
+
93
+ **Usage:** `underpost [options] [command]`
94
+
95
+ ### Global options
96
+
97
+ | Option | Description |
98
+ | --- | --- |
99
+ | `-V, --version` | output the version number |
100
+ | `-h, --help` | display help for command |
101
+
102
+ ### Commands
103
+
104
+ | Command | Description |
105
+ | --- | --- |
106
+ | [`new`](CLI-HELP.md#underpost-new) | Initializes a new Underpost project, service, or configuration. |
107
+ | [`client`](CLI-HELP.md#underpost-client) | Builds client assets, single replicas, and/or syncs environment ports. |
108
+ | [`start`](CLI-HELP.md#underpost-start) | Initiates application servers, build pipelines, or other defined services based on the deployment ID. |
109
+ | [`clone`](CLI-HELP.md#underpost-clone) | Clones a specified GitHub repository into the current directory. |
110
+ | [`pull`](CLI-HELP.md#underpost-pull) | Pulls the latest changes from a specified GitHub repository. |
111
+ | [`cmt`](CLI-HELP.md#underpost-cmt) | Manages commits to a GitHub repository, supporting various commit types and options. |
112
+ | [`push`](CLI-HELP.md#underpost-push) | Pushes committed changes from a local repository to a remote GitHub repository. |
113
+ | [`env`](CLI-HELP.md#underpost-env) | Sets environment variables and configurations related to a specific deployment ID. |
114
+ | [`static`](CLI-HELP.md#underpost-static) | Manages static build of page, bundles, and documentation with comprehensive customization options. |
115
+ | [`config`](CLI-HELP.md#underpost-config) | Manages Underpost configurations using various operators. |
116
+ | [`root`](CLI-HELP.md#underpost-root) | Displays the root path of the npm installation. |
117
+ | [`ip`](CLI-HELP.md#underpost-ip) | Displays the current public machine IP addresses. |
118
+ | [`cluster`](CLI-HELP.md#underpost-cluster) | Manages Kubernetes clusters, defaulting to Kind cluster initialization. |
119
+ | [`deploy`](CLI-HELP.md#underpost-deploy) | Manages application deployments, defaulting to deploying development pods. |
120
+ | [`secret`](CLI-HELP.md#underpost-secret) | Manages secrets for various platforms. |
121
+ | [`image`](CLI-HELP.md#underpost-image) | Manages Docker images, including building, saving, and loading into Kubernetes clusters. |
122
+ | [`install`](CLI-HELP.md#underpost-install) | Quickly imports Underpost npm dependencies by copying them. |
123
+ | [`db`](CLI-HELP.md#underpost-db) | Manages database operations with support for MariaDB and MongoDB, including import/export, multi-pod targeting, and Git integration. |
124
+ | [`metadata`](CLI-HELP.md#underpost-metadata) | Manages cluster metadata operations, including import and export. |
125
+ | [`cron`](CLI-HELP.md#underpost-cron) | Manages cron jobs: execute jobs directly or generate and apply K8s CronJob manifests. |
126
+ | [`fs`](CLI-HELP.md#underpost-fs) | Manages file storage, defaulting to file upload operations. |
127
+ | [`test`](CLI-HELP.md#underpost-test) | Manages and runs tests, defaulting to the current Underpost default test suite. |
128
+ | [`monitor`](CLI-HELP.md#underpost-monitor) | Manages health server monitoring for specified deployments. |
129
+ | [`ssh`](CLI-HELP.md#underpost-ssh) | Manages SSH credentials and sessions for remote access to cluster nodes or services. |
130
+ | [`run`](CLI-HELP.md#underpost-run) | Runs specified scripts using various runners. |
131
+ | [`lxd`](CLI-HELP.md#underpost-lxd) | Manages LXD virtual machines as K3s nodes (control plane or workers). |
132
+ | [`baremetal`](CLI-HELP.md#underpost-baremetal) | Manages baremetal server operations, including installation, database setup, commissioning, and user management. |
133
+ | [`release`](CLI-HELP.md#underpost-release) | Release orchestrator for building new versions and deploying releases of the Underpost CLI. |
134
+
133
135
  <!-- cli-index-end -->
134
136
 
135
137
  <a target="_top" href="https://github.com/underpostnet/pwa-microservices-template/blob/master/CLI-HELP.md">See CLI Docs.</a>
package/bin/build.js CHANGED
@@ -1,105 +1,64 @@
1
+ #! /usr/bin/env node
2
+
3
+ import { Command } from 'commander';
1
4
  import fs from 'fs-extra';
2
- import { loggerFactory } from '../src/server/logger.js';
3
- import { shellExec } from '../src/server/process.js';
4
5
  import dotenv from 'dotenv';
6
+ import { loggerFactory } from '../src/server/logger.js';
5
7
  import { getCapVariableName } from '../src/client/components/core/CommonJs.js';
6
- import { getPathsSSR } from '../src/server/conf.js';
8
+ import {
9
+ getPathsSSR,
10
+ resolveDeployList,
11
+ syncPrivateConf,
12
+ syncDeployIdSources,
13
+ buildTemplate,
14
+ } from '../src/server/conf.js';
15
+ import { loadDeployCatalog } from '../src/server/catalog.js';
16
+ import UnderpostRepository from '../src/cli/repository.js';
7
17
 
8
18
  const baseConfPath = './engine-private/conf/dd-cron/.env.production';
9
19
  if (fs.existsSync(baseConfPath)) dotenv.config({ path: baseConfPath, override: true });
10
20
 
11
21
  const logger = loggerFactory(import.meta);
12
22
 
13
- const confName = process.argv[2];
14
23
  const basePath = '../pwa-microservices-template';
15
- const repoName = `engine-${confName.split('dd-')[1]}`;
16
- const deployList = (confName === 'dd' ? fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8') : confName).split(
17
- ',',
18
- );
19
-
20
- logger.info('Build repository', {
21
- confName,
22
- repoName,
23
- basePath,
24
- deployList,
25
- });
26
-
27
- if (process.argv.includes('conf')) {
28
- for (const _confName of deployList) {
29
- const _repoName = `engine-${_confName.split('dd-')[1]}`;
30
- const privateRepoName = `${_repoName}-private`;
31
- const privateGitUri = `${process.env.GITHUB_USERNAME}/${privateRepoName}`;
32
-
33
- if (!fs.existsSync(`../${privateRepoName}`)) {
34
- shellExec(`cd .. && underpost clone ${privateGitUri}`, { silent: true });
35
- } else {
36
- shellExec(`cd ../${privateRepoName} && git checkout . && git clean -f -d && underpost pull . ${privateGitUri}`, {
37
- silent: true,
38
- });
39
- }
40
- const toPath = `../${privateRepoName}/conf/${_confName}`;
41
- fs.removeSync(toPath);
42
- fs.mkdirSync(toPath, { recursive: true });
43
- fs.copySync(`./engine-private/conf/${_confName}`, toPath);
44
- fs.removeSync(`../${privateRepoName}/replica`);
45
- if (fs.existsSync(`./engine-private/replica`)) {
46
- const replicas = await fs.readdir(`./engine-private/replica`);
47
- for (const replica of replicas)
48
- if (replica.match(_confName))
49
- fs.copySync(`./engine-private/replica/${replica}`, `../${privateRepoName}/replica/${replica}`);
50
- }
51
24
 
52
- if (fs.existsSync(`./engine-private/itc-scripts`)) {
53
- const itcScripts = await fs.readdir(`./engine-private/itc-scripts`);
54
- for (const itcScript of itcScripts)
55
- if (itcScript.match(_confName))
56
- fs.copySync(`./engine-private/itc-scripts/${itcScript}`, `../${privateRepoName}/itc-scripts/${itcScript}`);
57
- }
58
- switch (_confName) {
59
- case 'dd-cyberia':
60
- fs.copySync(`./engine-private/cyberia-instances/FOREST`, `../${privateRepoName}/cyberia-instances/FOREST`);
61
- break;
62
- default:
63
- break;
64
- }
65
- shellExec(
66
- `cd ../${privateRepoName}` +
67
- ` && git add .` +
68
- ` && underpost cmt . ci engine-core-conf 'Update ${_confName} conf'` +
69
- ` && underpost push . ${privateGitUri}`,
70
- {
71
- silent: true,
72
- silentOnError: true,
73
- },
74
- );
25
+ /**
26
+ * Assembles a single deploy id's public template under {@link basePath}: pulls in
27
+ * its deploy-id-specific public sources, then mirrors the APIs, client components,
28
+ * SSR assets, manifests, and packaging declared by its conf into the template repo.
29
+ * @param {string} confName - A concrete deploy id (e.g. `dd-prototype`).
30
+ */
31
+ const buildDeployTemplate = async (confName) => {
32
+ const repoName = `engine-${confName.split('dd-')[1]}`;
33
+ const catalog = await loadDeployCatalog(confName);
34
+
35
+ if (catalog.sourceMoves.length) {
36
+ UnderpostRepository.API.sparseCheckoutDirectory(`conf/${confName}`);
37
+ if (catalog.sourceMoves.some(([src]) => !fs.existsSync(src))) UnderpostRepository.API.pullSourceRepo(repoName);
75
38
  }
76
- process.exit(0);
77
- }
39
+ syncDeployIdSources(catalog.sourceMoves);
78
40
 
79
- if (confName === 'dd') {
80
- for (const _confName of deployList) {
81
- shellExec(`node bin/build ${_confName}`);
82
- }
83
- process.exit(0);
84
- }
41
+ const confDir = `./engine-private/conf/${confName}`;
42
+ const DefaultConf = {
43
+ server: JSON.parse(fs.readFileSync(`${confDir}/conf.server.json`, 'utf8')),
44
+ client: JSON.parse(fs.readFileSync(`${confDir}/conf.client.json`, 'utf8')),
45
+ ssr: JSON.parse(fs.readFileSync(`${confDir}/conf.ssr.json`, 'utf8')),
46
+ };
85
47
 
86
- const { DefaultConf } = await import(`../conf.${confName}.js`);
87
-
88
- {
89
48
  for (const host of Object.keys(DefaultConf.server)) {
90
49
  for (const path of Object.keys(DefaultConf.server[host])) {
91
50
  const { apis, ws } = DefaultConf.server[host][path];
92
51
  if (apis)
93
52
  for (const api of apis) {
94
- {
95
- const originPath = `./src/api/${api}`;
96
- logger.info(`Build`, originPath);
97
- fs.copySync(originPath, `${basePath}/src/api/${api}`);
53
+ const apiSrc = `./src/api/${api}`;
54
+ if (fs.existsSync(apiSrc)) {
55
+ logger.info(`Build`, apiSrc);
56
+ fs.copySync(apiSrc, `${basePath}/src/api/${api}`);
98
57
  }
99
- {
100
- const originPath = `./src/client/services/${api}`;
101
- logger.info(`Build`, originPath);
102
- fs.copySync(originPath, `${basePath}/src/client/services/${api}`);
58
+ const serviceSrc = `./src/client/services/${api}`;
59
+ if (fs.existsSync(serviceSrc)) {
60
+ logger.info(`Build`, serviceSrc);
61
+ fs.copySync(serviceSrc, `${basePath}/src/client/services/${api}`);
103
62
  }
104
63
  }
105
64
 
@@ -108,9 +67,7 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
108
67
  }
109
68
  }
110
69
  }
111
- }
112
70
 
113
- {
114
71
  for (const client of Object.keys(DefaultConf.client)) {
115
72
  const capName = getCapVariableName(client);
116
73
  for (const component of Object.keys(DefaultConf.client[client].components)) {
@@ -135,9 +92,7 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
135
92
  }
136
93
  }
137
94
  }
138
- }
139
95
 
140
- {
141
96
  for (const client of Object.keys(DefaultConf.ssr)) {
142
97
  const ssrPaths = getPathsSSR(DefaultConf.ssr[client]);
143
98
  for (const originPath of ssrPaths) {
@@ -149,16 +104,14 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
149
104
  }
150
105
 
151
106
  if (!fs.existsSync(`${basePath}/.github/workflows`))
152
- fs.mkdirSync(`${basePath}/.github/workflows`, {
153
- recursive: true,
154
- });
107
+ fs.mkdirSync(`${basePath}/.github/workflows`, { recursive: true });
155
108
 
156
109
  const originPackageJson = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
157
110
  const packageJson = JSON.parse(fs.readFileSync(`${basePath}/package.json`, 'utf8'));
158
111
  packageJson.name = repoName.replace('engine-', '');
159
112
 
160
113
  switch (confName) {
161
- case 'dd-cyberia':
114
+ case 'dd-cyberia': {
162
115
  fs.copyFileSync(`./bin/cyberia.js`, `${basePath}/bin/cyberia.js`);
163
116
  fs.copyFileSync(
164
117
  `./.github/workflows/publish.cyberia.ci.yml`,
@@ -167,59 +120,24 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
167
120
  if (packageJson.bin) delete packageJson.bin.underpost;
168
121
  if (!packageJson.bin) packageJson.bin = {};
169
122
  packageJson.bin.cyberia = 'bin/index.js';
170
- packageJson.keywords = [
171
- 'cyberia',
172
- 'cyberia-cli',
173
- 'engine-cyberia',
174
- 'sidecar',
175
- 'data-layer',
176
- 'engine-cyberia',
177
- 'object-layer',
178
- 'atlas-sprite-sheet',
179
- 'ipfs',
180
- 'erc-1155',
181
- 'object-layer-token',
182
- 'hardhat',
183
- 'hyperledger-besu',
184
- 'blockchain',
185
- 'web3',
186
- 'underpost-platform',
187
- 'mmorpg',
188
- ];
189
- packageJson.description =
190
- 'Cyberia CLI — toolchain for the Cyberia MMO data layer, content pipeline, persistence, gRPC services, and ERC-1155 lifecycle on Hyperledger Besu.';
123
+ packageJson.keywords = catalog.keywords;
124
+ packageJson.description = catalog.description;
191
125
  const { CyberiaDependencies } = await import(`../src/api/cyberia-server-defaults/cyberia-server-defaults.js`);
192
126
  packageJson.dependencies = {
193
127
  ...originPackageJson.dependencies,
194
128
  ...CyberiaDependencies,
195
129
  };
196
130
  fs.writeFileSync(`${basePath}/bin/index.js`, fs.readFileSync(`./bin/cyberia.js`, 'utf8'), 'utf8');
131
+ // Canonical Cyberia doc; engine-cyberia/README.md is a generated copy — never hand-edited.
197
132
  fs.writeFileSync(
198
133
  `${basePath}/README.md`,
199
- fs.readFileSync(`./src/client/public/cyberia-docs/CYBERIA-CLI.md`, 'utf8'),
134
+ fs.readFileSync(`./src/client/public/cyberia-docs/CYBERIA.md`, 'utf8'),
200
135
  'utf8',
201
136
  );
202
137
  fs.copySync(`./hardhat`, `${basePath}/hardhat`);
203
- for (const path of [
204
- '/src/grpc/cyberia',
205
- '/src/client/ssr/views/CyberiaServerMetrics.js',
206
- '/src/server/object-layer.js',
207
- '/src/server/atlas-sprite-sheet-generator.js',
208
- '/src/server/shape-generator.js',
209
- '/src/server/semantic-layer-generator.js',
210
- '/src/server/semantic-layer-generator-floor.js',
211
- '/src/server/semantic-layer-generator-skin.js',
212
- '/src/server/semantic-layer-generator-resource.js',
213
- '/test/shape-generator.test.js',
214
- '/src/server/besu-genesis-generator.js',
215
- '/src/runtime/cyberia-server',
216
- '/src/runtime/cyberia-client',
217
- '/.github/workflows/hardhat.ci.yml',
218
- '/src/client/public/cyberia-docs',
219
- '/src/api/cyberia-server-defaults',
220
- ])
221
- fs.copySync(`.${path}`, `${basePath}${path}`);
222
-
138
+ for (const path of catalog.templatePaths) fs.copySync(`.${path}`, `${basePath}${path}`);
139
+ break;
140
+ }
223
141
  default:
224
142
  break;
225
143
  }
@@ -247,8 +165,6 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
247
165
  `${basePath}/manifests/deployment/${confName}-development`,
248
166
  );
249
167
 
250
- // Copy conf.<deploy-id>.js to conf.js for the respective deployment
251
- fs.copyFileSync(`./conf.${confName}.js`, `${basePath}/conf.js`);
252
168
  fs.copyFileSync(`./manifests/deployment/${confName}-development/proxy.yaml`, `${basePath}/proxy.yaml`);
253
169
  fs.copyFileSync(`./manifests/deployment/${confName}-development/deployment.yaml`, `${basePath}/deployment.yaml`);
254
170
  const pvPvcPath = `./manifests/deployment/${confName}-development/pv-pvc.yaml`;
@@ -257,6 +173,41 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
257
173
  if (fs.existsSync(`./src/ws/${confName.split('-')[1]}`)) {
258
174
  fs.copySync(`./src/ws/${confName.split('-')[1]}`, `${basePath}/src/ws/${confName.split('-')[1]}`);
259
175
  }
260
- fs.copyFileSync(`.gitignore`, `${basePath}/.gitignore`);
261
- shellExec(`cd ${basePath} && npm install --ignore-scripts`);
262
- }
176
+ fs.writeFileSync(
177
+ `${basePath}/.gitignore`,
178
+ fs.readFileSync(`.gitignore`, 'utf8').split('# Ignore ERP / CRM custom prototypes src')[0],
179
+ );
180
+ };
181
+
182
+ const program = new Command();
183
+
184
+ program
185
+ .name('build')
186
+ .description('Assemble deploy id public templates and sync their private configuration repos.')
187
+ .argument('<conf-name>', 'Deploy id, comma-separated list, or the "dd" meta id (fans out via dd.router).')
188
+ .argument('[env]', 'Environment label (informational; kept for CI invocation compatibility).')
189
+ .option('--conf', 'Sync each deploy id private configuration repo and exit (no template assembly).')
190
+ .option(
191
+ '--no-template-rebuild',
192
+ 'Skip the from-scratch base template reconstruction before assembly (assemble onto the existing template).',
193
+ )
194
+ .action(async (confName, env, options) => {
195
+ const deployList = resolveDeployList(confName);
196
+ logger.info('Build repository', { confName, basePath, deployList, conf: !!options.conf });
197
+
198
+ if (options.conf) {
199
+ for (const deployId of deployList) {
200
+ const { privateConfPaths } = await loadDeployCatalog(deployId);
201
+ syncPrivateConf(deployId, privateConfPaths);
202
+ }
203
+ return;
204
+ }
205
+
206
+ // Reconstruct the base template from 0 before assembly so no src from a previous
207
+ // build run leaks into this one. Opt out with --no-template-rebuild.
208
+ if (options.templateRebuild) await buildTemplate({ toPath: basePath });
209
+
210
+ for (const deployId of deployList) await buildDeployTemplate(deployId);
211
+ });
212
+
213
+ await program.parseAsync();