underpost 2.99.6 → 2.99.7
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/ghpkg.ci.yml +10 -25
- package/.github/workflows/npmpkg.ci.yml +13 -2
- package/CHANGELOG.md +496 -0
- package/README.md +3 -3
- package/bin/deploy.js +13 -3
- package/cli.md +46 -28
- package/jsdoc.json +26 -5
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +9 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +9 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -2
- package/src/cli/baremetal.js +8 -322
- package/src/cli/cloud-init.js +2 -2
- package/src/cli/index.js +12 -1
- package/src/cli/repository.js +166 -13
- package/src/cli/run.js +16 -42
- package/src/cli/ssh.js +1 -1
- package/src/cli/system.js +332 -0
- package/src/db/DataBaseProvider.js +3 -3
- package/src/db/mariadb/MariaDB.js +3 -3
- package/src/db/mongo/MongooseDB.js +3 -3
- package/src/index.js +17 -5
- package/src/mailer/EmailRender.js +3 -3
- package/src/mailer/MailerProvider.js +4 -4
- package/src/server/backup.js +15 -4
- package/src/server/client-build-docs.js +28 -2
- package/src/server/conf.js +6 -5
- package/src/server/cron.js +48 -38
- package/src/server/dns.js +0 -8
package/src/cli/baremetal.js
CHANGED
|
@@ -773,13 +773,13 @@ rm -rf ${artifacts.join(' ')}`);
|
|
|
773
773
|
bootstrapArch,
|
|
774
774
|
callbackMetaData,
|
|
775
775
|
steps: [
|
|
776
|
-
...Underpost.
|
|
777
|
-
...Underpost.
|
|
778
|
-
...Underpost.
|
|
776
|
+
...Underpost.system.factory[systemProvisioning].base(),
|
|
777
|
+
...Underpost.system.factory[systemProvisioning].user(),
|
|
778
|
+
...Underpost.system.factory[systemProvisioning].timezone({
|
|
779
779
|
timezone,
|
|
780
780
|
chronyConfPath,
|
|
781
781
|
}),
|
|
782
|
-
...Underpost.
|
|
782
|
+
...Underpost.system.factory[systemProvisioning].keyboard(keyboard.layout),
|
|
783
783
|
],
|
|
784
784
|
});
|
|
785
785
|
}
|
|
@@ -826,13 +826,13 @@ rm -rf ${artifacts.join(' ')}`);
|
|
|
826
826
|
bootstrapArch,
|
|
827
827
|
callbackMetaData,
|
|
828
828
|
steps: [
|
|
829
|
-
...Underpost.
|
|
830
|
-
...Underpost.
|
|
831
|
-
...Underpost.
|
|
829
|
+
...Underpost.system.factory[systemProvisioning].base(),
|
|
830
|
+
...Underpost.system.factory[systemProvisioning].user(),
|
|
831
|
+
...Underpost.system.factory[systemProvisioning].timezone({
|
|
832
832
|
timezone,
|
|
833
833
|
chronyConfPath: chronyc.chronyConfPath,
|
|
834
834
|
}),
|
|
835
|
-
...Underpost.
|
|
835
|
+
...Underpost.system.factory[systemProvisioning].keyboard(keyboard.layout),
|
|
836
836
|
],
|
|
837
837
|
});
|
|
838
838
|
}
|
|
@@ -2961,320 +2961,6 @@ EOF`);
|
|
|
2961
2961
|
throw new Error(`Unsupported host architecture: ${machine}`);
|
|
2962
2962
|
},
|
|
2963
2963
|
|
|
2964
|
-
/**
|
|
2965
|
-
* @property {object} systemProvisioningFactory
|
|
2966
|
-
* @description A factory object containing functions for system provisioning based on OS type.
|
|
2967
|
-
* Each OS type (e.g., 'ubuntu') provides methods for base system setup, user creation,
|
|
2968
|
-
* timezone configuration, and keyboard layout settings. *
|
|
2969
|
-
* @memberof UnderpostBaremetal
|
|
2970
|
-
* @namespace UnderpostBaremetal.systemProvisioningFactory
|
|
2971
|
-
*/
|
|
2972
|
-
systemProvisioningFactory: {
|
|
2973
|
-
/**
|
|
2974
|
-
* @property {object} ubuntu
|
|
2975
|
-
* @description Provisioning steps for Ubuntu-based systems.
|
|
2976
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory
|
|
2977
|
-
* @namespace UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
2978
|
-
*/
|
|
2979
|
-
ubuntu: {
|
|
2980
|
-
/**
|
|
2981
|
-
* @method base
|
|
2982
|
-
* @description Generates shell commands for basic Ubuntu system provisioning.
|
|
2983
|
-
* This includes updating package lists, installing essential build tools,
|
|
2984
|
-
* kernel modules, cloud-init, SSH server, and other core utilities.
|
|
2985
|
-
* @param {object} params - The parameters for the function.
|
|
2986
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
2987
|
-
* @returns {string[]} An array of shell commands.
|
|
2988
|
-
*/
|
|
2989
|
-
base: () => [
|
|
2990
|
-
// Configure APT sources for Ubuntu ports
|
|
2991
|
-
`cat <<SOURCES | tee /etc/apt/sources.list
|
|
2992
|
-
deb http://ports.ubuntu.com/ubuntu-ports noble main restricted universe multiverse
|
|
2993
|
-
deb http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted universe multiverse
|
|
2994
|
-
deb http://ports.ubuntu.com/ubuntu-ports noble-security main restricted universe multiverse
|
|
2995
|
-
SOURCES`,
|
|
2996
|
-
|
|
2997
|
-
// Update package lists and perform a full system upgrade
|
|
2998
|
-
`apt update -qq`,
|
|
2999
|
-
`apt -y full-upgrade`,
|
|
3000
|
-
|
|
3001
|
-
// Install all essential packages in one consolidated step
|
|
3002
|
-
`DEBIAN_FRONTEND=noninteractive apt install -y build-essential xinput x11-xkb-utils usbutils uuid-runtime linux-image-generic systemd-sysv openssh-server sudo locales udev util-linux iproute2 netplan.io ca-certificates curl wget chrony apt-utils tzdata kmod keyboard-configuration console-setup iputils-ping`,
|
|
3003
|
-
|
|
3004
|
-
// Ensure systemd is the init system
|
|
3005
|
-
`ln -sf /lib/systemd/systemd /sbin/init`,
|
|
3006
|
-
|
|
3007
|
-
// Clean up
|
|
3008
|
-
`apt-get clean`,
|
|
3009
|
-
],
|
|
3010
|
-
/**
|
|
3011
|
-
* @method user
|
|
3012
|
-
* @description Generates shell commands for creating a root user and configuring SSH access.
|
|
3013
|
-
* This is a critical security step for initial access to the provisioned system.
|
|
3014
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
3015
|
-
* @returns {string[]} An array of shell commands.
|
|
3016
|
-
*/
|
|
3017
|
-
user: () => [
|
|
3018
|
-
`useradd -m -s /bin/bash -G sudo root`, // Create a root user with bash shell and sudo privileges.
|
|
3019
|
-
`echo 'root:root' | chpasswd`, // Set a default password for the root user (consider more secure methods for production).
|
|
3020
|
-
`mkdir -p /home/root/.ssh`, // Create .ssh directory for authorized keys.
|
|
3021
|
-
// Add the public SSH key to authorized_keys for passwordless login.
|
|
3022
|
-
`echo '${fs.readFileSync(
|
|
3023
|
-
`/home/dd/engine/engine-private/deploy/id_rsa.pub`,
|
|
3024
|
-
'utf8',
|
|
3025
|
-
)}' > /home/root/.ssh/authorized_keys`,
|
|
3026
|
-
`chown -R root /home/root/.ssh`, // Set ownership for security.
|
|
3027
|
-
`chmod 700 /home/root/.ssh`, // Set permissions for the .ssh directory.
|
|
3028
|
-
`chmod 600 /home/root/.ssh/authorized_keys`, // Set permissions for authorized_keys.
|
|
3029
|
-
],
|
|
3030
|
-
/**
|
|
3031
|
-
* @method timezone
|
|
3032
|
-
* @description Generates shell commands for configuring the system timezone and Chrony (NTP client).
|
|
3033
|
-
* Accurate time synchronization is essential for logging, security, and distributed systems.
|
|
3034
|
-
* @param {object} params - The parameters for the function.
|
|
3035
|
-
* @param {string} params.timezone - The timezone string (e.g., 'America/New_York').
|
|
3036
|
-
* @param {string} params.chronyConfPath - The path to the Chrony configuration file.
|
|
3037
|
-
* @param {string} [alias='chrony'] - The alias for the chrony service.
|
|
3038
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
3039
|
-
* @returns {string[]} An array of shell commands.
|
|
3040
|
-
*/
|
|
3041
|
-
timezone: ({ timezone, chronyConfPath }, alias = 'chrony') => [
|
|
3042
|
-
`export DEBIAN_FRONTEND=noninteractive`, // Set non-interactive mode for Debian packages.
|
|
3043
|
-
`ln -fs /usr/share/zoneinfo/${timezone} /etc/localtime`, // Symlink timezone.
|
|
3044
|
-
`sudo dpkg-reconfigure --frontend noninteractive tzdata`, // Reconfigure timezone data.
|
|
3045
|
-
`sudo timedatectl set-timezone ${timezone}`, // Set timezone using timedatectl.
|
|
3046
|
-
`sudo timedatectl set-ntp true`, // Enable NTP synchronization.
|
|
3047
|
-
|
|
3048
|
-
// Write the Chrony configuration file.
|
|
3049
|
-
`echo '
|
|
3050
|
-
# Use public servers from the pool.ntp.org project.
|
|
3051
|
-
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
|
|
3052
|
-
# pool 2.pool.ntp.org iburst
|
|
3053
|
-
server ${process.env.MAAS_NTP_SERVER} iburst
|
|
3054
|
-
|
|
3055
|
-
# Record the rate at which the system clock gains/losses time.
|
|
3056
|
-
driftfile /var/lib/chrony/drift
|
|
3057
|
-
|
|
3058
|
-
# Allow the system clock to be stepped in the first three updates
|
|
3059
|
-
# if its offset is larger than 1 second.
|
|
3060
|
-
makestep 1.0 3
|
|
3061
|
-
|
|
3062
|
-
# Enable kernel synchronization of the real-time clock (RTC).
|
|
3063
|
-
rtcsync
|
|
3064
|
-
|
|
3065
|
-
# Enable hardware timestamping on all interfaces that support it.
|
|
3066
|
-
#hwtimestamp *
|
|
3067
|
-
|
|
3068
|
-
# Increase the minimum number of selectable sources required to adjust
|
|
3069
|
-
# the system clock.
|
|
3070
|
-
#minsources 2
|
|
3071
|
-
|
|
3072
|
-
# Allow NTP client access from local network.
|
|
3073
|
-
#allow 192.168.0.0/16
|
|
3074
|
-
|
|
3075
|
-
# Serve time even if not synchronized to a time source.
|
|
3076
|
-
#local stratum 10
|
|
3077
|
-
|
|
3078
|
-
# Specify file containing keys for NTP authentication.
|
|
3079
|
-
keyfile /etc/chrony.keys
|
|
3080
|
-
|
|
3081
|
-
# Get TAI-UTC offset and leap seconds from the system tz database.
|
|
3082
|
-
leapsectz right/UTC
|
|
3083
|
-
|
|
3084
|
-
# Specify directory for log files.
|
|
3085
|
-
logdir /var/log/chrony
|
|
3086
|
-
|
|
3087
|
-
# Select which information is logged.
|
|
3088
|
-
#log measurements statistics tracking
|
|
3089
|
-
' > ${chronyConfPath}`,
|
|
3090
|
-
`systemctl stop ${alias}`, // Stop Chrony service before reconfiguring.
|
|
3091
|
-
|
|
3092
|
-
// Enable, restart, and check status of Chrony service.
|
|
3093
|
-
`sudo systemctl enable --now ${alias}`,
|
|
3094
|
-
`sudo systemctl restart ${alias}`,
|
|
3095
|
-
|
|
3096
|
-
// Wait for chrony to synchronize
|
|
3097
|
-
`echo "Waiting for chrony to synchronize..."`,
|
|
3098
|
-
`for i in {1..30}; do chronyc tracking | grep -q "Leap status : Normal" && break || sleep 2; done`,
|
|
3099
|
-
|
|
3100
|
-
`sudo systemctl status ${alias}`,
|
|
3101
|
-
|
|
3102
|
-
// Verify Chrony synchronization.
|
|
3103
|
-
`chronyc sources`,
|
|
3104
|
-
`chronyc tracking`,
|
|
3105
|
-
|
|
3106
|
-
`chronyc sourcestats -v`, // Display source statistics.
|
|
3107
|
-
`timedatectl status`, // Display current time and date settings.
|
|
3108
|
-
],
|
|
3109
|
-
/**
|
|
3110
|
-
* @method keyboard
|
|
3111
|
-
* @description Generates shell commands for configuring the keyboard layout.
|
|
3112
|
-
* This ensures correct input behavior on the provisioned system.
|
|
3113
|
-
* @param {string} [keyCode='en'] - The keyboard layout code (e.g., 'en', 'es').
|
|
3114
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
3115
|
-
* @returns {string[]} An array of shell commands.
|
|
3116
|
-
*/
|
|
3117
|
-
keyboard: (keyCode = 'en') => [
|
|
3118
|
-
`sudo locale-gen en_US.UTF-8`,
|
|
3119
|
-
`sudo update-locale LANG=en_US.UTF-8`,
|
|
3120
|
-
`sudo sed -i 's/XKBLAYOUT="us"/XKBLAYOUT="${keyCode}"/' /etc/default/keyboard`,
|
|
3121
|
-
`sudo dpkg-reconfigure --frontend noninteractive keyboard-configuration`,
|
|
3122
|
-
`sudo systemctl restart keyboard-setup.service`,
|
|
3123
|
-
],
|
|
3124
|
-
},
|
|
3125
|
-
/**
|
|
3126
|
-
* @property {object} rocky
|
|
3127
|
-
* @description Provisioning steps for Rocky Linux-based systems.
|
|
3128
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory
|
|
3129
|
-
* @namespace UnderpostBaremetal.systemProvisioningFactory.rocky
|
|
3130
|
-
*/
|
|
3131
|
-
rocky: {
|
|
3132
|
-
/**
|
|
3133
|
-
* @method base
|
|
3134
|
-
* @description Generates shell commands for basic Rocky Linux system provisioning.
|
|
3135
|
-
* This includes installing Node.js, npm, and underpost CLI tools.
|
|
3136
|
-
* @param {object} params - The parameters for the function.
|
|
3137
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.rocky
|
|
3138
|
-
* @returns {string[]} An array of shell commands.
|
|
3139
|
-
*/
|
|
3140
|
-
base: () => [
|
|
3141
|
-
// Update system and install EPEL repository
|
|
3142
|
-
`dnf -y update`,
|
|
3143
|
-
`dnf -y install epel-release`,
|
|
3144
|
-
|
|
3145
|
-
// Install essential system tools (avoiding duplicates from container packages)
|
|
3146
|
-
`dnf -y install --allowerasing bzip2 openssh-server nano vim-enhanced less openssl-devel git gnupg2 libnsl perl`,
|
|
3147
|
-
`dnf clean all`,
|
|
3148
|
-
|
|
3149
|
-
// Install Node.js
|
|
3150
|
-
`curl -fsSL https://rpm.nodesource.com/setup_24.x | bash -`,
|
|
3151
|
-
`dnf install -y nodejs`,
|
|
3152
|
-
`dnf clean all`,
|
|
3153
|
-
|
|
3154
|
-
// Verify Node.js and npm versions
|
|
3155
|
-
`node --version`,
|
|
3156
|
-
`npm --version`,
|
|
3157
|
-
|
|
3158
|
-
// Install underpost ci/cd cli
|
|
3159
|
-
`npm install -g underpost`,
|
|
3160
|
-
`underpost --version`,
|
|
3161
|
-
],
|
|
3162
|
-
/**
|
|
3163
|
-
* @method user
|
|
3164
|
-
* @description Generates shell commands for creating a root user and configuring SSH access on Rocky Linux.
|
|
3165
|
-
* This is a critical security step for initial access to the provisioned system.
|
|
3166
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.rocky
|
|
3167
|
-
* @returns {string[]} An array of shell commands.
|
|
3168
|
-
*/
|
|
3169
|
-
user: () => [
|
|
3170
|
-
`useradd -m -s /bin/bash -G wheel root`, // Create a root user with bash shell and wheel group (sudo on RHEL)
|
|
3171
|
-
`echo 'root:root' | chpasswd`, // Set a default password for the root user
|
|
3172
|
-
`mkdir -p /home/root/.ssh`, // Create .ssh directory for authorized keys
|
|
3173
|
-
// Add the public SSH key to authorized_keys for passwordless login
|
|
3174
|
-
`echo '${fs.readFileSync(
|
|
3175
|
-
`/home/dd/engine/engine-private/deploy/id_rsa.pub`,
|
|
3176
|
-
'utf8',
|
|
3177
|
-
)}' > /home/root/.ssh/authorized_keys`,
|
|
3178
|
-
`chown -R root:root /home/root/.ssh`, // Set ownership for security
|
|
3179
|
-
`chmod 700 /home/root/.ssh`, // Set permissions for the .ssh directory
|
|
3180
|
-
`chmod 600 /home/root/.ssh/authorized_keys`, // Set permissions for authorized_keys
|
|
3181
|
-
],
|
|
3182
|
-
/**
|
|
3183
|
-
* @method timezone
|
|
3184
|
-
* @description Generates shell commands for configuring the system timezone on Rocky Linux.
|
|
3185
|
-
* @param {object} params - The parameters for the function.
|
|
3186
|
-
* @param {string} params.timezone - The timezone string (e.g., 'America/Santiago').
|
|
3187
|
-
* @param {string} params.chronyConfPath - The path to the Chrony configuration file (optional).
|
|
3188
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.rocky
|
|
3189
|
-
* @returns {string[]} An array of shell commands.
|
|
3190
|
-
*/
|
|
3191
|
-
timezone: ({ timezone, chronyConfPath = '/etc/chrony.conf' }) => [
|
|
3192
|
-
// Set system timezone using both methods (for chroot and running system)
|
|
3193
|
-
`ln -sf /usr/share/zoneinfo/${timezone} /etc/localtime`,
|
|
3194
|
-
`echo '${timezone}' > /etc/timezone`,
|
|
3195
|
-
`timedatectl set-timezone ${timezone} 2>/dev/null`,
|
|
3196
|
-
|
|
3197
|
-
// Configure chrony with local NTP server and common NTP pools
|
|
3198
|
-
`echo '# Local NTP server' > ${chronyConfPath}`,
|
|
3199
|
-
`echo 'server 192.168.1.1 iburst prefer' >> ${chronyConfPath}`,
|
|
3200
|
-
`echo '' >> ${chronyConfPath}`,
|
|
3201
|
-
`echo '# Fallback public NTP servers' >> ${chronyConfPath}`,
|
|
3202
|
-
`echo 'server 0.pool.ntp.org iburst' >> ${chronyConfPath}`,
|
|
3203
|
-
`echo 'server 1.pool.ntp.org iburst' >> ${chronyConfPath}`,
|
|
3204
|
-
`echo 'server 2.pool.ntp.org iburst' >> ${chronyConfPath}`,
|
|
3205
|
-
`echo 'server 3.pool.ntp.org iburst' >> ${chronyConfPath}`,
|
|
3206
|
-
`echo '' >> ${chronyConfPath}`,
|
|
3207
|
-
`echo '# Configuration' >> ${chronyConfPath}`,
|
|
3208
|
-
`echo 'driftfile /var/lib/chrony/drift' >> ${chronyConfPath}`,
|
|
3209
|
-
`echo 'makestep 1.0 3' >> ${chronyConfPath}`,
|
|
3210
|
-
`echo 'rtcsync' >> ${chronyConfPath}`,
|
|
3211
|
-
`echo 'logdir /var/log/chrony' >> ${chronyConfPath}`,
|
|
3212
|
-
|
|
3213
|
-
// Enable chronyd to start on boot
|
|
3214
|
-
`systemctl enable chronyd 2>/dev/null`,
|
|
3215
|
-
|
|
3216
|
-
// Create systemd link for boot (works in chroot)
|
|
3217
|
-
`mkdir -p /etc/systemd/system/multi-user.target.wants`,
|
|
3218
|
-
`ln -sf /usr/lib/systemd/system/chronyd.service /etc/systemd/system/multi-user.target.wants/chronyd.service 2>/dev/null`,
|
|
3219
|
-
|
|
3220
|
-
// Start chronyd if systemd is running
|
|
3221
|
-
`systemctl start chronyd 2>/dev/null`,
|
|
3222
|
-
|
|
3223
|
-
// Restart chronyd to apply configuration
|
|
3224
|
-
`systemctl restart chronyd 2>/dev/null`,
|
|
3225
|
-
|
|
3226
|
-
// Force immediate time synchronization (only if chronyd is running)
|
|
3227
|
-
`chronyc makestep 2>/dev/null`,
|
|
3228
|
-
|
|
3229
|
-
// Verify timezone configuration
|
|
3230
|
-
`ls -l /etc/localtime`,
|
|
3231
|
-
`cat /etc/timezone || echo 'No /etc/timezone file'`,
|
|
3232
|
-
`timedatectl status 2>/dev/null || echo 'Timezone set to ${timezone} (timedatectl not available in chroot)'`,
|
|
3233
|
-
`chronyc tracking 2>/dev/null || echo 'Chrony configured but not running (will start on boot)'`,
|
|
3234
|
-
],
|
|
3235
|
-
/**
|
|
3236
|
-
* @method keyboard
|
|
3237
|
-
* @description Generates shell commands for configuring the keyboard layout on Rocky Linux.
|
|
3238
|
-
* This uses localectl to set the keyboard layout for both console and X11.
|
|
3239
|
-
* @param {string} [keyCode='us'] - The keyboard layout code (e.g., 'us', 'es').
|
|
3240
|
-
* @memberof UnderpostBaremetal.systemProvisioningFactory.rocky
|
|
3241
|
-
* @returns {string[]} An array of shell commands.
|
|
3242
|
-
*/
|
|
3243
|
-
keyboard: (keyCode = 'us') => [
|
|
3244
|
-
// Configure vconsole.conf for console keyboard layout (persistent)
|
|
3245
|
-
`echo 'KEYMAP=${keyCode}' > /etc/vconsole.conf`,
|
|
3246
|
-
`echo 'FONT=latarcyrheb-sun16' >> /etc/vconsole.conf`,
|
|
3247
|
-
|
|
3248
|
-
// Configure locale.conf for system locale
|
|
3249
|
-
`echo 'LANG=en_US.UTF-8' > /etc/locale.conf`,
|
|
3250
|
-
`echo 'LC_ALL=en_US.UTF-8' >> /etc/locale.conf`,
|
|
3251
|
-
|
|
3252
|
-
// Set keyboard layout using localectl (works if systemd is running)
|
|
3253
|
-
`localectl set-locale LANG=en_US.UTF-8 2>/dev/null`,
|
|
3254
|
-
`localectl set-keymap ${keyCode} 2>/dev/null`,
|
|
3255
|
-
`localectl set-x11-keymap ${keyCode} 2>/dev/null`,
|
|
3256
|
-
|
|
3257
|
-
// Configure X11 keyboard layout file directly
|
|
3258
|
-
`mkdir -p /etc/X11/xorg.conf.d`,
|
|
3259
|
-
`echo 'Section "InputClass"' > /etc/X11/xorg.conf.d/00-keyboard.conf`,
|
|
3260
|
-
`echo ' Identifier "system-keyboard"' >> /etc/X11/xorg.conf.d/00-keyboard.conf`,
|
|
3261
|
-
`echo ' MatchIsKeyboard "on"' >> /etc/X11/xorg.conf.d/00-keyboard.conf`,
|
|
3262
|
-
`echo ' Option "XkbLayout" "${keyCode}"' >> /etc/X11/xorg.conf.d/00-keyboard.conf`,
|
|
3263
|
-
`echo 'EndSection' >> /etc/X11/xorg.conf.d/00-keyboard.conf`,
|
|
3264
|
-
|
|
3265
|
-
// Load the keymap immediately (if not in chroot)
|
|
3266
|
-
`loadkeys ${keyCode} 2>/dev/null || echo 'Keymap ${keyCode} configured (loadkeys not available in chroot)'`,
|
|
3267
|
-
|
|
3268
|
-
// Verify configuration
|
|
3269
|
-
`echo 'Keyboard configuration files:'`,
|
|
3270
|
-
`cat /etc/vconsole.conf`,
|
|
3271
|
-
`cat /etc/locale.conf`,
|
|
3272
|
-
`cat /etc/X11/xorg.conf.d/00-keyboard.conf 2>/dev/null || echo 'X11 config created'`,
|
|
3273
|
-
`localectl status 2>/dev/null || echo 'Keyboard layout set to ${keyCode} (localectl not available in chroot)'`,
|
|
3274
|
-
],
|
|
3275
|
-
},
|
|
3276
|
-
},
|
|
3277
|
-
|
|
3278
2964
|
/**
|
|
3279
2965
|
* @method rebuildNfsServer
|
|
3280
2966
|
* @description Configures and restarts the NFS server to export the specified path.
|
package/src/cli/cloud-init.js
CHANGED
|
@@ -64,7 +64,7 @@ class UnderpostCloudInit {
|
|
|
64
64
|
fs.writeFileSync(
|
|
65
65
|
`${nfsHostToolsPath}/date.sh`,
|
|
66
66
|
Underpost.baremetal.stepsRender(
|
|
67
|
-
Underpost.
|
|
67
|
+
Underpost.system.factory[systemProvisioning].timezone({
|
|
68
68
|
timezone,
|
|
69
69
|
chronyConfPath,
|
|
70
70
|
}),
|
|
@@ -78,7 +78,7 @@ class UnderpostCloudInit {
|
|
|
78
78
|
fs.writeFileSync(
|
|
79
79
|
`${nfsHostToolsPath}/keyboard.sh`,
|
|
80
80
|
Underpost.baremetal.stepsRender(
|
|
81
|
-
Underpost.
|
|
81
|
+
Underpost.system.factory[systemProvisioning].keyboard(keyboard.layout),
|
|
82
82
|
false,
|
|
83
83
|
),
|
|
84
84
|
'utf8',
|
package/src/cli/index.js
CHANGED
|
@@ -76,11 +76,20 @@ program
|
|
|
76
76
|
.option('--info', 'Displays information about available commit types.')
|
|
77
77
|
.option('--diff', 'Shows the current git diff changes.')
|
|
78
78
|
.option('--edit', 'Edit last commit.')
|
|
79
|
-
.option('--msg <msg>', 'Sets a custom commit message.')
|
|
80
79
|
.option('--deploy-id <deploy-id>', 'Sets the deployment configuration ID for the commit context.')
|
|
81
80
|
.option('--cached', 'Commit staged changes only or context.')
|
|
82
81
|
.option('--hashes <hashes>', 'Comma-separated list of specific file hashes of commits.')
|
|
83
82
|
.option('--extension <extension>', 'specific file extensions of commits.')
|
|
83
|
+
.option(
|
|
84
|
+
'--changelog [latest-n]',
|
|
85
|
+
'Print plain the changelog of the specified number of latest n commits, if no number is provided it will get the changelog to latest ci integration',
|
|
86
|
+
)
|
|
87
|
+
.option('--changelog-build', 'Builds a CHANGELOG.md file based on the commit history')
|
|
88
|
+
.option('--changelog-min-version <version>', 'Sets the minimum version limit for --changelog-build (default: 2.85.0)')
|
|
89
|
+
.option(
|
|
90
|
+
'--changelog-no-hash',
|
|
91
|
+
'Excludes commit hashes from the generated changelog entries (used with --changelog-build).',
|
|
92
|
+
)
|
|
84
93
|
.description('Manages commits to a GitHub repository, supporting various commit types and options.')
|
|
85
94
|
.action(Underpost.repo.commit);
|
|
86
95
|
|
|
@@ -426,6 +435,7 @@ program
|
|
|
426
435
|
'--create-job-now',
|
|
427
436
|
'After applying manifests, immediately create a Job from each CronJob (requires --apply).',
|
|
428
437
|
)
|
|
438
|
+
.option('--ssh', 'Execute backup commands via SSH on the remote node instead of locally.')
|
|
429
439
|
.description('Manages cron jobs: execute jobs directly or generate and apply K8s CronJob manifests.')
|
|
430
440
|
.action(Underpost.cron.callback);
|
|
431
441
|
|
|
@@ -502,6 +512,7 @@ program
|
|
|
502
512
|
.option('--status', 'Checks the status of the SSH service.')
|
|
503
513
|
.option('--connect-uri', 'Displays the connection URI.')
|
|
504
514
|
.option('--copy', 'Copies the connection URI to clipboard.')
|
|
515
|
+
.description('Manages SSH credentials and sessions for remote access to cluster nodes or services.')
|
|
505
516
|
.action(Underpost.ssh.callback);
|
|
506
517
|
|
|
507
518
|
program
|
package/src/cli/repository.js
CHANGED
|
@@ -88,10 +88,13 @@ class UnderpostRepository {
|
|
|
88
88
|
* @param {boolean} [options.cached=false] - If true, commits only staged changes.
|
|
89
89
|
* @param {number} [options.log=0] - If greater than 0, shows the last N commits with diffs.
|
|
90
90
|
* @param {boolean} [options.lastMsg=0] - If greater than 0, copies or show the last last single n commit message to clipboard.
|
|
91
|
-
* @param {string} [options.msg=''] - If provided, outputs this message instead of committing.
|
|
92
91
|
* @param {string} [options.deployId=''] - An optional deploy ID to include in the commit message.
|
|
93
92
|
* @param {string} [options.hashes=''] - If provided with diff option, shows the diff between two hashes.
|
|
94
93
|
* @param {string} [options.extension=''] - If provided with diff option, filters the diff by this file extension.
|
|
94
|
+
* @param {boolean|string} [options.changelog=undefined] - If true, prints the changelog since the last CI integration commit (starting with 'ci(package-pwa-microservices-'). If a number string, prints the changelog of the last N commits split by version sections. Only considers commits starting with '[<tag>]'.
|
|
95
|
+
* @param {boolean} [options.changelogBuild=false] - If true, scrapes all git history and builds a full CHANGELOG.md. Commits containing 'New release v:' are used as version section titles. Only commits starting with '[<tag>]' are included as entries.
|
|
96
|
+
* @param {string} [options.changelogMinVersion=''] - If set, overrides the default minimum version limit (2.85.0) for --changelog-build.
|
|
97
|
+
* @param {boolean} [options.changelogNoHash=false] - If true, omits commit hashes from the changelog entries.
|
|
95
98
|
* @memberof UnderpostRepository
|
|
96
99
|
*/
|
|
97
100
|
commit(
|
|
@@ -108,13 +111,169 @@ class UnderpostRepository {
|
|
|
108
111
|
cached: false,
|
|
109
112
|
lastMsg: 0,
|
|
110
113
|
log: 0,
|
|
111
|
-
msg: '',
|
|
112
114
|
deployId: '',
|
|
113
115
|
hashes: '',
|
|
114
116
|
extension: '',
|
|
117
|
+
changelog: undefined,
|
|
118
|
+
changelogBuild: false,
|
|
119
|
+
changelogMinVersion: '',
|
|
120
|
+
changelogNoHash: false,
|
|
115
121
|
},
|
|
116
122
|
) {
|
|
117
123
|
if (!repoPath) repoPath = '.';
|
|
124
|
+
|
|
125
|
+
if (options.changelog !== undefined || options.changelogBuild) {
|
|
126
|
+
const ciIntegrationPrefix = 'ci(package-pwa-microservices-';
|
|
127
|
+
const releaseMatch = 'New release v:';
|
|
128
|
+
|
|
129
|
+
// Helper: parse [<tag>] commits into grouped sections
|
|
130
|
+
const buildSectionChangelog = (commits) => {
|
|
131
|
+
const groups = {};
|
|
132
|
+
const tagOrder = [];
|
|
133
|
+
for (const commit of commits) {
|
|
134
|
+
if (!commit.message.startsWith('[')) continue;
|
|
135
|
+
const match = commit.message.match(/^\[([^\]]+)\]\s*(.*)/);
|
|
136
|
+
if (match) {
|
|
137
|
+
const tag = match[1].trim();
|
|
138
|
+
const context = match[2].trim().replaceAll('"', '');
|
|
139
|
+
if (!groups[tag]) {
|
|
140
|
+
groups[tag] = [];
|
|
141
|
+
tagOrder.push(tag);
|
|
142
|
+
}
|
|
143
|
+
groups[tag].push({ ...commit, context });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let out = '';
|
|
147
|
+
for (const tag of tagOrder) {
|
|
148
|
+
out += `### ${tag}\n\n`;
|
|
149
|
+
for (const entry of groups[tag]) {
|
|
150
|
+
out += `- ${entry.context}${options.changelogNoHash ? '' : ` (${commitUrl(entry.hash, entry.fullHash)})`}\n`;
|
|
151
|
+
}
|
|
152
|
+
out += '\n';
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Helper: fetch git log as structured array
|
|
158
|
+
const fetchHistory = (limit) => {
|
|
159
|
+
const limitArg = limit ? ` -n ${limit}` : '';
|
|
160
|
+
const rawLog = shellExec(`git log --pretty=format:"%h||%H||%s||%ci"${limitArg}`, {
|
|
161
|
+
stdout: true,
|
|
162
|
+
silent: true,
|
|
163
|
+
disableLog: true,
|
|
164
|
+
});
|
|
165
|
+
return rawLog
|
|
166
|
+
.split('\n')
|
|
167
|
+
.map((line) => {
|
|
168
|
+
const parts = line.split('||');
|
|
169
|
+
return {
|
|
170
|
+
hash: (parts[0] || '').trim(),
|
|
171
|
+
fullHash: (parts[1] || '').trim(),
|
|
172
|
+
message: parts[2] || '',
|
|
173
|
+
date: parts[3] || '',
|
|
174
|
+
};
|
|
175
|
+
})
|
|
176
|
+
.filter((c) => c.hash);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const githubUser = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
180
|
+
const commitUrl = (shortHash, fullHash) =>
|
|
181
|
+
`[${shortHash}](https://github.com/${githubUser}/engine/commit/${fullHash})`;
|
|
182
|
+
|
|
183
|
+
// Helper: extract version from commit message containing 'New release v:'
|
|
184
|
+
const extractVersion = (message) => {
|
|
185
|
+
const idx = message.indexOf(releaseMatch);
|
|
186
|
+
if (idx === -1) return null;
|
|
187
|
+
return message.substring(idx + releaseMatch.length).trim();
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Helper: split commits array into version sections by 'New release v:' boundary
|
|
191
|
+
const buildVersionSections = (commits) => {
|
|
192
|
+
const sections = [];
|
|
193
|
+
let currentSection = { title: null, date: new Date().toISOString().split('T')[0], commits: [] };
|
|
194
|
+
|
|
195
|
+
for (const commit of commits) {
|
|
196
|
+
const version = extractVersion(commit.message);
|
|
197
|
+
if (version) {
|
|
198
|
+
// Push accumulated commits as a section
|
|
199
|
+
sections.push(currentSection);
|
|
200
|
+
// Start new version section; commits below this one belong to it
|
|
201
|
+
const commitDate = commit.date ? commit.date.split(' ')[0] : '';
|
|
202
|
+
currentSection = { title: `${releaseMatch}${version}`, date: commitDate, hash: commit.hash, commits: [] };
|
|
203
|
+
} else {
|
|
204
|
+
currentSection.commits.push(commit);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Push the last (oldest) section
|
|
208
|
+
if (currentSection.commits.length > 0) sections.push(currentSection);
|
|
209
|
+
return sections;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Helper: render sections array into changelog markdown string
|
|
213
|
+
const renderSections = (sections) => {
|
|
214
|
+
let changelog = '';
|
|
215
|
+
for (const section of sections) {
|
|
216
|
+
const sectionBody = buildSectionChangelog(section.commits);
|
|
217
|
+
if (!sectionBody) continue;
|
|
218
|
+
if (section.title) {
|
|
219
|
+
changelog += `## ${section.title}${options.changelogNoHash ? '' : ` (${section.date})`}\n\n`;
|
|
220
|
+
} else {
|
|
221
|
+
changelog += `## ${section.date}\n\n`;
|
|
222
|
+
}
|
|
223
|
+
changelog += sectionBody;
|
|
224
|
+
}
|
|
225
|
+
return changelog;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const changelogMinVersion = options.changelogMinVersion || '2.97.1';
|
|
229
|
+
|
|
230
|
+
if (options.changelogBuild) {
|
|
231
|
+
// --changelog-build: scrape ALL history, split by 'New release v:' commits as version sections
|
|
232
|
+
const allCommits = fetchHistory();
|
|
233
|
+
const sections = buildVersionSections(allCommits);
|
|
234
|
+
|
|
235
|
+
// Filter sections: stop at changelogMinVersion boundary
|
|
236
|
+
const limitedSections = [];
|
|
237
|
+
for (const section of sections) {
|
|
238
|
+
limitedSections.push(section);
|
|
239
|
+
if (section.title) {
|
|
240
|
+
const versionStr = section.title.replace(releaseMatch, '').trim();
|
|
241
|
+
if (versionStr === changelogMinVersion) break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let changelog = renderSections(limitedSections);
|
|
246
|
+
|
|
247
|
+
if (!changelog) {
|
|
248
|
+
changelog = `No changelog entries found.\n`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const changelogPath = `${repoPath === '.' ? '.' : repoPath}/CHANGELOG.md`;
|
|
252
|
+
fs.writeFileSync(changelogPath, `# Changelog\n\n${changelog}`);
|
|
253
|
+
logger.info('CHANGELOG.md built at', changelogPath);
|
|
254
|
+
} else {
|
|
255
|
+
// --changelog [latest-n]: print changelog of last N commits or since last release
|
|
256
|
+
const hasExplicitCount =
|
|
257
|
+
options.changelog !== undefined && options.changelog !== true && !isNaN(parseInt(options.changelog));
|
|
258
|
+
const scanLimit = hasExplicitCount ? parseInt(options.changelog) : 500;
|
|
259
|
+
const allCommits = fetchHistory(scanLimit);
|
|
260
|
+
|
|
261
|
+
let commits;
|
|
262
|
+
if (!hasExplicitCount) {
|
|
263
|
+
// No explicit count: find commits up to the last CI integration boundary
|
|
264
|
+
const ciIndex = allCommits.findIndex((c) => c.message.startsWith(ciIntegrationPrefix));
|
|
265
|
+
commits = ciIndex >= 0 ? allCommits.slice(0, ciIndex) : allCommits;
|
|
266
|
+
} else {
|
|
267
|
+
commits = allCommits;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const sections = buildVersionSections(commits);
|
|
271
|
+
let changelog = renderSections(sections);
|
|
272
|
+
console.log(changelog || `No changelog entries found.\n`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
118
277
|
if (options.diff && options.hashes) {
|
|
119
278
|
const hashes = options.hashes.split(',');
|
|
120
279
|
const cmd = `git --no-pager diff ${hashes[0]} ${hashes[1] ? hashes[1] : 'HEAD'}${options.extension ? ` -- '*.${options.extension}'` : ''}`;
|
|
@@ -123,16 +282,6 @@ class UnderpostRepository {
|
|
|
123
282
|
} else console.log(cmd);
|
|
124
283
|
return;
|
|
125
284
|
}
|
|
126
|
-
if (options.msg) {
|
|
127
|
-
options.msg = options.msg.replaceAll('"', '').replaceAll(`'`, '').replaceAll('`', '');
|
|
128
|
-
let key = Object.keys(commitData).find((k) => k && options.msg.toLocaleLowerCase().slice(0, 16).match(k));
|
|
129
|
-
if (!key) key = Object.keys(commitData).find((k) => k && options.msg.toLocaleLowerCase().match(k));
|
|
130
|
-
if (!key || key === undefined) key = 'chore';
|
|
131
|
-
shellExec(
|
|
132
|
-
`underpost cmt ${repoPath} ${key} ${options.deployId ? options.deployId : `''`} '${options.msg.replaceAll(`${key}(${key}`, '')}'`,
|
|
133
|
-
);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
285
|
if (options.lastMsg) {
|
|
137
286
|
if (options.copy) {
|
|
138
287
|
pbcopy(Underpost.repo.getLastCommitMsg(options.lastMsg - 1));
|
|
@@ -197,7 +346,11 @@ class UnderpostRepository {
|
|
|
197
346
|
* @memberof UnderpostRepository
|
|
198
347
|
*/
|
|
199
348
|
getLastCommitMsg(skip = 0) {
|
|
200
|
-
return shellExec(`git --no-pager log -1 --skip=${skip} --pretty=%B`, {
|
|
349
|
+
return shellExec(`git --no-pager log -1 --skip=${skip} --pretty=%B`, {
|
|
350
|
+
stdout: true,
|
|
351
|
+
silent: true,
|
|
352
|
+
disableLog: true,
|
|
353
|
+
});
|
|
201
354
|
},
|
|
202
355
|
|
|
203
356
|
/**
|