@underpostnet/underpost 2.99.4 → 2.99.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.
@@ -261,6 +261,12 @@ curl -X POST \\
261
261
  * @param {boolean} params.ubuntuToolsBuild - Flag to determine if Ubuntu tools should be built.
262
262
  * @param {string} [params.bootcmd] - Optional custom commands to run during boot.
263
263
  * @param {string} [params.runcmd] - Optional custom commands to run during first boot.
264
+ * @param {object} [params.write_files] - Optional array of files to write during cloud-init, each with path, permissions, owner, and content.
265
+ * @param {string} [params.write_files[].path] - The file path where the content will be written on the target machine.
266
+ * @param {string} [params.write_files[].permissions] - The file permissions to set for the written file (e.g., '0644').
267
+ * @param {string} [params.write_files[].owner] - The owner of the written file (e.g., 'root:root').
268
+ * @param {string} [params.write_files[].content] - The content to write into the file.
269
+ * @param {boolean} [params.write_files[].defer] - Whether to defer writing the file until the 'final' stage of cloud-init.
264
270
  * @param {object} [authCredentials={}] - Optional MAAS authentication credentials.
265
271
  * @returns {object} The generated cloud-init configuration content.
266
272
  * @memberof UnderpostCloudInit
@@ -278,6 +284,15 @@ curl -X POST \\
278
284
  ubuntuToolsBuild,
279
285
  bootcmd: bootcmdParam,
280
286
  runcmd: runcmdParam,
287
+ write_files = [
288
+ {
289
+ path: '',
290
+ permissions: '',
291
+ owner: '',
292
+ content: '',
293
+ defer: false,
294
+ },
295
+ ],
281
296
  },
282
297
  authCredentials = { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
283
298
  ) {
@@ -305,6 +320,7 @@ curl -X POST \\
305
320
  let runcmd = [
306
321
  'echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"',
307
322
  'echo "Init runcmd"',
323
+ 'systemctl enable --now ssh',
308
324
  'echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"',
309
325
  ];
310
326
 
@@ -334,12 +350,14 @@ curl -X POST \\
334
350
  name: process.env.MAAS_ADMIN_USERNAME,
335
351
  sudo: ['ALL=(ALL) NOPASSWD:ALL'],
336
352
  shell: '/bin/bash',
337
- lock_passwd: false,
353
+ lock_passwd: true,
338
354
  groups: 'sudo,users,admin,wheel,lxd',
339
- plain_text_passwd: process.env.MAAS_ADMIN_USERNAME,
355
+ // plain_text_passwd: process.env.MAAS_ADMIN_USERNAME,
340
356
  ssh_authorized_keys: [fs.readFileSync(`/home/dd/engine/engine-private/deploy/id_rsa.pub`, 'utf8')],
341
357
  },
342
358
  ],
359
+ disable_root: true,
360
+ ssh_pwauth: false,
343
361
  timezone,
344
362
  ntp: {
345
363
  enabled: true,
@@ -360,6 +378,7 @@ curl -X POST \\
360
378
  'smartmontools',
361
379
  'net-tools',
362
380
  'util-linux',
381
+ 'openssh-server',
363
382
  ],
364
383
  resize_rootfs: false,
365
384
  growpart: { mode: 'off' },
@@ -380,7 +399,7 @@ curl -X POST \\
380
399
  final_message: '====== Cloud init finished ======',
381
400
  bootcmd,
382
401
  runcmd,
383
- disable_root: true,
402
+ write_files,
384
403
  preserve_hostname: false,
385
404
  cloud_init_modules: [
386
405
  // minimal for commissioning
@@ -394,37 +413,37 @@ curl -X POST \\
394
413
  'ssh', // enable/configure SSH for temporary access
395
414
 
396
415
  // optional modules (commented out by default)
397
- // 'migrator',
398
- // 'seed_random',
399
- // 'growpart',
400
- // 'resizefs',
401
- // 'users-groups',
416
+ 'migrator',
417
+ 'seed_random',
418
+ 'growpart',
419
+ 'resizefs',
420
+ 'users-groups',
402
421
  ],
403
422
  cloud_config_modules: [
404
423
  // minimal so MAAS can run commissioning scripts
405
424
  'runcmd', // commissioning / final script execution
406
425
  'mounts', // mount devices during commissioning if needed
407
- // 'ntp', // optional — enable if you want time sync
426
+ 'ntp', // optional — enable if you want time sync
408
427
 
409
428
  // typically not required for basic commissioning (commented)
410
- // 'emit_upstart',
411
- // 'disk_setup',
412
- // 'ssh-import-id',
413
- // 'locale',
414
- // 'set-passwords',
415
- // 'grub-dpkg',
416
- // 'apt-pipelining',
417
- // 'apt-configure',
418
- // 'package-update-upgrade-install', // heavy; do NOT enable by default
419
- // 'landscape',
420
- // 'timezone',
421
- // 'puppet',
422
- // 'chef',
423
- // 'salt-minion',
424
- // 'mcollective',
425
- // 'disable-ec2-metadata',
426
- // 'byobu',
427
- // 'ssh-import-id', // duplicate in original list
429
+ 'emit_upstart',
430
+ 'disk_setup',
431
+ 'ssh-import-id',
432
+ 'locale',
433
+ 'set-passwords',
434
+ 'grub-dpkg',
435
+ 'apt-pipelining',
436
+ 'apt-configure',
437
+ 'package-update-upgrade-install', // heavy; do NOT enable by default
438
+ 'landscape',
439
+ 'timezone',
440
+ 'puppet',
441
+ 'chef',
442
+ 'salt-minion',
443
+ 'mcollective',
444
+ 'disable-ec2-metadata',
445
+ 'byobu',
446
+ 'ssh-import-id', // duplicate in original list
428
447
  ],
429
448
  cloud_final_modules: [
430
449
  // minimal suggestions so final scripts run and node reports status
@@ -432,64 +451,19 @@ curl -X POST \\
432
451
  'final-message', // useful for logs/reporting
433
452
 
434
453
  // optional / commented
435
- // 'rightscale_userdata',
436
- // 'scripts-vendor',
437
- // 'scripts-per-once',
438
- // 'scripts-user',
439
- // 'ssh-authkey-fingerprints',
440
- // 'keys-to-console',
441
- // 'power-state-change', // use carefully (can poweroff/reboot)
454
+ 'rightscale_userdata',
455
+ 'scripts-vendor',
456
+ 'scripts-per-once',
457
+ 'scripts-user',
458
+ 'ssh-authkey-fingerprints',
459
+ 'keys-to-console',
460
+ 'power-state-change', // use carefully (can poweroff/reboot)
442
461
  ],
443
462
  });
444
463
 
445
464
  return { cloudConfigSrc };
446
465
  },
447
466
 
448
- /**
449
- * @method authCredentialsFactory
450
- * @description Retrieves MAAS API key credentials from the MAAS CLI.
451
- * This method parses the output of `maas apikey` to extract the consumer key,
452
- * consumer secret, token key, and token secret.
453
- * @returns {object} An object containing the MAAS authentication credentials.
454
- * @memberof UnderpostCloudInit
455
- * @throws {Error} If the MAAS API key format is invalid.
456
- */
457
- authCredentialsFactory() {
458
- // Expected formats:
459
- // <consumer_key>:<consumer_token>:<secret> (older format)
460
- // <consumer_key>:<consumer_secret>:<token_key>:<token_secret> (newer format)
461
- // Commands used to generate API keys:
462
- // maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}
463
- // maas ${process.env.MAAS_ADMIN_USERNAME} account create-authorisation-token
464
- // maas apikey --generate --username ${process.env.MAAS_ADMIN_USERNAME}
465
- // Reference: https://github.com/CanonicalLtd/maas-docs/issues/647
466
-
467
- const parts = shellExec(`maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}`, {
468
- stdout: true,
469
- })
470
- .trim()
471
- .split(`\n`)[0] // Take only the first line of output.
472
- .split(':'); // Split by colon to get individual parts.
473
-
474
- let consumer_key, consumer_secret, token_key, token_secret;
475
-
476
- // Determine the format of the API key and assign parts accordingly.
477
- if (parts.length === 4) {
478
- [consumer_key, consumer_secret, token_key, token_secret] = parts;
479
- } else if (parts.length === 3) {
480
- // Handle older 3-part format, setting consumer_secret as empty.
481
- [consumer_key, token_key, token_secret] = parts;
482
- consumer_secret = '""';
483
- token_secret = token_secret.split(' MAAS consumer')[0].trim(); // Clean up token secret.
484
- } else {
485
- // Throw an error if the format is not recognized.
486
- throw new Error('Invalid token format');
487
- }
488
-
489
- logger.info('Maas api token generated', { consumer_key, consumer_secret, token_key, token_secret });
490
- return { consumer_key, consumer_secret, token_key, token_secret };
491
- },
492
-
493
467
  /**
494
468
  * @method generateCloudConfig
495
469
  * @description Generates a generic cloud-init configuration string.
@@ -518,6 +492,7 @@ curl -X POST \\
518
492
  growpart,
519
493
  network,
520
494
  runcmd,
495
+ write_files,
521
496
  final_message,
522
497
  bootcmd,
523
498
  disable_root,
@@ -645,6 +620,21 @@ curl -X POST \\
645
620
  runcmd.forEach((cmd) => yaml.push(` - ${cmd}`));
646
621
  }
647
622
 
623
+ if (write_files) {
624
+ yaml.push('write_files:');
625
+ write_files.forEach((file) => {
626
+ yaml.push(` - path: ${file.path}`);
627
+ if (file.encoding) yaml.push(` encoding: ${file.encoding}`);
628
+ if (file.owner) yaml.push(` owner: ${file.owner}`);
629
+ if (file.permissions) yaml.push(` permissions: '${file.permissions}'`);
630
+ if (file.defer) yaml.push(` defer: ${file.defer}`);
631
+ if (file.content) {
632
+ yaml.push(` content: |`);
633
+ file.content.split('\n').forEach((line) => yaml.push(` ${line}`));
634
+ }
635
+ });
636
+ }
637
+
648
638
  if (final_message) yaml.push(`final_message: "${final_message}"`);
649
639
 
650
640
  if (bootcmd) {
@@ -669,6 +659,34 @@ curl -X POST \\
669
659
  }
670
660
 
671
661
  return yaml.join('\n');
662
+ } /**
663
+ * @method kernelParamsFactory
664
+ * @description Generates the kernel parameters for the target machine's bootloader configuration,
665
+ * including the necessary parameters to enable cloud-init with a specific configuration URL and logging settings.
666
+ * @param {array} cmd - The existing array of kernel parameters to which cloud-init parameters will be appended.
667
+ * @param {object} options - Options for generating kernel parameters.
668
+ * @param {string} options.ipDhcpServer - The IP address of the DHCP server.
669
+ * @param {object} [options.machine] - The machine information, including system_id for constructing the cloud-init configuration URL.
670
+ * @param {string} [options.machine.system_id] - The unique identifier of the machine, used to fetch the correct cloud-init preseed configuration from MAAS.
671
+ * @return {array} The modified array of kernel parameters with cloud-init parameters included.
672
+ * @memberof UnderpostCloudInit
673
+ */,
674
+ kernelParamsFactory(
675
+ macAddress,
676
+ cmd = [],
677
+ options = {
678
+ ipDhcpServer: '',
679
+ bootstrapHttpServerPort: 8888,
680
+ hostname: '',
681
+ machine: {
682
+ system_id: '',
683
+ },
684
+ authCredentials: { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
685
+ },
686
+ ) {
687
+ const { ipDhcpServer, bootstrapHttpServerPort, hostname } = options;
688
+ const cloudConfigUrl = `http://${ipDhcpServer}:${bootstrapHttpServerPort}/${hostname}/cloud-init/user-data`;
689
+ return cmd.concat([`cloud-config-url=${cloudConfigUrl}`]);
672
690
  },
673
691
  };
674
692
  }
package/src/cli/env.js CHANGED
@@ -8,6 +8,7 @@ import { getNpmRootPath, writeEnv } from '../server/conf.js';
8
8
  import fs from 'fs-extra';
9
9
  import { loggerFactory } from '../server/logger.js';
10
10
  import dotenv from 'dotenv';
11
+ import { pbcopy } from '../server/process.js';
11
12
 
12
13
  dotenv.config();
13
14
 
@@ -72,9 +73,10 @@ class UnderpostRootEnv {
72
73
  * @param {object} options - Options for getting the environment variable.
73
74
  * @param {boolean} [options.plain=false] - If true, returns the environment variable value as a string.
74
75
  * @param {boolean} [options.disableLog=false] - If true, disables logging of the environment variable value.
76
+ * @param {boolean} [options.copy=false] - If true, copies the environment variable value to the clipboard.
75
77
  * @memberof UnderpostEnv
76
78
  */
77
- get(key, value, options = { plain: false, disableLog: false }) {
79
+ get(key, value, options = { plain: false, disableLog: false, copy: false }) {
78
80
  const exeRootPath = `${getNpmRootPath()}/underpost`;
79
81
  const envPath = `${exeRootPath}/.env`;
80
82
  if (!fs.existsSync(envPath)) {
@@ -84,6 +86,7 @@ class UnderpostRootEnv {
84
86
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
85
87
  if (!options.disableLog)
86
88
  options?.plain === true ? console.log(env[key]) : logger.info(`${key}(${typeof env[key]})`, env[key]);
89
+ if (options.copy === true) pbcopy(env[key]);
87
90
  return env[key];
88
91
  },
89
92
  /**
package/src/cli/image.js CHANGED
@@ -79,8 +79,6 @@ class UnderpostImage {
79
79
  * @param {boolean} [options.kind=false] - If true, load the image archive into a Kind cluster.
80
80
  * @param {boolean} [options.kubeadm=false] - If true, load the image archive into a Kubeadm cluster (uses 'ctr').
81
81
  * @param {boolean} [options.k3s=false] - If true, load the image archive into a K3s cluster (uses 'k3s ctr').
82
- * @param {boolean} [options.secrets=false] - If true, load secrets from the .env file for the build.
83
- * @param {string} [options.secretsPath=''] - Custom path to the .env file for secrets.
84
82
  * @param {boolean} [options.reset=false] - If true, perform a no-cache build.
85
83
  * @param {boolean} [options.dev=false] - If true, use development mode.
86
84
  * @memberof UnderpostImage
@@ -96,27 +94,11 @@ class UnderpostImage {
96
94
  kind: false,
97
95
  kubeadm: false,
98
96
  k3s: false,
99
- secrets: false,
100
- secretsPath: '',
101
97
  reset: false,
102
98
  dev: false,
103
99
  },
104
100
  ) {
105
- let {
106
- path,
107
- imageName,
108
- version,
109
- imagePath,
110
- dockerfileName,
111
- podmanSave,
112
- secrets,
113
- secretsPath,
114
- kind,
115
- kubeadm,
116
- k3s,
117
- reset,
118
- dev,
119
- } = options;
101
+ let { path, imageName, version, imagePath, dockerfileName, podmanSave, kind, kubeadm, k3s, reset, dev } = options;
120
102
  if (!path) path = '.';
121
103
  if (!imageName) imageName = `rockylinux9-underpost:${Underpost.version}`;
122
104
  if (!imagePath) imagePath = '.';
@@ -126,29 +108,14 @@ class UnderpostImage {
126
108
  if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
127
109
  fs.mkdirSync(imagePath, { recursive: true });
128
110
  const tarFile = `${imagePath}/${imageName.replace(':', '_')}.tar`;
129
- let secretsInput = ' ';
130
- let secretDockerInput = '';
131
111
  let cache = '';
132
- if (secrets === true) {
133
- const envObj = dotenv.parse(
134
- fs.readFileSync(
135
- secretsPath && typeof secretsPath === 'string' ? secretsPath : `${getNpmRootPath()}/underpost/.env`,
136
- 'utf8',
137
- ),
138
- );
139
- for (const key of Object.keys(envObj)) {
140
- secretsInput += ` && export ${key}="${envObj[key]}" `;
141
- secretDockerInput += ` --secret id=${key},env=${key} \ `;
142
- }
143
- }
144
112
  if (reset === true) cache += ' --rm --no-cache';
145
- if (path && typeof path === 'string')
113
+ if (path)
146
114
  shellExec(
147
- `cd ${path}${secretsInput}&& sudo podman build -f ./${
115
+ `cd ${path} && sudo podman build -f ./${
148
116
  dockerfileName && typeof dockerfileName === 'string' ? dockerfileName : 'Dockerfile'
149
- } -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput} --network host`,
117
+ } -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache} --network host`,
150
118
  );
151
-
152
119
  if (podmanSave === true) {
153
120
  if (fs.existsSync(tarFile)) fs.removeSync(tarFile);
154
121
  shellExec(`podman save -o ${tarFile} ${podManImg}`);
package/src/cli/index.js CHANGED
@@ -138,9 +138,6 @@ program
138
138
  .option('--head-components <paths>', 'Comma-separated SSR head component paths.')
139
139
  .option('--body-components <paths>', 'Comma-separated SSR body component paths.')
140
140
 
141
- .option('--deploy-id <deploy-id>', 'Build static assets for a specific deployment ID.')
142
- .option('--build', 'Triggers the static build process for the specified deployment ID.')
143
- .option('--build-host <build-host>', 'Sets a custom build host for static documents or assets.')
144
141
  .option('--build-path <build-path>', 'Sets a custom build path for static documents or assets.')
145
142
  .option('--env <env>', 'Sets the environment for the static build (e.g., "development", "production").')
146
143
  .option('--minify', 'Minify HTML output (default: true for production).')
@@ -165,6 +162,7 @@ program
165
162
  .option('--filter <keyword>', 'Filters the list by matching key or value (only for list operation).')
166
163
  .option('--deploy-id <deploy-id>', 'Sets the deployment configuration ID for the operation context.')
167
164
  .option('--build', 'Sets the build context for the operation.')
165
+ .option('--copy', 'Copies the configuration value to the clipboard (only for get operation).')
168
166
  .description(`Manages Underpost configurations using various operators.`)
169
167
  .action((...args) => Underpost.env[args[0]](args[1], args[2], args[3]));
170
168
 
@@ -320,8 +318,6 @@ program
320
318
  .option('--kubeadm', 'Set kubeadm cluster env image context management.')
321
319
  .option('--k3s', 'Set k3s cluster env image context management.')
322
320
  .option('--node-name', 'Set node name for kubeadm or k3s cluster env image context management.')
323
- .option('--secrets', 'Includes Dockerfile environment secrets during the build.')
324
- .option('--secrets-path [secrets-path]', 'Specifies a custom path for Dockerfile environment secrets.')
325
321
  .option('--reset', 'Performs a build without using the cache.')
326
322
  .option('--dev', 'Use development mode.')
327
323
  .option('--pull-dockerhub <dockerhub-image>', 'Sets a custom Docker Hub image for base image pulls.')
@@ -629,6 +625,10 @@ program
629
625
  )
630
626
  .option('--ipxe', 'Chainloads iPXE to normalize identity before commissioning.')
631
627
  .option('--ipxe-rebuild', 'Forces rebuild of iPXE binary with embedded boot script.')
628
+ .option(
629
+ '--ipxe-build-iso <iso-path>',
630
+ 'Builds a standalone iPXE ISO with embedded script for the specified workflow ID.',
631
+ )
632
632
  .option('--install-packer', 'Installs Packer CLI.')
633
633
  .option(
634
634
  '--packer-maas-image-template <template-path>',
@@ -672,7 +672,17 @@ program
672
672
  .option('--rocky-tools-test', 'Tests rocky linux tools in chroot environment.')
673
673
  .option('--bootcmd <bootcmd-list>', 'Comma-separated list of boot commands to execute.')
674
674
  .option('--runcmd <runcmd-list>', 'Comma-separated list of run commands to execute.')
675
- .option('--logs <log-id>', 'Displays logs for log id: dhcp, cloud, machine, cloud-config.')
675
+ .option(
676
+ '--logs <log-id>',
677
+ `Displays logs for log id: ${[
678
+ 'dhcp',
679
+ 'dhcp-lease',
680
+ 'dhcp-lan',
681
+ 'cloud-init',
682
+ 'cloud-init-machine',
683
+ 'cloud-init-config',
684
+ ]}`,
685
+ )
676
686
  .option('--dev', 'Sets the development context environment for baremetal operations.')
677
687
  .option('--ls', 'Lists available boot resources and machines.')
678
688
  .description(
@@ -152,6 +152,7 @@ class UnderpostRepository {
152
152
  .map((commitData, i) => `${i === 0 ? '' : ' && '}git ${diffCmd} ${commitData.hash}`)
153
153
  .join('');
154
154
  if (history[0]) {
155
+ let index = history.length;
155
156
  for (const commit of history) {
156
157
  console.log(
157
158
  shellExec(`git show -s --format=%ci ${commit.hash}`, {
@@ -160,7 +161,8 @@ class UnderpostRepository {
160
161
  disableLog: true,
161
162
  }).trim().green,
162
163
  );
163
- console.log(commit.hash.yellow, commit.message);
164
+ console.log(`${index}`.magenta, commit.hash.yellow, commit.message);
165
+ index--;
164
166
  console.log(
165
167
  shellExec(`git show --name-status --pretty="" ${commit.hash}`, {
166
168
  stdout: true,
package/src/cli/run.js CHANGED
@@ -1234,6 +1234,19 @@ EOF
1234
1234
  shellExec(`./scripts/disk-clean.sh`);
1235
1235
  },
1236
1236
 
1237
+ /**
1238
+ * @method disk-devices
1239
+ * @description Executes the `disk-devices.sh` script to display information about disk devices.
1240
+ * @param {string} path - The input value, identifier, or path for the operation.
1241
+ * @param {Object} options - The default underpost runner options for customizing workflow
1242
+ * @memberof UnderpostRun
1243
+ */
1244
+ 'disk-devices': async (path = '/', options = DEFAULT_OPTION) => {
1245
+ const { underpostRoot } = options;
1246
+ shellExec(`chmod +x ${underpostRoot}/scripts/disk-devices.sh`);
1247
+ shellExec(`${underpostRoot}/scripts/disk-devices.sh`);
1248
+ },
1249
+
1237
1250
  /**
1238
1251
  * @method disk-usage
1239
1252
  * @description Displays disk usage statistics using the `du` command, sorted by size.
@@ -18,40 +18,6 @@ dotenv.config();
18
18
  */
19
19
  class UnderpostSecret {
20
20
  static API = {
21
- /**
22
- * @method docker
23
- * @description Manages the secrets of the application.
24
- * @memberof UnderpostSecret
25
- */
26
- docker: {
27
- /**
28
- * @method init
29
- * @description Initializes the docker secrets.
30
- * @memberof UnderpostSecret
31
- */
32
- init() {
33
- shellExec(`docker swarm init`);
34
- },
35
- /**
36
- * @method createFromEnvFile
37
- * @description Creates a secret from an env file.
38
- * @param {string} envPath - The path to the env file.
39
- * @memberof UnderpostSecret
40
- */
41
- createFromEnvFile(envPath) {
42
- const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
43
- for (const key of Object.keys(envObj)) {
44
- Underpost.secret.docker.set(key, envObj[key]);
45
- }
46
- },
47
- set(key, value) {
48
- shellExec(`docker secret rm ${key}`);
49
- shellExec(`echo "${value}" | docker secret create ${key} -`);
50
- },
51
- list() {
52
- shellExec(`docker secret ls`);
53
- },
54
- },
55
21
  /**
56
22
  * @method underpost
57
23
  * @description Manages the secrets of the application.
package/src/cli/static.js CHANGED
@@ -68,11 +68,8 @@ const logger = loggerFactory(import.meta);
68
68
  * @property {string} [page=''] - SSR component path to render
69
69
  * @property {string} [title='Home'] - Page title (deprecated: use metadata.title)
70
70
  * @property {string} [outputPath='.'] - Output file path
71
- * @property {string} [deployId=''] - Deployment identifier
72
- * @property {string} [buildHost=''] - Build host URL
73
71
  * @property {string} [buildPath='/'] - Build path
74
72
  * @property {string} [env='production'] - Environment (development/production)
75
- * @property {boolean} [build=false] - Whether to trigger build
76
73
  * @property {boolean} [dev=false] - Development mode flag
77
74
  * @property {boolean} [minify=true] - Minify HTML output
78
75
  * @property {MetadataOptions} [metadata={}] - Comprehensive metadata options
@@ -95,11 +92,8 @@ const DefaultStaticGenerationOptions = {
95
92
  page: '',
96
93
  title: '',
97
94
  outputPath: '',
98
- deployId: '',
99
- buildHost: '',
100
95
  buildPath: '/',
101
96
  env: 'production',
102
- build: false,
103
97
  dev: false,
104
98
  minify: true,
105
99
  metadata: {},
@@ -385,11 +379,8 @@ class UnderpostStatic {
385
379
  * @param {string} [options.page] - Path to the SSR component to render
386
380
  * @param {string} [options.title] - Page title (deprecated: use metadata.title)
387
381
  * @param {string} [options.outputPath] - Output file path
388
- * @param {string} [options.deployId] - Deployment identifier
389
- * @param {string} [options.buildHost] - Build host URL
390
382
  * @param {string} [options.buildPath='/'] - Build path
391
383
  * @param {string} [options.env='production'] - Environment (development/production)
392
- * @param {boolean} [options.build=false] - Whether to trigger build
393
384
  * @param {boolean} [options.minify=true] - Minify HTML output
394
385
  * @param {MetadataOptions} [options.metadata={}] - Comprehensive metadata options
395
386
  * @param {Object} [options.scripts={}] - Script injection options
@@ -601,23 +592,6 @@ class UnderpostStatic {
601
592
  throw error;
602
593
  }
603
594
  }
604
-
605
- // Trigger build if requested
606
- if (options.deployId && options.build) {
607
- try {
608
- logger.info(`Triggering build for deployment: ${options.deployId}`);
609
-
610
- shellExec(`underpost env ${options.deployId} ${options.env}`);
611
- shellExec(
612
- `npm run build ${options.deployId}${options.buildHost ? ` ${options.buildHost} ${options.buildPath}` : ``}`,
613
- );
614
-
615
- logger.info('Build completed successfully');
616
- } catch (error) {
617
- logger.error(`Build error: ${error.message}`);
618
- throw error;
619
- }
620
- }
621
595
  },
622
596
 
623
597
  /**
package/src/index.js CHANGED
@@ -25,7 +25,7 @@ import UnderpostDns from './server/dns.js';
25
25
  import UnderpostBackup from './server/backup.js';
26
26
  import UnderpostCron from './server/cron.js';
27
27
  import UnderpostStartUp from './server/start.js';
28
- import UnderpostTls from './server/tls.js';
28
+ import UnderpostTLS from './server/tls.js';
29
29
 
30
30
  /**
31
31
  * Underpost main module methods
@@ -39,7 +39,16 @@ class Underpost {
39
39
  * @type {String}
40
40
  * @memberof Underpost
41
41
  */
42
- static version = 'v2.99.4';
42
+ static version = 'v2.99.5';
43
+
44
+ /**
45
+ * Required Node.js major version
46
+ * @static
47
+ * @type {String}
48
+ * @memberof Underpost
49
+ */
50
+ static majorNodejsVersion = 'v24';
51
+
43
52
  /**
44
53
  * Repository cli API
45
54
  * @static
@@ -233,14 +242,19 @@ class Underpost {
233
242
  /**
234
243
  * TLS/SSL server utilities API
235
244
  * @static
236
- * @type {UnderpostTls.API}
245
+ * @type {UnderpostTLS.API}
237
246
  * @memberof Underpost
238
247
  */
239
248
  static get tls() {
240
- return UnderpostTls.API;
249
+ return UnderpostTLS.API;
241
250
  }
242
251
  }
243
252
 
253
+ if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
254
+ console.warn(
255
+ `${`Underpost Warning: Required Node.js version is `.red}${`${Underpost.majorNodejsVersion}.x`.bgBlue.bold.white}${`, you are using `.red}${process.version.bgRed.bold.white}`,
256
+ );
257
+
244
258
  const up = Underpost;
245
259
 
246
260
  const underpost = Underpost;
@@ -269,7 +283,7 @@ export {
269
283
  UnderpostBackup,
270
284
  UnderpostCron,
271
285
  UnderpostStartUp,
272
- UnderpostTls,
286
+ UnderpostTLS,
273
287
  };
274
288
 
275
289
  export default Underpost;