From 27e7245acfbf490fa1ff1ba0cb31f75eb51c7e0a Mon Sep 17 00:00:00 2001 From: User Date: Thu, 4 Sep 2025 04:27:10 +0800 Subject: [PATCH 01/22] Add Fedora 40-42 and rawhide support - Add Fedora to supported OS list - Correctly detect and use valkey instead of redis for Fedora 40+ - Skip EPEL repository installation on Fedora - Handle libvhdi-tools gracefully (inform if not available) - Special handling for Fedora Rawhide detection --- README-FEDORA.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++ xo-install.sh | 37 +++++++++++++--- 2 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 README-FEDORA.md diff --git a/README-FEDORA.md b/README-FEDORA.md new file mode 100644 index 0000000..d876648 --- /dev/null +++ b/README-FEDORA.md @@ -0,0 +1,110 @@ +# Fedora Support for XenOrchestraInstallerUpdater + +This fork adds support for Fedora 40, 41, 42, and rawhide to the XenOrchestraInstallerUpdater script. + +## Changes Made + +### 1. Added Fedora to Supported Operating Systems +- Modified OS detection to recognize Fedora +- Added version checks for Fedora 40, 41, 42, and rawhide +- Special handling for Fedora Rawhide (rolling release) + +### 2. Fixed Package Management for Fedora +- **Valkey vs Redis**: Correctly detects that Fedora 40+ uses valkey instead of redis +- **EPEL Repository**: Skips EPEL installation as it's not needed on Fedora +- **libvhdi**: Provides information about libvhdi availability (currently not in Fedora repos) + +### 3. Tested Configurations +- Fedora 42 (current stable) +- Fedora rawhide (development version) + +## Installation on Fedora + +### Prerequisites + +```bash +# Ensure system is up to date +sudo dnf update -y + +# Install git if not present +sudo dnf install -y git +``` + +### Using the Modified Script + +```bash +# Clone this fork +git clone https://github.com/YOUR_USERNAME/XenOrchestraInstallerUpdater +cd XenOrchestraInstallerUpdater + +# Run installation +sudo bash xo-install.sh +``` + +### Manual Installation Steps + +If you prefer to run the installation with specific options: + +```bash +# Fresh installation +sudo bash xo-install.sh --install + +# Update existing installation +sudo bash xo-install.sh --update + +# Rollback to previous version +sudo bash xo-install.sh --rollback +``` + +## Configuration + +The script uses the same configuration file (`xo-install.cfg`) as the original. + +### Fedora-Specific Notes + +1. **Yarn Installation**: The script will automatically configure the Yarn repository and install it, as it's not available in Fedora base repos. + +2. **Node.js**: Fedora 42 includes Node.js 22 in the base repository, which matches XO requirements. + +3. **libvhdi**: Currently not available in Fedora repositories. The script will skip this and inform you that XO will work without it, but some VHD operations may be limited. + +## Known Limitations + +- libvhdi-tools is not available from COPR for Fedora (only EPEL) +- Some VHD operations may be limited without libvhdi + +## Future Improvements + +- [ ] Create COPR repository for libvhdi on Fedora +- [ ] Add support for more Fedora versions as they're released +- [ ] Improve rawhide handling for better stability + +## Testing Status + +| Fedora Version | Status | Notes | +|---------------|--------|-------| +| Fedora 40 | ✅ Supported | Uses valkey | +| Fedora 41 | ✅ Supported | Uses valkey | +| Fedora 42 | ✅ Tested | Current stable, uses valkey | +| Fedora Rawhide | ✅ Supported | Development version, uses valkey | + +## Contributing + +To contribute to Fedora support: + +1. Fork this repository +2. Test on your Fedora version +3. Submit issues for any problems +4. Create pull requests with fixes + +## Pull Request Status + +- [ ] PR submitted to upstream repository +- [ ] Waiting for review +- [ ] Merged + +## Contact + +For issues specific to Fedora support, please open an issue in this fork. + +For general XenOrchestraInstallerUpdater issues, see the [original repository](https://github.com/ronivay/XenOrchestraInstallerUpdater). \ No newline at end of file diff --git a/xo-install.sh b/xo-install.sh index ffafb5d..dfc9f72 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -208,7 +208,8 @@ function InstallDependenciesRPM { fi # only install epel-release if doesn't exist and user allows it to be installed - if [[ -z $(runcmd_stdout "rpm -qa epel-release") ]] && [[ "$INSTALL_REPOS" == "true" ]]; then + # Skip for Fedora as it doesn't need EPEL + if [[ -z $(runcmd_stdout "rpm -qa epel-release") ]] && [[ "$INSTALL_REPOS" == "true" ]] && [[ "$OSNAME" != "Fedora" ]]; then echo printprog "Installing epel-repo" runcmd "dnf -y install epel-release" @@ -253,12 +254,17 @@ function InstallDependenciesRPM { # Only install libvhdi-tools if vhdimount is not present if [[ -z $(runcmd_stdout "command -v vhdimount") ]]; then - if [[ "$INSTALL_REPOS" == "true" ]] && [[ "$INSTALL_EL_LIBVHDI" == "true" ]]; then + # Skip COPR for Fedora as it only supports EPEL + if [[ "$INSTALL_REPOS" == "true" ]] && [[ "$INSTALL_EL_LIBVHDI" == "true" ]] && [[ "$OSNAME" != "Fedora" ]]; then echo printprog "Installing libvhdi-tools" runcmd "dnf copr enable -y bnerickson/libvhdi" runcmd "dnf install -y libvhdi-tools" printok "Installing libvhdi-tools" + elif [[ "$OSNAME" == "Fedora" ]] && [[ "$INSTALL_EL_LIBVHDI" == "true" ]]; then + echo + printinfo "libvhdi-tools not available for Fedora from COPR. Skipping installation." + printinfo "XO will work without it, but some VHD operations may be limited." fi fi @@ -1293,6 +1299,13 @@ function CheckOS { OSVERSION=$(runcmd_stdout "grep ^VERSION_ID /etc/os-release | cut -d'=' -f2 | grep -Eo '[0-9]{1,2}' | head -1") OSNAME=$(runcmd_stdout "grep ^NAME /etc/os-release | cut -d'=' -f2 | sed 's/\"//g' | awk '{print \$1}'") + # Special handling for Fedora Rawhide + if [[ "$OSNAME" == "Fedora" ]]; then + local VERSION_STRING=$(runcmd_stdout "grep ^VERSION_ID /etc/os-release | cut -d'=' -f2 | sed 's/\"//g'") + if [[ "$VERSION_STRING" == "rawhide" ]]; then + OSVERSION="rawhide" + fi + fi # check that were not on official XOA VM. if yes, bail out if [[ $(runcmd_stdout "grep ^GRUB_DISTRIBUTOR /etc/default/grub | grep 'Xen Orchestra'") ]]; then @@ -1302,8 +1315,15 @@ function CheckOS { if [[ $(runcmd_stdout "command -v dnf") ]]; then PKG_FORMAT="rpm" - # Since version 10, redis is replaced with valkey - if [[ $OSVERSION -ge 10 ]]; then + # Handle Fedora separately - uses valkey since Fedora 40 + if [[ "$OSNAME" == "Fedora" ]]; then + if [[ "$OSVERSION" == "rawhide" ]] || [[ "$OSVERSION" -ge 40 ]]; then + REDIS=0 # Use valkey + else + REDIS=1 # Use redis (for older Fedora versions if any) + fi + # For RHEL-based distros, valkey since version 10 + elif [[ $OSVERSION -ge 10 ]]; then REDIS=0 else REDIS=1 @@ -1325,8 +1345,8 @@ function CheckOS { return 0 fi - if [[ ! "$OSNAME" =~ ^(Debian|Ubuntu|CentOS|Rocky|AlmaLinux)$ ]]; then - printfail "Only Ubuntu/Debian/CentOS/Rocky/AlmaLinux supported" + if [[ ! "$OSNAME" =~ ^(Debian|Ubuntu|CentOS|Rocky|AlmaLinux|Fedora)$ ]]; then + printfail "Only Ubuntu/Debian/CentOS/Rocky/AlmaLinux/Fedora supported" exit 1 fi @@ -1345,6 +1365,11 @@ function CheckOS { exit 1 fi + if [[ "$OSNAME" == "Fedora" ]] && [[ ! "$OSVERSION" =~ ^(40|41|42|rawhide)$ ]]; then + printfail "Only Fedora 40/41/42/rawhide supported" + exit 1 + fi + if [[ "$OSNAME" == "Debian" ]] && [[ ! "$OSVERSION" =~ ^(11|12|13)$ ]]; then printfail "Only Debian 11/12/13 supported" exit 1 From 3138a32a578dba8480a430dd3c2037ce793641dc Mon Sep 17 00:00:00 2001 From: User Date: Thu, 4 Sep 2025 13:13:10 +0800 Subject: [PATCH 02/22] Update Fedora support to 41/42/43/rawhide with correct COPR repos - Changed supported versions from 40/41/42 to 41/42/43 plus rawhide - Fedora 41+ uses valkey (not 40+) - Use reversejames/libvhdi COPR for Fedora - Keep bnerickson/libvhdi COPR for RHEL-based systems - Updated documentation to reflect correct versions and COPR usage --- README-FEDORA.md | 24 ++++++++++++------------ xo-install.sh | 23 ++++++++++++----------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/README-FEDORA.md b/README-FEDORA.md index d876648..ae5d167 100644 --- a/README-FEDORA.md +++ b/README-FEDORA.md @@ -1,18 +1,18 @@ # Fedora Support for XenOrchestraInstallerUpdater -This fork adds support for Fedora 40, 41, 42, and rawhide to the XenOrchestraInstallerUpdater script. +This fork adds support for Fedora 41, 42, 43, and rawhide to the XenOrchestraInstallerUpdater script. ## Changes Made ### 1. Added Fedora to Supported Operating Systems - Modified OS detection to recognize Fedora -- Added version checks for Fedora 40, 41, 42, and rawhide +- Added version checks for Fedora 41, 42, 43, and rawhide - Special handling for Fedora Rawhide (rolling release) ### 2. Fixed Package Management for Fedora -- **Valkey vs Redis**: Correctly detects that Fedora 40+ uses valkey instead of redis +- **Valkey vs Redis**: Correctly detects that Fedora 41+ uses valkey instead of redis - **EPEL Repository**: Skips EPEL installation as it's not needed on Fedora -- **libvhdi**: Provides information about libvhdi availability (currently not in Fedora repos) +- **libvhdi**: Automatically installs from reversejames/libvhdi COPR repository for Fedora ### 3. Tested Configurations - Fedora 42 (current stable) @@ -66,16 +66,16 @@ The script uses the same configuration file (`xo-install.cfg`) as the original. 2. **Node.js**: Fedora 42 includes Node.js 22 in the base repository, which matches XO requirements. -3. **libvhdi**: Currently not available in Fedora repositories. The script will skip this and inform you that XO will work without it, but some VHD operations may be limited. +3. **libvhdi**: Automatically installed from the reversejames/libvhdi COPR repository. This provides full VHD operation support. ## Known Limitations -- libvhdi-tools is not available from COPR for Fedora (only EPEL) -- Some VHD operations may be limited without libvhdi +- libvhdi-tools is installed from reversejames/libvhdi COPR repository +- Full VHD operations are supported with libvhdi installed ## Future Improvements -- [ ] Create COPR repository for libvhdi on Fedora +- [x] COPR repository for libvhdi on Fedora (reversejames/libvhdi) - [ ] Add support for more Fedora versions as they're released - [ ] Improve rawhide handling for better stability @@ -83,10 +83,10 @@ The script uses the same configuration file (`xo-install.cfg`) as the original. | Fedora Version | Status | Notes | |---------------|--------|-------| -| Fedora 40 | ✅ Supported | Uses valkey | -| Fedora 41 | ✅ Supported | Uses valkey | -| Fedora 42 | ✅ Tested | Current stable, uses valkey | -| Fedora Rawhide | ✅ Supported | Development version, uses valkey | +| Fedora 41 | ✅ Supported | Uses valkey, libvhdi from COPR | +| Fedora 42 | ✅ Tested | Current stable, uses valkey, libvhdi from COPR | +| Fedora 43 | ✅ Supported | Uses valkey, libvhdi from COPR | +| Fedora Rawhide | ✅ Supported | Development version, uses valkey, libvhdi from COPR | ## Contributing diff --git a/xo-install.sh b/xo-install.sh index dfc9f72..94970b7 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -254,17 +254,18 @@ function InstallDependenciesRPM { # Only install libvhdi-tools if vhdimount is not present if [[ -z $(runcmd_stdout "command -v vhdimount") ]]; then - # Skip COPR for Fedora as it only supports EPEL - if [[ "$INSTALL_REPOS" == "true" ]] && [[ "$INSTALL_EL_LIBVHDI" == "true" ]] && [[ "$OSNAME" != "Fedora" ]]; then + if [[ "$INSTALL_REPOS" == "true" ]] && [[ "$INSTALL_EL_LIBVHDI" == "true" ]]; then echo printprog "Installing libvhdi-tools" - runcmd "dnf copr enable -y bnerickson/libvhdi" + if [[ "$OSNAME" == "Fedora" ]]; then + # Use reversejames/libvhdi for Fedora + runcmd "dnf copr enable -y reversejames/libvhdi" + else + # Use bnerickson/libvhdi for RHEL-based systems + runcmd "dnf copr enable -y bnerickson/libvhdi" + fi runcmd "dnf install -y libvhdi-tools" printok "Installing libvhdi-tools" - elif [[ "$OSNAME" == "Fedora" ]] && [[ "$INSTALL_EL_LIBVHDI" == "true" ]]; then - echo - printinfo "libvhdi-tools not available for Fedora from COPR. Skipping installation." - printinfo "XO will work without it, but some VHD operations may be limited." fi fi @@ -1315,9 +1316,9 @@ function CheckOS { if [[ $(runcmd_stdout "command -v dnf") ]]; then PKG_FORMAT="rpm" - # Handle Fedora separately - uses valkey since Fedora 40 + # Handle Fedora separately - uses valkey since Fedora 41 if [[ "$OSNAME" == "Fedora" ]]; then - if [[ "$OSVERSION" == "rawhide" ]] || [[ "$OSVERSION" -ge 40 ]]; then + if [[ "$OSVERSION" == "rawhide" ]] || [[ "$OSVERSION" -ge 41 ]]; then REDIS=0 # Use valkey else REDIS=1 # Use redis (for older Fedora versions if any) @@ -1365,8 +1366,8 @@ function CheckOS { exit 1 fi - if [[ "$OSNAME" == "Fedora" ]] && [[ ! "$OSVERSION" =~ ^(40|41|42|rawhide)$ ]]; then - printfail "Only Fedora 40/41/42/rawhide supported" + if [[ "$OSNAME" == "Fedora" ]] && [[ ! "$OSVERSION" =~ ^(41|42|43|rawhide)$ ]]; then + printfail "Only Fedora 41/42/43/rawhide supported" exit 1 fi From 46a0ed13a48d77770cf5079b52e8e48b71e5f847 Mon Sep 17 00:00:00 2001 From: User Date: Thu, 4 Sep 2025 15:12:18 +0800 Subject: [PATCH 03/22] Use Fedora's native yarnpkg and add unofficial support warning - Use yarnpkg from Fedora repos instead of external yarn repository - Add warning that XO doesn't officially support Fedora - Update documentation to clarify experimental status - Follows Fedora packaging best practices by using native packages --- README-FEDORA.md | 14 ++++++++++---- xo-install.sh | 21 +++++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README-FEDORA.md b/README-FEDORA.md index ae5d167..c17c920 100644 --- a/README-FEDORA.md +++ b/README-FEDORA.md @@ -1,6 +1,8 @@ # Fedora Support for XenOrchestraInstallerUpdater -This fork adds support for Fedora 41, 42, 43, and rawhide to the XenOrchestraInstallerUpdater script. +**⚠️ IMPORTANT: Xen Orchestra does not officially support Fedora. This is an experimental implementation.** + +This fork adds experimental support for Fedora 41, 42, 43, and rawhide to the XenOrchestraInstallerUpdater script. ## Changes Made @@ -62,16 +64,20 @@ The script uses the same configuration file (`xo-install.cfg`) as the original. ### Fedora-Specific Notes -1. **Yarn Installation**: The script will automatically configure the Yarn repository and install it, as it's not available in Fedora base repos. +1. **Yarn Installation**: The script installs Fedora's native `yarnpkg` package instead of using external repositories. This follows Fedora packaging best practices and avoids third-party dependencies. + +2. **Node.js**: Fedora includes recent Node.js versions in the base repository, which should match XO requirements. -2. **Node.js**: Fedora 42 includes Node.js 22 in the base repository, which matches XO requirements. +3. **Package Differences**: Unlike RHEL-based systems which use Yarn's official repository, Fedora uses its native `yarnpkg` package to follow distribution packaging guidelines. 3. **libvhdi**: Automatically installed from the reversejames/libvhdi COPR repository. This provides full VHD operation support. ## Known Limitations +- **Unofficial Support**: Xen Orchestra does not officially support Fedora +- **Experimental Status**: This installation may encounter unexpected issues - libvhdi-tools is installed from reversejames/libvhdi COPR repository -- Full VHD operations are supported with libvhdi installed +- Uses Fedora's `yarnpkg` package instead of upstream yarn ## Future Improvements diff --git a/xo-install.sh b/xo-install.sh index 94970b7..77ee3bd 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -243,12 +243,17 @@ function InstallDependenciesRPM { echo printprog "Installing yarn" - # only install yarn repo if user allows it to be installed - if [[ "$INSTALL_REPOS" == "true" ]]; then - runcmd "curl -s -o /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo" + if [[ "$OSNAME" == "Fedora" ]]; then + # Use Fedora's native yarnpkg package + runcmd "dnf -y install yarnpkg" + else + # For RHEL-based systems, use Yarn's official repository + if [[ "$INSTALL_REPOS" == "true" ]]; then + runcmd "curl -s -o /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo" + fi + runcmd "dnf -y install yarn" fi - runcmd "dnf -y install yarn" printok "Installing yarn" fi @@ -1381,6 +1386,14 @@ function CheckOS { exit 1 fi + # Display warning for Fedora users about unofficial support + if [[ "$OSNAME" == "Fedora" ]]; then + echo + printwarning "NOTICE: Xen Orchestra does not officially support Fedora" + printwarning "This is an experimental installation using Fedora's native packages" + echo + fi + } # we don't want anyone to attempt running this on xcp-ng/xenserver host, bail out if xe command is present From 1ff2bf5ce73a800fe59622f99142d528524440e4 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 15:55:23 +0800 Subject: [PATCH 04/22] Fix undefined printwarning function for Fedora support Define printwarning function conditionally if not already available as system command --- xo-install.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/xo-install.sh b/xo-install.sh index 77ee3bd..2bcf645 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -173,6 +173,13 @@ function printinfo { echo -e "${INFO} $*" } +# Define printwarning if it doesn't already exist as a system command +if ! command -v printwarning &>/dev/null; then + function printwarning { + echo -e "${INFO} $*" + } +fi + # if script fails at a stage where installation is not complete, we don't want to keep the install specific directory and content # this is called by trap inside different functions function ErrorHandling { From 30ad16879f89d42d4cb57ba4ce963702eb9bf9b9 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 16:15:51 +0800 Subject: [PATCH 05/22] Use Fedora's native Node.js packages instead of NodeSource - Fedora ships with Node.js 22 natively, no need for external repository - Updated both initial installation and UpdateNodeYarn function - Maintains NodeSource for RHEL-based systems that need it --- xo-install.sh | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 2bcf645..87e6fec 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -173,12 +173,9 @@ function printinfo { echo -e "${INFO} $*" } -# Define printwarning if it doesn't already exist as a system command -if ! command -v printwarning &>/dev/null; then - function printwarning { - echo -e "${INFO} $*" - } -fi +function printwarning { + echo -e "${INFO} $*" +} # if script fails at a stage where installation is not complete, we don't want to keep the install specific directory and content # this is called by trap inside different functions @@ -234,12 +231,17 @@ function InstallDependenciesRPM { echo printprog "Installing node.js" - # only install nodejs repo if user allows it to be installed - if [[ "$INSTALL_REPOS" == "true" ]]; then - runcmd "curl -s -L https://rpm.nodesource.com/setup_${NODEVERSION}.x | bash -" + if [[ "$OSNAME" == "Fedora" ]]; then + # Use Fedora's native nodejs package (v22) + runcmd "dnf install -y nodejs nodejs-npm" + else + # For RHEL-based systems, use NodeSource repository + if [[ "$INSTALL_REPOS" == "true" ]]; then + runcmd "curl -s -L https://rpm.nodesource.com/setup_${NODEVERSION}.x | bash -" + fi + runcmd "dnf install -y nodejs" fi - runcmd "dnf install -y nodejs" printok "Installing node.js" else UpdateNodeYarn @@ -413,15 +415,25 @@ function UpdateNodeYarn { if [ "$PKG_FORMAT" == "rpm" ]; then # update node version if needed. - # skip update if repository install is disabled as we can't quarantee this actually updates anything - if [[ "${NODEV:-0}" -lt "${NODEVERSION}" ]] && [[ "$INSTALL_REPOS" == "true" ]]; then + # For Fedora, use native packages; for others use NodeSource if allowed + if [[ "${NODEV:-0}" -lt "${NODEVERSION}" ]]; then echo printprog "node.js version is ${NODEV:-"not installed"}, upgrading to ${NODEVERSION}.x" - runcmd "curl -sL https://rpm.nodesource.com/setup_${NODEVERSION}.x | bash -" + if [[ "$OSNAME" == "Fedora" ]]; then + # Use Fedora's native nodejs package + runcmd "dnf clean all" + runcmd "dnf install -y nodejs nodejs-npm" + elif [[ "$INSTALL_REPOS" == "true" ]]; then + # For RHEL-based systems, use NodeSource repository + runcmd "curl -sL https://rpm.nodesource.com/setup_${NODEVERSION}.x | bash -" + runcmd "dnf clean all" + runcmd "dnf install -y nodejs" + else + printfail "Node.js version update needed but INSTALL_REPOS set to false for non-Fedora system, can't continue" + exit 1 + fi - runcmd "dnf clean all" - runcmd "dnf install -y nodejs" printok "node.js version is ${NODEV:-"not installed"}, upgrading to ${NODEVERSION}.x" else if [[ -z "$NODEV" ]]; then From dee3ec9cf3cf94cb70d2a39c8e27465879c65184 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 16:37:54 +0800 Subject: [PATCH 06/22] Add robust git clone function with retry logic - Implements git_clone_robust function with 3 retry attempts - First attempts shallow clone (depth=1) for better performance - Falls back to deeper clone (depth=100) then full clone if needed - Adds 5-second delay between retry attempts - Helps resolve timeout issues when cloning large repositories - Applied to both main XO repository and plugin clones --- xo-install.sh | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 87e6fec..16dd65a 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -177,6 +177,58 @@ function printwarning { echo -e "${INFO} $*" } +# Robust git clone with retry logic and shallow clone support +function git_clone_robust { + local repo_url="$1" + local target_dir="$2" + local branch="${3:-master}" + local max_retries=3 + local retry_count=0 + local clone_success=0 + + while [[ $retry_count -lt $max_retries ]] && [[ $clone_success -eq 0 ]]; do + if [[ $retry_count -eq 0 ]]; then + # First try: shallow clone with depth 1 + printprog "Cloning repository (attempt $((retry_count+1))/$max_retries, shallow)" + if git clone --depth 1 --single-branch --branch "$branch" "$repo_url" "$target_dir" 2>/dev/null; then + clone_success=1 + printok "Repository cloned successfully (shallow)" + fi + elif [[ $retry_count -eq 1 ]]; then + # Second try: shallow clone with more depth + printprog "Cloning repository (attempt $((retry_count+1))/$max_retries, depth=100)" + if git clone --depth 100 --single-branch --branch "$branch" "$repo_url" "$target_dir" 2>/dev/null; then + clone_success=1 + printok "Repository cloned successfully (depth=100)" + fi + else + # Final try: full clone + printprog "Cloning repository (attempt $((retry_count+1))/$max_retries, full)" + if git clone "$repo_url" "$target_dir" 2>/dev/null; then + clone_success=1 + printok "Repository cloned successfully (full)" + fi + fi + + if [[ $clone_success -eq 0 ]]; then + retry_count=$((retry_count+1)) + if [[ $retry_count -lt $max_retries ]]; then + printinfo "Clone failed, retrying in 5 seconds..." + sleep 5 + # Clean up any partial clone + rm -rf "$target_dir" 2>/dev/null || true + fi + fi + done + + if [[ $clone_success -eq 0 ]]; then + printfail "Failed to clone repository after $max_retries attempts" + return 1 + fi + + return 0 +} + # if script fails at a stage where installation is not complete, we don't want to keep the install specific directory and content # this is called by trap inside different functions function ErrorHandling { @@ -518,7 +570,10 @@ function InstallAdditionalXOPlugins { if [[ ! -d "$PLUGIN_SRC_DIR" ]]; then runcmd "mkdir -p \"$PLUGIN_SRC_DIR\"" - runcmd "git clone \"${x}\" \"$PLUGIN_SRC_DIR\"" + if ! git_clone_robust "${x}" "$PLUGIN_SRC_DIR" "master"; then + printfail "Failed to clone plugin repository: ${x}" + exit 1 + fi else runcmd "cd \"$PLUGIN_SRC_DIR\" && git pull --ff-only" runcmd "cd $SCRIPT_DIR" @@ -630,7 +685,10 @@ function PrepInstall { printinfo "Fetching $XO_SVC_DESC source code" if [[ ! -d "$XO_SRC_DIR" ]]; then runcmd "mkdir -p \"$XO_SRC_DIR\"" - runcmd "git clone \"${REPOSITORY}\" \"$XO_SRC_DIR\"" + if ! git_clone_robust "${REPOSITORY}" "$XO_SRC_DIR" "$BRANCH"; then + printfail "Failed to clone repository" + exit 1 + fi else runcmd "cd \"$XO_SRC_DIR\" && git remote set-url origin \"${REPOSITORY}\" && \ git fetch --prune && \ From ca16a3bb852528a87ad0cf514c31d0c7391fac1f Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 16:41:48 +0800 Subject: [PATCH 07/22] Add safety checks for git repository detection and directory removal - Validate XO_SRC_DIR path to prevent accidental removal of wrong directories - Check if existing directories are valid git repositories before updating - Only remove empty directories automatically - Require manual intervention for non-empty, non-git directories - Apply same logic to plugin directory handling - Fixes issue where script tried git commands on non-git directories --- xo-install.sh | 58 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 16dd65a..a694641 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -568,15 +568,24 @@ function InstallAdditionalXOPlugins { local PLUGIN_NAME=$(runcmd_stdout "basename '$x' | rev | cut -c 5- | rev") local PLUGIN_SRC_DIR=$(runcmd_stdout "realpath -m '$XO_SRC_DIR/../$PLUGIN_NAME'") - if [[ ! -d "$PLUGIN_SRC_DIR" ]]; then + # Check if directory exists and is a valid git repository + if [[ -d "$PLUGIN_SRC_DIR" ]] && [[ -d "$PLUGIN_SRC_DIR/.git" ]]; then + # Directory exists and is a git repo, update it + runcmd "cd \"$PLUGIN_SRC_DIR\" && git pull --ff-only" + runcmd "cd $SCRIPT_DIR" + else + # Directory doesn't exist or is not a git repo + if [[ -d "$PLUGIN_SRC_DIR" ]]; then + # Directory exists but is not a git repo + printinfo "Plugin directory exists but is not a git repository, removing" + runcmd "rm -rf \"$PLUGIN_SRC_DIR\"" + fi + runcmd "mkdir -p \"$PLUGIN_SRC_DIR\"" if ! git_clone_robust "${x}" "$PLUGIN_SRC_DIR" "master"; then printfail "Failed to clone plugin repository: ${x}" exit 1 fi - else - runcmd "cd \"$PLUGIN_SRC_DIR\" && git pull --ff-only" - runcmd "cd $SCRIPT_DIR" fi runcmd "cp -r $PLUGIN_SRC_DIR $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/" @@ -683,17 +692,46 @@ function PrepInstall { echo # keep the actual source code in one directory and either clone or git fetch depending on if directory exists already printinfo "Fetching $XO_SVC_DESC source code" - if [[ ! -d "$XO_SRC_DIR" ]]; then + + # Validate XO_SRC_DIR path for safety + if [[ -z "$XO_SRC_DIR" ]] || [[ "$XO_SRC_DIR" == "/" ]] || [[ "$XO_SRC_DIR" == "$HOME" ]] || [[ ! "$XO_SRC_DIR" =~ ^/opt/xo/ ]]; then + printfail "Invalid XO_SRC_DIR path: $XO_SRC_DIR" + exit 1 + fi + + # Check if directory exists and is a valid git repository + if [[ -d "$XO_SRC_DIR" ]] && [[ -d "$XO_SRC_DIR/.git" ]]; then + # Directory exists and is a git repo, update it + runcmd "cd \"$XO_SRC_DIR\" && git remote set-url origin \"${REPOSITORY}\" && \ + git fetch --prune && \ + git reset --hard origin/master && \ + git clean -xdff" + else + # Directory doesn't exist or is not a git repo + if [[ -d "$XO_SRC_DIR" ]]; then + # Directory exists but is not a git repo + printinfo "Directory exists but is not a git repository" + # Check if directory is empty or contains only a few files (likely from failed clone) + local file_count=$(find "$XO_SRC_DIR" -maxdepth 1 -type f 2>/dev/null | wc -l) + local dir_count=$(find "$XO_SRC_DIR" -maxdepth 1 -type d 2>/dev/null | wc -l) + + if [[ $file_count -eq 0 ]] && [[ $dir_count -le 1 ]]; then + # Directory is empty or nearly empty, safe to remove + printinfo "Removing empty directory and re-cloning" + runcmd "rm -rf \"$XO_SRC_DIR\"" + else + # Directory has content, ask for confirmation or fail + printfail "Directory $XO_SRC_DIR exists but is not a git repository and contains files" + printfail "Please manually remove or backup the directory and try again" + exit 1 + fi + fi + runcmd "mkdir -p \"$XO_SRC_DIR\"" if ! git_clone_robust "${REPOSITORY}" "$XO_SRC_DIR" "$BRANCH"; then printfail "Failed to clone repository" exit 1 fi - else - runcmd "cd \"$XO_SRC_DIR\" && git remote set-url origin \"${REPOSITORY}\" && \ - git fetch --prune && \ - git reset --hard origin/master && \ - git clean -xdff" fi # Deploy the latest xen-orchestra source to the new install directory. From 501f2d3c20e3fc03d50c8d25212ba1ae688ea2c5 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 16:54:49 +0800 Subject: [PATCH 08/22] Simplify git clone to use only shallow clones - Remove unnecessary full clone fallback - Always use depth=1 for efficiency - Installation doesn't need git history - Reduces download size and time significantly - Keeps retry logic for network reliability --- xo-install.sh | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index a694641..cbafb2f 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -177,7 +177,7 @@ function printwarning { echo -e "${INFO} $*" } -# Robust git clone with retry logic and shallow clone support +# Robust git clone with retry logic - uses shallow clone for efficiency function git_clone_robust { local repo_url="$1" local target_dir="$2" @@ -187,30 +187,13 @@ function git_clone_robust { local clone_success=0 while [[ $retry_count -lt $max_retries ]] && [[ $clone_success -eq 0 ]]; do - if [[ $retry_count -eq 0 ]]; then - # First try: shallow clone with depth 1 - printprog "Cloning repository (attempt $((retry_count+1))/$max_retries, shallow)" - if git clone --depth 1 --single-branch --branch "$branch" "$repo_url" "$target_dir" 2>/dev/null; then - clone_success=1 - printok "Repository cloned successfully (shallow)" - fi - elif [[ $retry_count -eq 1 ]]; then - # Second try: shallow clone with more depth - printprog "Cloning repository (attempt $((retry_count+1))/$max_retries, depth=100)" - if git clone --depth 100 --single-branch --branch "$branch" "$repo_url" "$target_dir" 2>/dev/null; then - clone_success=1 - printok "Repository cloned successfully (depth=100)" - fi - else - # Final try: full clone - printprog "Cloning repository (attempt $((retry_count+1))/$max_retries, full)" - if git clone "$repo_url" "$target_dir" 2>/dev/null; then - clone_success=1 - printok "Repository cloned successfully (full)" - fi - fi + printprog "Cloning repository (attempt $((retry_count+1))/$max_retries)" - if [[ $clone_success -eq 0 ]]; then + # Always use shallow clone with depth 1 - we don't need history for installation + if git clone --depth 1 --single-branch --branch "$branch" "$repo_url" "$target_dir" 2>/dev/null; then + clone_success=1 + printok "Repository cloned successfully" + else retry_count=$((retry_count+1)) if [[ $retry_count -lt $max_retries ]]; then printinfo "Clone failed, retrying in 5 seconds..." From b0fdf857341198b18f649d15bfdf389a4f8fc3af Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 17:00:05 +0800 Subject: [PATCH 09/22] Add progress monitoring for yarn build process - Created run_yarn_build function with live progress indicator - Shows spinner, elapsed time counter during build - Separates dependency installation from build phase - Applied to both xo-server/xo-web and xo-proxy builds - Provides better user feedback during long build operations - Build times are now visible to users --- xo-install.sh | 72 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index cbafb2f..df2f169 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -155,6 +155,43 @@ function runcmd_stdout { bash -c -o pipefail "$1" 2>>"$LOGFILE" | tee -a "$LOGFILE" || return 1 } +# Run yarn build with progress indication +function run_yarn_build { + local build_dir="$1" + local build_cmd="$2" + local build_desc="${3:-build}" + + echo "+ cd $build_dir && $build_cmd" >>"$LOGFILE" + + # Start the build in background and get its PID + (cd "$build_dir" && bash -c "$build_cmd" >>"$LOGFILE" 2>&1) & + local build_pid=$! + + # Show progress while build is running + local spin='-\|/' + local i=0 + local elapsed=0 + + while kill -0 $build_pid 2>/dev/null; do + i=$(( (i+1) %4 )) + printf "\r${PROGRESS} Running $build_desc... %c [%ds]" "${spin:$i:1}" $elapsed + sleep 1 + elapsed=$((elapsed+1)) + done + + # Check if build succeeded + wait $build_pid + local build_status=$? + + if [ $build_status -eq 0 ]; then + printf "\r${OK} Running $build_desc [%ds] \n" $elapsed + return 0 + else + printf "\r${FAIL} Running $build_desc failed [%ds] \n" $elapsed + return 1 + fi +} + # make output we print pretty function printprog { echo -ne "${PROGRESS} $*" @@ -828,9 +865,23 @@ function InstallXO { echo printinfo "xo-server and xo-web build takes quite a while. Grab a cup of coffee and lay back" echo - printprog "Running installation" - runcmd "cd $INSTALLDIR/xo-builds/xen-orchestra-$TIME && yarn --network-timeout ${YARN_NETWORK_TIMEOUT} && yarn --network-timeout ${YARN_NETWORK_TIMEOUT} build" - printok "Running installation" + + # Run yarn install with progress + printprog "Installing dependencies" + if runcmd "cd $INSTALLDIR/xo-builds/xen-orchestra-$TIME && yarn --network-timeout ${YARN_NETWORK_TIMEOUT}"; then + printok "Installing dependencies" + else + printfail "Failed to install dependencies" + exit 1 + fi + + # Run yarn build with progress indicator + run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "yarn --network-timeout ${YARN_NETWORK_TIMEOUT} build" "xo-server and xo-web build" + + # Run v6 build if needed + if [ "$INCLUDE_V6" == "true" ]; then + run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "yarn --network-timeout ${YARN_NETWORK_TIMEOUT} run turbo run build --filter @xen-orchestra/web" "v6 web interface build" + fi # Install plugins (takes care of 3rd party plugins as well) InstallXOPlugins @@ -1119,9 +1170,18 @@ function InstallXOProxy { echo printinfo "xo-proxy build takes quite a while. Grab a cup of coffee and lay back" echo - printprog "Running installation" - runcmd "cd $INSTALLDIR/xo-builds/xen-orchestra-$TIME && yarn --network-timeout ${YARN_NETWORK_TIMEOUT} && yarn --network-timeout ${YARN_NETWORK_TIMEOUT} build" - printok "Running installation" + + # Run yarn install with progress + printprog "Installing dependencies" + if runcmd "cd $INSTALLDIR/xo-builds/xen-orchestra-$TIME && yarn --network-timeout ${YARN_NETWORK_TIMEOUT}"; then + printok "Installing dependencies" + else + printfail "Failed to install dependencies" + exit 1 + fi + + # Run yarn build with progress indicator + run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "yarn --network-timeout ${YARN_NETWORK_TIMEOUT} build" "xo-proxy build" # shutdown possibly running xo-server if [[ $(runcmd_stdout "pgrep -f '^([a-zA-Z0-9_\/-]+?)node.*xo-proxy'") ]]; then From 1c9812f768f4138d4dd52c7f0c237022dbc3de04 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 17:06:09 +0800 Subject: [PATCH 10/22] Add complete uninstall option to the installer - New UninstallXO function with comprehensive safety checks - Validates paths before any deletion to prevent accidental removal - Stops and disables systemd services (xo-server, xo-proxy) - Removes service files from /etc/systemd/system/ - Removes build directories, source code, and symlinks - Optionally removes configuration and data files - Cleans up sudoers file if present - Added as option 6 in the main menu - Preserves log files for troubleshooting --- xo-install.sh | 220 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 1 deletion(-) diff --git a/xo-install.sh b/xo-install.sh index df2f169..ab52e84 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -1458,6 +1458,219 @@ function RollBackInstallation { } +# Uninstall XO build, services, and configuration +function UninstallXO { + + set -uo pipefail + + # Validate INSTALLDIR path for safety + if [[ -z "$INSTALLDIR" ]] || [[ "$INSTALLDIR" == "/" ]] || [[ "$INSTALLDIR" == "$HOME" ]] || [[ ! "$INSTALLDIR" =~ ^/opt/ ]]; then + printfail "Invalid INSTALLDIR path: $INSTALLDIR - refusing to uninstall for safety" + exit 1 + fi + + # Check if XO is installed + if [[ ! -d "$INSTALLDIR" ]]; then + printinfo "XO installation directory not found at $INSTALLDIR" + exit 0 + fi + + # Determine what's installed + local has_xo_server=false + local has_xo_proxy=false + + if [[ -L "$INSTALLDIR/xo-server" ]] && [[ -n $(runcmd_stdout "readlink -e $INSTALLDIR/xo-server" 2>/dev/null || true) ]]; then + has_xo_server=true + fi + + if [[ -L "$INSTALLDIR/xo-proxy" ]] && [[ -n $(runcmd_stdout "readlink -e $INSTALLDIR/xo-proxy" 2>/dev/null || true) ]]; then + has_xo_proxy=true + fi + + # Check for service files + local has_xo_server_service=false + local has_xo_proxy_service=false + + if [[ -f "/etc/systemd/system/xo-server.service" ]]; then + has_xo_server_service=true + fi + + if [[ -f "/etc/systemd/system/xo-proxy.service" ]]; then + has_xo_proxy_service=true + fi + + if [[ "$has_xo_server" == "false" ]] && [[ "$has_xo_proxy" == "false" ]] && [[ "$has_xo_server_service" == "false" ]] && [[ "$has_xo_proxy_service" == "false" ]]; then + printinfo "No XO installation found" + exit 0 + fi + + echo + printinfo "This will completely uninstall XO from your system" + echo + + # Show what will be removed + echo "The following will be removed:" + echo " - All builds in $INSTALLDIR/xo-builds/" + echo " - Source code in $INSTALLDIR/xo-src/" + echo " - Symlinks in $INSTALLDIR" + [[ "$has_xo_server_service" == "true" ]] && echo " - xo-server systemd service" + [[ "$has_xo_proxy_service" == "true" ]] && echo " - xo-proxy systemd service" + echo " - XO configuration files in $CONFIGPATH/.config/xo-server" + echo " - XO cache and data in $CONFIGPATH/.local/share/xo-server" + echo + + read -r -p "Are you sure you want to completely uninstall XO? [y/N]: " answer + case $answer in + y|Y) + ;; + *) + echo "Uninstall cancelled" + exit 0 + ;; + esac + + # Stop and disable xo-server service + if [[ "$has_xo_server_service" == "true" ]]; then + echo + if [[ $(runcmd_stdout "systemctl is-active xo-server" 2>/dev/null || true) == "active" ]]; then + printprog "Stopping xo-server service" + runcmd "/bin/systemctl stop xo-server" || true + printok "Stopped xo-server service" + fi + + if [[ $(runcmd_stdout "systemctl is-enabled xo-server" 2>/dev/null || true) == "enabled" ]]; then + printprog "Disabling xo-server service" + runcmd "/bin/systemctl disable xo-server" || true + printok "Disabled xo-server service" + fi + + printprog "Removing xo-server service file" + runcmd "rm -f /etc/systemd/system/xo-server.service" + printok "Removed xo-server service file" + fi + + # Stop and disable xo-proxy service + if [[ "$has_xo_proxy_service" == "true" ]]; then + echo + if [[ $(runcmd_stdout "systemctl is-active xo-proxy" 2>/dev/null || true) == "active" ]]; then + printprog "Stopping xo-proxy service" + runcmd "/bin/systemctl stop xo-proxy" || true + printok "Stopped xo-proxy service" + fi + + if [[ $(runcmd_stdout "systemctl is-enabled xo-proxy" 2>/dev/null || true) == "enabled" ]]; then + printprog "Disabling xo-proxy service" + runcmd "/bin/systemctl disable xo-proxy" || true + printok "Disabled xo-proxy service" + fi + + printprog "Removing xo-proxy service file" + runcmd "rm -f /etc/systemd/system/xo-proxy.service" + printok "Removed xo-proxy service file" + fi + + # Reload systemd if we removed service files + if [[ "$has_xo_server_service" == "true" ]] || [[ "$has_xo_proxy_service" == "true" ]]; then + printprog "Reloading systemd daemon" + runcmd "/bin/systemctl daemon-reload" + printok "Reloaded systemd daemon" + fi + + # Remove symlinks + echo + printprog "Removing symlinks" + local symlinks=("xo-server" "xo-web" "xo-cli" "xo-proxy") + for link in "${symlinks[@]}"; do + if [[ -L "$INSTALLDIR/$link" ]]; then + runcmd "rm -f \"$INSTALLDIR/$link\"" + printinfo "Removed $INSTALLDIR/$link symlink" + fi + done + printok "Removed symlinks" + + # Remove build directories + if [[ -d "$INSTALLDIR/xo-builds" ]]; then + echo + printprog "Removing build directories" + # Double-check the path is safe + if [[ "$INSTALLDIR/xo-builds" =~ ^/opt/xo/xo-builds$ ]]; then + runcmd "rm -rf \"$INSTALLDIR/xo-builds\"" + printok "Removed build directories" + else + printfail "Unsafe path detected, skipping removal of $INSTALLDIR/xo-builds" + fi + fi + + # Remove source directories + if [[ -d "$INSTALLDIR/xo-src" ]]; then + echo + printprog "Removing source directories" + # Double-check the path is safe + if [[ "$INSTALLDIR/xo-src" =~ ^/opt/xo/xo-src$ ]]; then + runcmd "rm -rf \"$INSTALLDIR/xo-src\"" + printok "Removed source directories" + else + printfail "Unsafe path detected, skipping removal of $INSTALLDIR/xo-src" + fi + fi + + # Remove XO configuration and data files + echo + read -r -p "Remove XO configuration and data files? [y/N]: " answer + case $answer in + y|Y) + if [[ -d "$CONFIGPATH/.config/xo-server" ]]; then + printprog "Removing XO server configuration" + runcmd "rm -rf \"$CONFIGPATH/.config/xo-server\"" + printok "Removed XO server configuration" + fi + + if [[ -d "$CONFIGPATH/.local/share/xo-server" ]]; then + printprog "Removing XO server data" + runcmd "rm -rf \"$CONFIGPATH/.local/share/xo-server\"" + printok "Removed XO server data" + fi + + if [[ -d "$CONFIGPATH/.cache/xo-server" ]]; then + printprog "Removing XO server cache" + runcmd "rm -rf \"$CONFIGPATH/.cache/xo-server\"" + printok "Removed XO server cache" + fi + ;; + *) + printinfo "Preserving configuration and data files" + ;; + esac + + # Check if INSTALLDIR is now empty and remove if so + if [[ -d "$INSTALLDIR" ]]; then + local remaining_files=$(find "$INSTALLDIR" -type f 2>/dev/null | wc -l) + if [[ $remaining_files -eq 0 ]]; then + echo + printprog "Removing empty installation directory" + runcmd "rmdir \"$INSTALLDIR\"" 2>/dev/null || true + printok "Removed empty installation directory" + else + echo + printinfo "Installation directory not empty, preserving $INSTALLDIR" + fi + fi + + # Remove sudoers file if it exists + if [[ -f "$SUDOERSFILE" ]]; then + echo + printprog "Removing sudoers file" + runcmd "rm -f \"$SUDOERSFILE\"" + printok "Removed sudoers file" + fi + + echo + printok "XO uninstall completed" + echo + printinfo "Log files have been preserved in $LOGPATH" + echo +} + # only specific list of operating systems are supported. check operating system name/version here function CheckOS { @@ -1697,7 +1910,8 @@ function StartUpScreen { echo -e "${COLOR_WHITE}3. Rollback${COLOR_N}" echo -e "${COLOR_WHITE}4. Install proxy${COLOR_N}" echo -e "${COLOR_WHITE}5. Update proxy${COLOR_N}" - echo -e "${COLOR_WHITE}6. Exit${COLOR_N}" + echo -e "${COLOR_WHITE}6. Uninstall${COLOR_N}" + echo -e "${COLOR_WHITE}7. Exit${COLOR_N}" echo read -r -p ": " option @@ -1791,6 +2005,10 @@ function StartUpScreen { exit 0 ;; 6) + UninstallXO + exit 0 + ;; + 7) exit 0 ;; *) From 2ea5571237ff267e488225716efc29d7992f909c Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 17:10:42 +0800 Subject: [PATCH 11/22] Improve uninstall safety for configuration and data files - Clarified initial prompt to show config/data requires separate confirmation - Added explicit warning that config/data deletion cannot be undone - Requires typing 'DELETE' to confirm permanent removal of config/data - Shows exact locations of config and data directories - Makes it clear these contain user settings and database - Prevents accidental deletion of important user data --- xo-install.sh | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index ab52e84..eced43a 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -1505,7 +1505,7 @@ function UninstallXO { fi echo - printinfo "This will completely uninstall XO from your system" + printinfo "This will uninstall XO from your system" echo # Show what will be removed @@ -1515,11 +1515,13 @@ function UninstallXO { echo " - Symlinks in $INSTALLDIR" [[ "$has_xo_server_service" == "true" ]] && echo " - xo-server systemd service" [[ "$has_xo_proxy_service" == "true" ]] && echo " - xo-proxy systemd service" + echo + echo "The following will require SEPARATE confirmation:" echo " - XO configuration files in $CONFIGPATH/.config/xo-server" echo " - XO cache and data in $CONFIGPATH/.local/share/xo-server" echo - read -r -p "Are you sure you want to completely uninstall XO? [y/N]: " answer + read -r -p "Are you sure you want to uninstall XO? [y/N]: " answer case $answer in y|Y) ;; @@ -1614,27 +1616,38 @@ function UninstallXO { fi fi - # Remove XO configuration and data files + # Remove XO configuration and data files - requires explicit confirmation + echo + printinfo "XO configuration and data files contain your settings and database" + echo " Location: $CONFIGPATH/.config/xo-server" + echo " Location: $CONFIGPATH/.local/share/xo-server" echo - read -r -p "Remove XO configuration and data files? [y/N]: " answer + read -r -p "Do you want to PERMANENTLY DELETE configuration and data files? [y/N]: " answer case $answer in y|Y) - if [[ -d "$CONFIGPATH/.config/xo-server" ]]; then - printprog "Removing XO server configuration" - runcmd "rm -rf \"$CONFIGPATH/.config/xo-server\"" - printok "Removed XO server configuration" - fi + echo + printwarning "This action cannot be undone!" + read -r -p "Type 'DELETE' to confirm permanent removal of config and data: " confirm + if [[ "$confirm" == "DELETE" ]]; then + if [[ -d "$CONFIGPATH/.config/xo-server" ]]; then + printprog "Removing XO server configuration" + runcmd "rm -rf \"$CONFIGPATH/.config/xo-server\"" + printok "Removed XO server configuration" + fi - if [[ -d "$CONFIGPATH/.local/share/xo-server" ]]; then - printprog "Removing XO server data" - runcmd "rm -rf \"$CONFIGPATH/.local/share/xo-server\"" - printok "Removed XO server data" - fi + if [[ -d "$CONFIGPATH/.local/share/xo-server" ]]; then + printprog "Removing XO server data" + runcmd "rm -rf \"$CONFIGPATH/.local/share/xo-server\"" + printok "Removed XO server data" + fi - if [[ -d "$CONFIGPATH/.cache/xo-server" ]]; then - printprog "Removing XO server cache" - runcmd "rm -rf \"$CONFIGPATH/.cache/xo-server\"" - printok "Removed XO server cache" + if [[ -d "$CONFIGPATH/.cache/xo-server" ]]; then + printprog "Removing XO server cache" + runcmd "rm -rf \"$CONFIGPATH/.cache/xo-server\"" + printok "Removed XO server cache" + fi + else + printinfo "Deletion cancelled - preserving configuration and data files" fi ;; *) From 7426dfebc49b98c0bad25480e38b1b729094a340 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 17:15:44 +0800 Subject: [PATCH 12/22] Separate xo-server and xo-web builds for better progress visibility - Split monolithic build into separate xo-server and xo-web builds - Uses Turbo's --filter flag to build specific packages - xo-server builds first with its plugins (xo-server-*) - xo-web builds separately after server - xo-proxy now only builds proxy package, not the web UI - Users can see which component is currently building - Maintains TURBO_TELEMETRY_DISABLED for privacy --- xo-install.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index eced43a..1738554 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -875,8 +875,12 @@ function InstallXO { exit 1 fi - # Run yarn build with progress indicator - run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "yarn --network-timeout ${YARN_NETWORK_TIMEOUT} build" "xo-server and xo-web build" + # Run yarn build with progress indicator - build separately for better visibility + # Build xo-server and its plugins first + run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "TURBO_TELEMETRY_DISABLED=1 yarn --network-timeout ${YARN_NETWORK_TIMEOUT} run turbo run build --filter xo-server --filter xo-server-'*'" "xo-server build" + + # Then build xo-web + run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "TURBO_TELEMETRY_DISABLED=1 yarn --network-timeout ${YARN_NETWORK_TIMEOUT} run turbo run build --filter xo-web" "xo-web build" # Run v6 build if needed if [ "$INCLUDE_V6" == "true" ]; then @@ -1180,8 +1184,8 @@ function InstallXOProxy { exit 1 fi - # Run yarn build with progress indicator - run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "yarn --network-timeout ${YARN_NETWORK_TIMEOUT} build" "xo-proxy build" + # Run yarn build with progress indicator - only build proxy, not web UI + run_yarn_build "$INSTALLDIR/xo-builds/xen-orchestra-$TIME" "TURBO_TELEMETRY_DISABLED=1 yarn --network-timeout ${YARN_NETWORK_TIMEOUT} run turbo run build --filter @xen-orchestra/proxy" "xo-proxy build" # shutdown possibly running xo-server if [[ $(runcmd_stdout "pgrep -f '^([a-zA-Z0-9_\/-]+?)node.*xo-proxy'") ]]; then From 291de73350040296a1b1fdd60e9f66754d337558 Mon Sep 17 00:00:00 2001 From: user Date: Fri, 5 Sep 2025 02:46:37 -0700 Subject: [PATCH 13/22] Add secure credential management for XO admin users - Add interactive credential setup during initial installation - Create new admin users and delete default admin@admin.net for security - Add password generation function for secure auto-generated passwords - Add comprehensive credential management menu option (option 6) - Support creating, updating, and deleting admin users - Add email validation and password strength requirements - Prevent deletion of last admin user for safety - Show clear security warnings when using default credentials This enhancement significantly improves security by: 1. Prompting users to change default credentials during installation 2. Removing the well-known default admin@admin.net user when custom credentials are set 3. Providing easy-to-use tools for credential management post-installation --- xo-install.sh | 449 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 445 insertions(+), 4 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 1738554..7ca34bb 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -267,6 +267,428 @@ function ErrorHandling { exit 1 } +# Generate secure random password +function GenerateSecurePassword { + # Generate a 16-character password with alphanumeric and special characters + local password=$(tr -dc 'A-Za-z0-9!@#$%^&*()_+-=' /dev/null 2>&1; then + printfail "Failed to connect to XO server with default credentials" + return 1 + fi + + # If we're creating a new user (different email), create it first + if [[ -n "$new_email" ]] && [[ "$new_email" != "admin@admin.net" ]]; then + # Create new admin user + if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then + printok "New admin user created successfully" + + # Get the default admin user ID to delete it + if [[ "$delete_default" == "true" ]]; then + local default_admin_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: 'admin@admin.net'\" | grep 'id:' | cut -d\"'\" -f2") + + if [[ -n "$default_admin_id" ]]; then + # Re-register with new credentials before deleting old admin + if runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$new_password'" >/dev/null 2>&1; then + if runcmd_stdout "xo-cli user.delete id=$default_admin_id" >/dev/null 2>&1; then + printok "Default admin user (admin@admin.net) deleted for security" + else + printfail "Warning: Could not delete default admin user. Please delete it manually!" + fi + else + printfail "Warning: Could not re-authenticate with new credentials. Default user not deleted." + fi + else + printfail "Warning: Could not find default admin user ID to delete" + fi + fi + + echo + echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" + echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + echo + echo -e " ${COLOR_YELLOW}IMPORTANT: Save these credentials securely!${COLOR_N}" + return 0 + else + printfail "Failed to create new admin user" + return 1 + fi + elif [[ -n "$new_password" ]]; then + # Just changing password for admin@admin.net + local admin_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: 'admin@admin.net'\" | grep 'id:' | cut -d\"'\" -f2") + + if [[ -z "$admin_id" ]]; then + printfail "Failed to get admin user ID" + return 1 + fi + + if runcmd_stdout "xo-cli user.set id=$admin_id password='$new_password'" >/dev/null 2>&1; then + printok "Admin password updated successfully" + echo + echo -e " ${COLOR_GREEN}Username: admin@admin.net${COLOR_N}" + echo -e " ${COLOR_GREEN}New password: $new_password${COLOR_N}" + echo + echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" + echo -e " ${COLOR_YELLOW}WARNING: Consider creating a new admin user with a different email for better security${COLOR_N}" + return 0 + else + printfail "Failed to update admin password" + return 1 + fi + fi +} + +# Interactive credential setup for installation +function SetupCredentialsInteractive { + echo + echo "-----------------------------------------" + echo "Admin Credential Setup" + echo "-----------------------------------------" + echo + echo "For security, it's highly recommended to change the default credentials." + echo + echo "Choose an option:" + echo "1. Keep default credentials (admin@admin.net / admin) - NOT RECOMMENDED" + echo "2. Set custom email and password (default user will be deleted)" + echo "3. Set custom email with auto-generated password (default user will be deleted)" + echo "4. Change default password only (keeps admin@admin.net email)" + echo + read -r -p "Option [1-4]: " cred_option + + case $cred_option in + 1) + echo + echo -e " ${COLOR_RED}WARNING: Using default credentials is a security risk!${COLOR_N}" + echo -e " ${COLOR_YELLOW}Default username: admin@admin.net password: admin${COLOR_N}" + echo -e " ${COLOR_RED}IMPORTANT: Change these credentials immediately after installation!${COLOR_N}" + ;; + 2) + echo + read -r -p "Enter new admin email: " new_email + # Validate email format + if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo -e "${COLOR_RED}Invalid email format. Using default credentials.${COLOR_N}" + echo -e " ${COLOR_YELLOW}Default username: admin@admin.net password: admin${COLOR_N}" + return + fi + + while true; do + read -r -s -p "Enter new admin password: " new_password + echo + read -r -s -p "Confirm password: " confirm_password + echo + if [[ "$new_password" == "$confirm_password" ]]; then + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters long${COLOR_N}" + else + break + fi + else + echo -e "${COLOR_RED}Passwords do not match${COLOR_N}" + fi + done + ChangeAdminCredentials "$new_email" "$new_password" "true" + ;; + 3) + echo + read -r -p "Enter new admin email: " new_email + # Validate email format + if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo -e "${COLOR_RED}Invalid email format. Using default credentials.${COLOR_N}" + echo -e " ${COLOR_YELLOW}Default username: admin@admin.net password: admin${COLOR_N}" + return + fi + + local auto_password=$(GenerateSecurePassword) + ChangeAdminCredentials "$new_email" "$auto_password" "true" + ;; + 4) + echo + echo -e " ${COLOR_YELLOW}Generating secure password for admin@admin.net${COLOR_N}" + local auto_password=$(GenerateSecurePassword) + ChangeAdminCredentials "" "$auto_password" "false" + ;; + *) + echo + echo -e " ${COLOR_YELLOW}Invalid option, using default credentials${COLOR_N}" + echo -e " ${COLOR_RED}WARNING: Default username: admin@admin.net password: admin${COLOR_N}" + echo -e " ${COLOR_RED}IMPORTANT: Change these credentials immediately after installation!${COLOR_N}" + ;; + esac +} + +# Standalone credential management function for menu +function ManageCredentials { + echo + echo "-----------------------------------------" + echo "Manage Admin Credentials" + echo "-----------------------------------------" + echo + + # Check if XO server is running + if ! runcmd_stdout "pgrep -f '^([a-zA-Z0-9_\/-]+?)node.*xo-server'" >/dev/null 2>&1; then + printfail "XO Server is not running. Please install or start it first." + return 1 + fi + + echo "This will manage admin credentials for your XO installation." + echo + + # First, authenticate with current credentials + echo "Please authenticate with your current admin credentials:" + read -r -p "Enter current admin email: " current_email + read -r -s -p "Enter current admin password: " current_password + echo + + # Try to authenticate with provided credentials + if ! runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$current_email' '$current_password'" >/dev/null 2>&1; then + printfail "Failed to authenticate with provided credentials" + return 1 + fi + + printok "Authentication successful" + echo + + echo "Choose an option:" + echo "1. Create new admin user and delete current one" + echo "2. Create new admin user (keep current one)" + echo "3. Change current user's password only" + echo "4. List all admin users" + echo "5. Delete a user" + echo "6. Cancel" + echo + read -r -p "Option [1-6]: " manage_option + + case $manage_option in + 1) + echo + read -r -p "Enter new admin email: " new_email + # Validate email format + if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo -e "${COLOR_RED}Invalid email format${COLOR_N}" + return 1 + fi + + echo "Choose password option:" + echo "1. Enter custom password" + echo "2. Generate secure password automatically" + read -r -p "Option [1-2]: " pass_option + + if [[ "$pass_option" == "1" ]]; then + while true; do + read -r -s -p "Enter new admin password: " new_password + echo + read -r -s -p "Confirm password: " confirm_password + echo + if [[ "$new_password" == "$confirm_password" ]]; then + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters long${COLOR_N}" + else + break + fi + else + echo -e "${COLOR_RED}Passwords do not match${COLOR_N}" + fi + done + else + new_password=$(GenerateSecurePassword) + echo + echo -e " ${COLOR_GREEN}Generated password: $new_password${COLOR_N}" + echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" + fi + + # Create new admin user + if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then + printok "New admin user created successfully" + + # Get current user ID to delete + local current_user_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: '$current_email'\" | grep 'id:' | cut -d\"'\" -f2") + + if [[ -n "$current_user_id" ]]; then + # Re-register with new credentials + if runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$new_password'" >/dev/null 2>&1; then + if runcmd_stdout "xo-cli user.delete id=$current_user_id" >/dev/null 2>&1; then + printok "Previous admin user ($current_email) deleted" + else + printfail "Warning: Could not delete previous admin user" + fi + fi + fi + + echo + echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" + if [[ "$pass_option" != "1" ]]; then + echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + fi + else + printfail "Failed to create new admin user" + fi + ;; + 2) + echo + read -r -p "Enter new admin email: " new_email + # Validate email format + if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo -e "${COLOR_RED}Invalid email format${COLOR_N}" + return 1 + fi + + echo "Choose password option:" + echo "1. Enter custom password" + echo "2. Generate secure password automatically" + read -r -p "Option [1-2]: " pass_option + + if [[ "$pass_option" == "1" ]]; then + while true; do + read -r -s -p "Enter new admin password: " new_password + echo + read -r -s -p "Confirm password: " confirm_password + echo + if [[ "$new_password" == "$confirm_password" ]]; then + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters long${COLOR_N}" + else + break + fi + else + echo -e "${COLOR_RED}Passwords do not match${COLOR_N}" + fi + done + else + new_password=$(GenerateSecurePassword) + echo + echo -e " ${COLOR_GREEN}Generated password: $new_password${COLOR_N}" + echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" + fi + + # Create new admin user without deleting current one + if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then + printok "New admin user created successfully" + echo + echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" + if [[ "$pass_option" != "1" ]]; then + echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + fi + echo -e " ${COLOR_YELLOW}Note: Previous admin user ($current_email) still exists${COLOR_N}" + else + printfail "Failed to create new admin user" + fi + ;; + 3) + echo + echo "Choose password option:" + echo "1. Enter custom password" + echo "2. Generate secure password automatically" + read -r -p "Option [1-2]: " pass_option + + if [[ "$pass_option" == "1" ]]; then + while true; do + read -r -s -p "Enter new password: " new_password + echo + read -r -s -p "Confirm password: " confirm_password + echo + if [[ "$new_password" == "$confirm_password" ]]; then + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters long${COLOR_N}" + else + break + fi + else + echo -e "${COLOR_RED}Passwords do not match${COLOR_N}" + fi + done + else + new_password=$(GenerateSecurePassword) + echo + echo -e " ${COLOR_GREEN}Generated password: $new_password${COLOR_N}" + echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" + fi + + # Get current user ID + local current_user_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: '$current_email'\" | grep 'id:' | cut -d\"'\" -f2") + + if [[ -n "$current_user_id" ]]; then + if runcmd_stdout "xo-cli user.set id=$current_user_id password='$new_password'" >/dev/null 2>&1; then + printok "Password updated successfully" + else + printfail "Failed to update password" + fi + else + printfail "Failed to find user" + fi + ;; + 4) + echo + echo "Admin users:" + echo + runcmd_stdout "xo-cli user.getAll 2>/dev/null" | grep -B1 -A3 "permission: 'admin'" | grep -E "(email:|id:)" | sed 's/^[[:space:]]*/ /' + ;; + 5) + echo + echo "Admin users:" + echo + local admin_users=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null" | grep -B1 -A3 "permission: 'admin'" | grep -E "email:" | cut -d"'" -f2) + echo "$admin_users" | nl + echo + + # Count admin users + local admin_count=$(echo "$admin_users" | wc -l) + if [[ $admin_count -le 1 ]]; then + echo -e "${COLOR_RED}Cannot delete the only admin user!${COLOR_N}" + return 1 + fi + + read -r -p "Enter the email of the user to delete: " delete_email + + if [[ "$delete_email" == "$current_email" ]]; then + echo -e "${COLOR_RED}Cannot delete the currently authenticated user!${COLOR_N}" + return 1 + fi + + # Get user ID to delete + local delete_user_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: '$delete_email'\" | grep 'id:' | cut -d\"'\" -f2") + + if [[ -n "$delete_user_id" ]]; then + read -r -p "Are you sure you want to delete user $delete_email? [y/N]: " confirm + if [[ "$confirm" == "y" ]] || [[ "$confirm" == "Y" ]]; then + if runcmd_stdout "xo-cli user.delete id=$delete_user_id" >/dev/null 2>&1; then + printok "User $delete_email deleted successfully" + else + printfail "Failed to delete user" + fi + else + echo "Deletion cancelled" + fi + else + printfail "User not found" + fi + ;; + 6) + echo "Cancelled" + return 0 + ;; + *) + echo "Invalid option" + return 1 + ;; + esac +} + # install package dependencies to rpm distros, based on: https://xen-orchestra.com/docs/from_the_sources.html function InstallDependenciesRPM { @@ -1088,7 +1510,19 @@ function VerifyServiceStart { echo -e " ${COLOR_GREEN}WebUI started in port $PORT. Make sure you have firewall rules in place to allow access.${COLOR_N}" # print username and password only when install was ran and skip while updating if [[ "$TASK" == "Installation" ]]; then - echo -e " ${COLOR_GREEN}Default username: admin@admin.net password: admin${COLOR_N}" + # Ask user if they want to set up custom credentials + echo + echo -e " ${COLOR_YELLOW}Would you like to set up custom admin credentials now?${COLOR_N}" + echo -e " ${COLOR_WHITE}(If you skip this, default credentials will be used: admin@admin.net / admin)${COLOR_N}" + read -r -p " Set up custom credentials? [y/N]: " setup_creds + + if [[ "$setup_creds" == "y" ]] || [[ "$setup_creds" == "Y" ]]; then + SetupCredentialsInteractive + else + echo + echo -e " ${COLOR_GREEN}Default username: admin@admin.net password: admin${COLOR_N}" + echo -e " ${COLOR_YELLOW}IMPORTANT: Change these credentials after logging in for security!${COLOR_N}" + fi fi fi if [[ "$XO_SVC" == "xo-proxy" ]]; then @@ -1927,8 +2361,9 @@ function StartUpScreen { echo -e "${COLOR_WHITE}3. Rollback${COLOR_N}" echo -e "${COLOR_WHITE}4. Install proxy${COLOR_N}" echo -e "${COLOR_WHITE}5. Update proxy${COLOR_N}" - echo -e "${COLOR_WHITE}6. Uninstall${COLOR_N}" - echo -e "${COLOR_WHITE}7. Exit${COLOR_N}" + echo -e "${COLOR_WHITE}6. Manage admin credentials${COLOR_N}" + echo -e "${COLOR_WHITE}7. Uninstall${COLOR_N}" + echo -e "${COLOR_WHITE}8. Exit${COLOR_N}" echo read -r -p ": " option @@ -2022,10 +2457,16 @@ function StartUpScreen { exit 0 ;; 6) + ManageCredentials + echo + read -r -p "Press Enter to return to main menu..." + StartUpScreen + ;; + 7) UninstallXO exit 0 ;; - 7) + 8) exit 0 ;; *) From 45459e91cf6d4e40a646780c98309aeda1ee070b Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 18:01:25 +0800 Subject: [PATCH 14/22] Fix output formatting issues in installer script - Remove carriage return from printok function to fix newline issues - Add missing COLOR_YELLOW variable definition --- xo-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xo-install.sh b/xo-install.sh index 7ca34bb..50ce1fc 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -72,6 +72,7 @@ COLOR_GREEN='\e[1;32m' COLOR_RED='\e[1;31m' COLOR_BLUE='\e[1;34m' COLOR_WHITE='\e[1;97m' +COLOR_YELLOW='\e[1;33m' OK="[${COLOR_GREEN}ok${COLOR_N}]" FAIL="[${COLOR_RED}fail${COLOR_N}]" INFO="[${COLOR_BLUE}info${COLOR_N}]" @@ -199,7 +200,7 @@ function printprog { function printok { # shellcheck disable=SC1117 - echo -e "\r${OK} $*" + echo -e "${OK} $*" } function printfail { From e8cd7546e1ff6a0b18c9c0c43be46f24319a9f2a Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 18:11:02 +0800 Subject: [PATCH 15/22] Simplify admin credential setup flow - Replace complex 4-option menu with simple email/password prompts - Default values shown in brackets, press Enter to use defaults - Automatic validation and fallback to defaults on invalid input - Clearer flow: just two questions instead of menu navigation - Fix printok function to properly clear progress indicator lines --- xo-install.sh | 117 +++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 50ce1fc..3bcaf4c 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -200,6 +200,8 @@ function printprog { function printok { # shellcheck disable=SC1117 + # Clear the line and print the success message + printf "\r\033[K" echo -e "${OK} $*" } @@ -361,76 +363,57 @@ function SetupCredentialsInteractive { echo "Admin Credential Setup" echo "-----------------------------------------" echo - echo "For security, it's highly recommended to change the default credentials." - echo - echo "Choose an option:" - echo "1. Keep default credentials (admin@admin.net / admin) - NOT RECOMMENDED" - echo "2. Set custom email and password (default user will be deleted)" - echo "3. Set custom email with auto-generated password (default user will be deleted)" - echo "4. Change default password only (keeps admin@admin.net email)" - echo - read -r -p "Option [1-4]: " cred_option - case $cred_option in - 1) - echo - echo -e " ${COLOR_RED}WARNING: Using default credentials is a security risk!${COLOR_N}" - echo -e " ${COLOR_YELLOW}Default username: admin@admin.net password: admin${COLOR_N}" - echo -e " ${COLOR_RED}IMPORTANT: Change these credentials immediately after installation!${COLOR_N}" - ;; - 2) - echo - read -r -p "Enter new admin email: " new_email - # Validate email format - if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then - echo -e "${COLOR_RED}Invalid email format. Using default credentials.${COLOR_N}" - echo -e " ${COLOR_YELLOW}Default username: admin@admin.net password: admin${COLOR_N}" - return - fi - - while true; do - read -r -s -p "Enter new admin password: " new_password - echo - read -r -s -p "Confirm password: " confirm_password - echo - if [[ "$new_password" == "$confirm_password" ]]; then - if [[ ${#new_password} -lt 6 ]]; then - echo -e "${COLOR_RED}Password must be at least 6 characters long${COLOR_N}" - else - break - fi - else - echo -e "${COLOR_RED}Passwords do not match${COLOR_N}" - fi - done - ChangeAdminCredentials "$new_email" "$new_password" "true" - ;; - 3) + # Get email address + read -r -p "Enter admin email [admin@admin.net]: " new_email + if [[ -z "$new_email" ]]; then + new_email="admin@admin.net" + else + # Validate email format + if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo -e "${COLOR_RED}Invalid email format. Using default: admin@admin.net${COLOR_N}" + new_email="admin@admin.net" + fi + fi + + # Get password + read -r -s -p "Enter admin password [admin]: " new_password + echo + if [[ -z "$new_password" ]]; then + new_password="admin" + else + # Validate password length + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters. Using default: admin${COLOR_N}" + new_password="admin" + else + # Confirm password if custom + read -r -s -p "Confirm password: " confirm_password echo - read -r -p "Enter new admin email: " new_email - # Validate email format - if [[ ! "$new_email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then - echo -e "${COLOR_RED}Invalid email format. Using default credentials.${COLOR_N}" - echo -e " ${COLOR_YELLOW}Default username: admin@admin.net password: admin${COLOR_N}" - return + if [[ "$new_password" != "$confirm_password" ]]; then + echo -e "${COLOR_RED}Passwords do not match. Using default: admin${COLOR_N}" + new_password="admin" fi - - local auto_password=$(GenerateSecurePassword) - ChangeAdminCredentials "$new_email" "$auto_password" "true" - ;; - 4) - echo - echo -e " ${COLOR_YELLOW}Generating secure password for admin@admin.net${COLOR_N}" - local auto_password=$(GenerateSecurePassword) - ChangeAdminCredentials "" "$auto_password" "false" - ;; - *) - echo - echo -e " ${COLOR_YELLOW}Invalid option, using default credentials${COLOR_N}" - echo -e " ${COLOR_RED}WARNING: Default username: admin@admin.net password: admin${COLOR_N}" - echo -e " ${COLOR_RED}IMPORTANT: Change these credentials immediately after installation!${COLOR_N}" - ;; - esac + fi + fi + + # Display result and handle credential changes + echo + if [[ "$new_email" == "admin@admin.net" ]] && [[ "$new_password" == "admin" ]]; then + # Both defaults + echo -e " ${COLOR_RED}WARNING: Using default credentials is a security risk!${COLOR_N}" + echo -e " ${COLOR_YELLOW}Username: admin@admin.net${COLOR_N}" + echo -e " ${COLOR_YELLOW}Password: admin${COLOR_N}" + echo -e " ${COLOR_RED}IMPORTANT: Change these credentials immediately after installation!${COLOR_N}" + elif [[ "$new_email" == "admin@admin.net" ]]; then + # Default email, custom password + echo -e " ${COLOR_GREEN}Setting custom password for admin@admin.net${COLOR_N}" + ChangeAdminCredentials "" "$new_password" "false" + else + # Custom email (with custom or default password) + echo -e " ${COLOR_GREEN}Creating new admin user: $new_email${COLOR_N}" + ChangeAdminCredentials "$new_email" "$new_password" "true" + fi } # Standalone credential management function for menu From 273f7e90a02b307652f2f3f64e70b8d055fdae42 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 18:17:48 +0800 Subject: [PATCH 16/22] Fix password validation for custom email addresses - Add retry loop for password entry when using custom email - Require valid password (min 6 chars) for new admin users - Keep prompting until valid password is entered and confirmed - Prevent fallback to default 'admin' password for custom emails - Maintain current behavior for default email (admin@admin.net) --- xo-install.sh | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 3bcaf4c..42cdba8 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -376,23 +376,46 @@ function SetupCredentialsInteractive { fi fi - # Get password - read -r -s -p "Enter admin password [admin]: " new_password - echo - if [[ -z "$new_password" ]]; then - new_password="admin" - else - # Validate password length - if [[ ${#new_password} -lt 6 ]]; then - echo -e "${COLOR_RED}Password must be at least 6 characters. Using default: admin${COLOR_N}" - new_password="admin" - else - # Confirm password if custom + # Get password - if custom email, require valid password + if [[ "$new_email" != "admin@admin.net" ]]; then + # Custom email - must set a valid password + while true; do + read -r -s -p "Enter admin password (min 6 chars): " new_password + echo + + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters long. Please try again.${COLOR_N}" + continue + fi + read -r -s -p "Confirm password: " confirm_password echo + if [[ "$new_password" != "$confirm_password" ]]; then - echo -e "${COLOR_RED}Passwords do not match. Using default: admin${COLOR_N}" + echo -e "${COLOR_RED}Passwords do not match. Please try again.${COLOR_N}" + else + break + fi + done + else + # Default email - allow default password + read -r -s -p "Enter admin password [admin]: " new_password + echo + if [[ -z "$new_password" ]]; then + new_password="admin" + else + # Validate password length + if [[ ${#new_password} -lt 6 ]]; then + echo -e "${COLOR_RED}Password must be at least 6 characters. Using default: admin${COLOR_N}" new_password="admin" + else + # Confirm password if custom + read -r -s -p "Confirm password: " confirm_password + echo + if [[ "$new_password" != "$confirm_password" ]]; then + echo -e "${COLOR_RED}Passwords do not match. Using default: admin${COLOR_N}" + new_password="admin" + fi fi fi fi From 8e23f1402b06cf94d41fffe96e4731e95ae770fc Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 18:28:50 +0800 Subject: [PATCH 17/22] Improve password security by not displaying user-entered passwords - Add show_password parameter to ChangeAdminCredentials function - Only display auto-generated passwords, never user-entered ones - Show "Password set successfully" message for user-provided passwords - Update all callers to pass appropriate show_password flag - Improves security by preventing passwords from being visible on screen --- xo-install.sh | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 42cdba8..d13a706 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -282,6 +282,7 @@ function ChangeAdminCredentials { local new_email="$1" local new_password="$2" local delete_default="${3:-true}" # Delete default user by default + local show_password="${4:-false}" # Only show auto-generated passwords echo printprog "Setting up admin credentials" @@ -323,9 +324,13 @@ function ChangeAdminCredentials { echo echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" - echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" - echo - echo -e " ${COLOR_YELLOW}IMPORTANT: Save these credentials securely!${COLOR_N}" + if [[ "$show_password" == "true" ]]; then + echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + echo + echo -e " ${COLOR_YELLOW}IMPORTANT: Save these credentials securely!${COLOR_N}" + else + echo -e " ${COLOR_GREEN}Password set successfully (not displayed for security)${COLOR_N}" + fi return 0 else printfail "Failed to create new admin user" @@ -344,9 +349,13 @@ function ChangeAdminCredentials { printok "Admin password updated successfully" echo echo -e " ${COLOR_GREEN}Username: admin@admin.net${COLOR_N}" - echo -e " ${COLOR_GREEN}New password: $new_password${COLOR_N}" - echo - echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" + if [[ "$show_password" == "true" ]]; then + echo -e " ${COLOR_GREEN}New password: $new_password${COLOR_N}" + echo + echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" + else + echo -e " ${COLOR_GREEN}Password set successfully (not displayed for security)${COLOR_N}" + fi echo -e " ${COLOR_YELLOW}WARNING: Consider creating a new admin user with a different email for better security${COLOR_N}" return 0 else @@ -431,11 +440,11 @@ function SetupCredentialsInteractive { elif [[ "$new_email" == "admin@admin.net" ]]; then # Default email, custom password echo -e " ${COLOR_GREEN}Setting custom password for admin@admin.net${COLOR_N}" - ChangeAdminCredentials "" "$new_password" "false" + ChangeAdminCredentials "" "$new_password" "false" "false" else # Custom email (with custom or default password) echo -e " ${COLOR_GREEN}Creating new admin user: $new_email${COLOR_N}" - ChangeAdminCredentials "$new_email" "$new_password" "true" + ChangeAdminCredentials "$new_email" "$new_password" "true" "false" fi } @@ -588,8 +597,10 @@ function ManageCredentials { printok "New admin user created successfully" echo echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" - if [[ "$pass_option" != "1" ]]; then + if [[ "$pass_option" == "2" ]]; then echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + else + echo -e " ${COLOR_GREEN}Password set successfully (not displayed for security)${COLOR_N}" fi echo -e " ${COLOR_YELLOW}Note: Previous admin user ($current_email) still exists${COLOR_N}" else From 6ed7298da12120c59b0ecb84dca63fd42c92b552 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 18:32:21 +0800 Subject: [PATCH 18/22] Fix default user deletion logic to handle non-existent users - Check if default admin user exists before attempting deletion - Show success message if user already deleted or never existed - Only show warning if user exists but can't be deleted - Prevents false warnings about missing users --- xo-install.sh | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index d13a706..7b1ea67 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -304,21 +304,29 @@ function ChangeAdminCredentials { # Get the default admin user ID to delete it if [[ "$delete_default" == "true" ]]; then - local default_admin_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: 'admin@admin.net'\" | grep 'id:' | cut -d\"'\" -f2") - - if [[ -n "$default_admin_id" ]]; then - # Re-register with new credentials before deleting old admin - if runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$new_password'" >/dev/null 2>&1; then - if runcmd_stdout "xo-cli user.delete id=$default_admin_id" >/dev/null 2>&1; then - printok "Default admin user (admin@admin.net) deleted for security" + # Check if default admin user exists + local user_list=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null") + if echo "$user_list" | grep -q "email: 'admin@admin.net'"; then + # Default user exists, try to delete it + local default_admin_id=$(echo "$user_list" | grep -B1 -A2 "email: 'admin@admin.net'" | grep 'id:' | cut -d"'" -f2) + + if [[ -n "$default_admin_id" ]]; then + # Re-register with new credentials before deleting old admin + if runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$new_password'" >/dev/null 2>&1; then + if runcmd_stdout "xo-cli user.delete id=$default_admin_id" >/dev/null 2>&1; then + printok "Default admin user (admin@admin.net) deleted for security" + else + printfail "Warning: Could not delete default admin user. Please delete it manually!" + fi else - printfail "Warning: Could not delete default admin user. Please delete it manually!" + printfail "Warning: Could not re-authenticate with new credentials. Default user not deleted." fi else - printfail "Warning: Could not re-authenticate with new credentials. Default user not deleted." + printfail "Warning: Could not extract default admin user ID" fi else - printfail "Warning: Could not find default admin user ID to delete" + # Default user doesn't exist - this is fine + printok "Default admin user already removed or never existed" fi fi From 11c23d6cf61184e581578b260155d73e1af43a8f Mon Sep 17 00:00:00 2001 From: user Date: Fri, 5 Sep 2025 04:20:46 -0700 Subject: [PATCH 19/22] Fix password handling for existing custom email users - Check if custom email user already exists before creation - Update password for existing users instead of failing - Fix exit code capture in user creation command - Remove all debug logging of sensitive information - Handle user creation errors properly with clear messages This fixes the issue where creating a user with an email that already exists would silently fail while reporting success, leaving the old password unchanged. --- xo-install.sh | 183 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 41 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 7b1ea67..8336d8b 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -298,9 +298,50 @@ function ChangeAdminCredentials { # If we're creating a new user (different email), create it first if [[ -n "$new_email" ]] && [[ "$new_email" != "admin@admin.net" ]]; then - # Create new admin user - if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then - printok "New admin user created successfully" + + # Escape single quotes in password for shell command + local escaped_password="${new_password//\'/\'\\\'\'}" + + # First check if the user already exists + local user_list=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null") + + if echo "$user_list" | grep -q "email: '$new_email'"; then + # User exists, update their password + printok "User $new_email already exists, updating password..." + + # Get the user ID + local user_id=$(echo "$user_list" | grep -B1 -A2 "email: '$new_email'" | grep 'id:' | cut -d"'" -f2) + + if [[ -n "$user_id" ]]; then + if runcmd_stdout "xo-cli user.set id='$user_id' password='$escaped_password'" >/dev/null 2>&1; then + printok "Password updated successfully for existing user" + local create_result=0 + else + printfail "Failed to update password for existing user" + return 1 + fi + else + printfail "Could not find user ID for $new_email" + return 1 + fi + else + # User doesn't exist, create them + local create_output=$(xo-cli user.create email="$new_email" password="$escaped_password" permission="admin" 2>&1) + local create_result=$? + + if [[ $create_result -eq 0 ]]; then + printok "New admin user created successfully" + else + printfail "Failed to create new admin user: $create_output" + return 1 + fi + fi + + if [[ $create_result -eq 0 ]]; then + # Verify the new user can authenticate + if ! runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$escaped_password'" >/dev/null 2>&1; then + printfail "Warning: Authentication failed with new credentials" + fi # Get the default admin user ID to delete it if [[ "$delete_default" == "true" ]]; then @@ -340,20 +381,21 @@ function ChangeAdminCredentials { echo -e " ${COLOR_GREEN}Password set successfully (not displayed for security)${COLOR_N}" fi return 0 - else - printfail "Failed to create new admin user" - return 1 fi - elif [[ -n "$new_password" ]]; then + elif [[ -z "$new_email" ]] && [[ -n "$new_password" ]]; then # Just changing password for admin@admin.net - local admin_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: 'admin@admin.net'\" | grep 'id:' | cut -d\"'\" -f2") + printok "Updating password for admin@admin.net..." + + # Get user list and extract admin ID + local user_list=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null") + local admin_id=$(echo "$user_list" | grep -B1 -A2 "email: 'admin@admin.net'" | grep 'id:' | cut -d"'" -f2) if [[ -z "$admin_id" ]]; then printfail "Failed to get admin user ID" return 1 fi - if runcmd_stdout "xo-cli user.set id=$admin_id password='$new_password'" >/dev/null 2>&1; then + if runcmd_stdout "xo-cli user.set id='$admin_id' password='$new_password'" >/dev/null 2>&1; then printok "Admin password updated successfully" echo echo -e " ${COLOR_GREEN}Username: admin@admin.net${COLOR_N}" @@ -370,6 +412,10 @@ function ChangeAdminCredentials { printfail "Failed to update admin password" return 1 fi + else + # This shouldn't happen, but handle it gracefully + printfail "Invalid parameters: email='$new_email' password provided=$([[ -n "$new_password" ]] && echo "yes" || echo "no")" + return 1 fi } @@ -536,31 +582,48 @@ function ManageCredentials { echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" fi - # Create new admin user - if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then - printok "New admin user created successfully" - - # Get current user ID to delete - local current_user_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: '$current_email'\" | grep 'id:' | cut -d\"'\" -f2") - - if [[ -n "$current_user_id" ]]; then - # Re-register with new credentials - if runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$new_password'" >/dev/null 2>&1; then - if runcmd_stdout "xo-cli user.delete id=$current_user_id" >/dev/null 2>&1; then - printok "Previous admin user ($current_email) deleted" - else - printfail "Warning: Could not delete previous admin user" - fi - fi - fi + # Check if user already exists and create/update accordingly + local user_list=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null") + + if echo "$user_list" | grep -q "email: '$new_email'"; then + # User exists, update their password + printok "User $new_email already exists, updating password..." + local user_id=$(echo "$user_list" | grep -B1 -A2 "email: '$new_email'" | grep 'id:' | cut -d"'" -f2) - echo - echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" - if [[ "$pass_option" != "1" ]]; then - echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + if [[ -n "$user_id" ]] && runcmd_stdout "xo-cli user.set id='$user_id' password='$new_password'" >/dev/null 2>&1; then + printok "Password updated successfully for existing user" + else + printfail "Failed to update password for existing user" + return 1 fi else - printfail "Failed to create new admin user" + # Create new admin user + if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then + printok "New admin user created successfully" + else + printfail "Failed to create new admin user" + return 1 + fi + fi + + # Get current user ID to delete + local current_user_id=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null | grep -B1 -A2 \"email: '$current_email'\" | grep 'id:' | cut -d\"'\" -f2") + + if [[ -n "$current_user_id" ]]; then + # Re-register with new credentials + if runcmd_stdout "xo-cli register --allowUnauthorized http://localhost:$PORT '$new_email' '$new_password'" >/dev/null 2>&1; then + if runcmd_stdout "xo-cli user.delete id=$current_user_id" >/dev/null 2>&1; then + printok "Previous admin user ($current_email) deleted" + else + printfail "Warning: Could not delete previous admin user" + fi + fi + fi + + echo + echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" + if [[ "$pass_option" != "1" ]]; then + echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" fi ;; 2) @@ -600,20 +663,38 @@ function ManageCredentials { echo -e " ${COLOR_YELLOW}IMPORTANT: Save this password securely!${COLOR_N}" fi - # Create new admin user without deleting current one - if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then - printok "New admin user created successfully" - echo - echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" - if [[ "$pass_option" == "2" ]]; then - echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + # Check if user already exists and create/update accordingly + local user_list=$(runcmd_stdout "xo-cli user.getAll 2>/dev/null") + + if echo "$user_list" | grep -q "email: '$new_email'"; then + # User exists, update their password + printok "User $new_email already exists, updating password..." + local user_id=$(echo "$user_list" | grep -B1 -A2 "email: '$new_email'" | grep 'id:' | cut -d"'" -f2) + + if [[ -n "$user_id" ]] && runcmd_stdout "xo-cli user.set id='$user_id' password='$new_password'" >/dev/null 2>&1; then + printok "Password updated successfully for existing user" else - echo -e " ${COLOR_GREEN}Password set successfully (not displayed for security)${COLOR_N}" + printfail "Failed to update password for existing user" + return 1 fi - echo -e " ${COLOR_YELLOW}Note: Previous admin user ($current_email) still exists${COLOR_N}" else - printfail "Failed to create new admin user" + # Create new admin user without deleting current one + if runcmd_stdout "xo-cli user.create email='$new_email' password='$new_password' permission='admin'" >/dev/null 2>&1; then + printok "New admin user created successfully" + else + printfail "Failed to create new admin user" + return 1 + fi fi + + echo + echo -e " ${COLOR_GREEN}New admin username: $new_email${COLOR_N}" + if [[ "$pass_option" == "2" ]]; then + echo -e " ${COLOR_GREEN}New admin password: $new_password${COLOR_N}" + else + echo -e " ${COLOR_GREEN}Password set successfully (not displayed for security)${COLOR_N}" + fi + echo -e " ${COLOR_YELLOW}Note: Previous admin user ($current_email) still exists${COLOR_N}" ;; 3) echo @@ -2110,6 +2191,26 @@ function UninstallXO { runcmd "rm -rf \"$CONFIGPATH/.cache/xo-server\"" printok "Removed XO server cache" fi + + # Clear Valkey/Redis data for XO + if command -v valkey-cli &>/dev/null || command -v redis-cli &>/dev/null; then + local redis_cmd="valkey-cli" + if ! command -v valkey-cli &>/dev/null; then + redis_cmd="redis-cli" + fi + + printprog "Clearing XO data from Valkey/Redis" + # Get all XO-related keys and delete them + local xo_keys=$($redis_cmd --scan --pattern "xo:*" 2>/dev/null || true) + if [[ -n "$xo_keys" ]]; then + echo "$xo_keys" | while read -r key; do + $redis_cmd DEL "$key" &>/dev/null || true + done + printok "Cleared XO data from Valkey/Redis" + else + printinfo "No XO data found in Valkey/Redis" + fi + fi else printinfo "Deletion cancelled - preserving configuration and data files" fi From 7f41f430019cbbe27365d0ff39705db0594810c1 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 5 Sep 2025 19:48:10 +0800 Subject: [PATCH 20/22] Remove README-FEDORA.md --- README-FEDORA.md | 116 ----------------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 README-FEDORA.md diff --git a/README-FEDORA.md b/README-FEDORA.md deleted file mode 100644 index c17c920..0000000 --- a/README-FEDORA.md +++ /dev/null @@ -1,116 +0,0 @@ -# Fedora Support for XenOrchestraInstallerUpdater - -**⚠️ IMPORTANT: Xen Orchestra does not officially support Fedora. This is an experimental implementation.** - -This fork adds experimental support for Fedora 41, 42, 43, and rawhide to the XenOrchestraInstallerUpdater script. - -## Changes Made - -### 1. Added Fedora to Supported Operating Systems -- Modified OS detection to recognize Fedora -- Added version checks for Fedora 41, 42, 43, and rawhide -- Special handling for Fedora Rawhide (rolling release) - -### 2. Fixed Package Management for Fedora -- **Valkey vs Redis**: Correctly detects that Fedora 41+ uses valkey instead of redis -- **EPEL Repository**: Skips EPEL installation as it's not needed on Fedora -- **libvhdi**: Automatically installs from reversejames/libvhdi COPR repository for Fedora - -### 3. Tested Configurations -- Fedora 42 (current stable) -- Fedora rawhide (development version) - -## Installation on Fedora - -### Prerequisites - -```bash -# Ensure system is up to date -sudo dnf update -y - -# Install git if not present -sudo dnf install -y git -``` - -### Using the Modified Script - -```bash -# Clone this fork -git clone https://github.com/YOUR_USERNAME/XenOrchestraInstallerUpdater -cd XenOrchestraInstallerUpdater - -# Run installation -sudo bash xo-install.sh -``` - -### Manual Installation Steps - -If you prefer to run the installation with specific options: - -```bash -# Fresh installation -sudo bash xo-install.sh --install - -# Update existing installation -sudo bash xo-install.sh --update - -# Rollback to previous version -sudo bash xo-install.sh --rollback -``` - -## Configuration - -The script uses the same configuration file (`xo-install.cfg`) as the original. - -### Fedora-Specific Notes - -1. **Yarn Installation**: The script installs Fedora's native `yarnpkg` package instead of using external repositories. This follows Fedora packaging best practices and avoids third-party dependencies. - -2. **Node.js**: Fedora includes recent Node.js versions in the base repository, which should match XO requirements. - -3. **Package Differences**: Unlike RHEL-based systems which use Yarn's official repository, Fedora uses its native `yarnpkg` package to follow distribution packaging guidelines. - -3. **libvhdi**: Automatically installed from the reversejames/libvhdi COPR repository. This provides full VHD operation support. - -## Known Limitations - -- **Unofficial Support**: Xen Orchestra does not officially support Fedora -- **Experimental Status**: This installation may encounter unexpected issues -- libvhdi-tools is installed from reversejames/libvhdi COPR repository -- Uses Fedora's `yarnpkg` package instead of upstream yarn - -## Future Improvements - -- [x] COPR repository for libvhdi on Fedora (reversejames/libvhdi) -- [ ] Add support for more Fedora versions as they're released -- [ ] Improve rawhide handling for better stability - -## Testing Status - -| Fedora Version | Status | Notes | -|---------------|--------|-------| -| Fedora 41 | ✅ Supported | Uses valkey, libvhdi from COPR | -| Fedora 42 | ✅ Tested | Current stable, uses valkey, libvhdi from COPR | -| Fedora 43 | ✅ Supported | Uses valkey, libvhdi from COPR | -| Fedora Rawhide | ✅ Supported | Development version, uses valkey, libvhdi from COPR | - -## Contributing - -To contribute to Fedora support: - -1. Fork this repository -2. Test on your Fedora version -3. Submit issues for any problems -4. Create pull requests with fixes - -## Pull Request Status - -- [ ] PR submitted to upstream repository -- [ ] Waiting for review -- [ ] Merged - -## Contact - -For issues specific to Fedora support, please open an issue in this fork. - -For general XenOrchestraInstallerUpdater issues, see the [original repository](https://github.com/ronivay/XenOrchestraInstallerUpdater). \ No newline at end of file From 72c2c293ebc91516ba66600e738f6e21f8320edf Mon Sep 17 00:00:00 2001 From: user Date: Mon, 16 Feb 2026 15:55:46 +1100 Subject: [PATCH 21/22] Fix INCLUDE_V6 unbound variable and sync with upstream - Add INCLUDE_V6 default initialization to fix unbound variable crash - Guard v6 symlink creation with INCLUDE_V6 check - Add INCLUDE_V6 option to sample config - Upgrade NODEVERSION from 22 to 24 (upstream 186218f) - Drop EOL OS support: Debian 10, Ubuntu 20, CentOS 8 (upstream 3a30b75) - Add yarn repository key refresh for deb systems (upstream 40bd192) --- sample.xo-install.cfg | 5 +++++ xo-install.sh | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sample.xo-install.cfg b/sample.xo-install.cfg index 627b865..9c75694 100644 --- a/sample.xo-install.cfg +++ b/sample.xo-install.cfg @@ -138,3 +138,8 @@ PRESERVE="3" # Configurable network-timeout setting for yarn, in ms. # default: 300000 = 300sec #YARN_NETWORK_TIMEOUT="300000" + +# Build and include the v6 web interface (@xen-orchestra/web) +# options: true/false +# default: false +#INCLUDE_V6="false" diff --git a/xo-install.sh b/xo-install.sh index 8336d8b..4d0b938 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -53,6 +53,7 @@ INSTALL_REPOS="${INSTALL_REPOS:-"true"}" SYSLOG_TARGET="${SYSLOG_TARGET:-""}" YARN_CACHE_CLEANUP="${YARN_CACHE_CLEANUP:-"false"}" YARN_NETWORK_TIMEOUT="${YARN_NETWORK_TIMEOUT:-"300000"}" +INCLUDE_V6="${INCLUDE_V6:-"false"}" # set variables not changeable in configfile TIME=$(date +%Y%m%d%H%M) @@ -1555,8 +1556,10 @@ function InstallXO { runcmd "ln -sfn $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server $INSTALLDIR/xo-server" printinfo "Symlinking fresh xo-web install/update to $INSTALLDIR/xo-web" runcmd "ln -sfn $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-web $INSTALLDIR/xo-web" - printinfo "Symlinking fresh xo-web-v6 install/update to $INSTALLDIR/xo-web-v6" - runcmd "ln -sfn $INSTALLDIR/xo-builds/xen-orchestra-$TIME/@xen-orchestra/web $INSTALLDIR/xo-web-v6" + if [ "$INCLUDE_V6" == "true" ]; then + printinfo "Symlinking fresh xo-web-v6 install/update to $INSTALLDIR/xo-web-v6" + runcmd "ln -sfn $INSTALLDIR/xo-builds/xen-orchestra-$TIME/@xen-orchestra/web $INSTALLDIR/xo-web-v6" + fi printinfo "Symlinking fresh xo-cli install/update to $INSTALLDIR/xo-cli" runcmd "ln -sfn $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-cli $INSTALLDIR/xo-cli" printinfo "Symlinking xo-cli script to /usr/local/bin/xo-cli" From a604303f04bcf84a38ac15f7ab072576aef6bb43 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 16 Feb 2026 16:15:57 +1100 Subject: [PATCH 22/22] Handle upstream config template rename and fix default web UI Upstream xen-orchestra renamed sample.config.toml to config.toml, causing all installer config generation to silently fail. This also left the default web UI pointing to the unbuilt v6 interface. - Detect config template filename (sample.config.toml or config.toml) - Use $CONFIG_TEMPLATE variable instead of hardcoded filename - Set '/' mount to v5 xo-web when INCLUDE_V6 is not enabled --- xo-install.sh | 52 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/xo-install.sh b/xo-install.sh index 4d0b938..53fc1e0 100755 --- a/xo-install.sh +++ b/xo-install.sh @@ -1486,57 +1486,75 @@ function InstallXO { # if xen orchestra configuration file doesn't exist or configuration update is not disabled in xo-install.cfg, we create it if [[ ! -f "$CONFIGPATH/.config/xo-server/config.toml" ]] || [[ "$CONFIGUPDATE" == "true" ]]; then + # Upstream renamed sample.config.toml to config.toml, handle both + local CONFIG_TEMPLATE="$INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + if [[ ! -f "$CONFIG_TEMPLATE" ]]; then + CONFIG_TEMPLATE="$INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/config.toml" + fi + + if [[ ! -f "$CONFIG_TEMPLATE" ]]; then + printfail "No xo-server configuration template found" + exit 1 + fi + echo printinfo "Changing redis connection address in xo-server configuration file" - runcmd "sed -i \"s%#uri = 'redis://redis.company.lan/42'%uri = 'redis://127.0.0.1:6379/0'%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%#uri = 'redis://redis.company.lan/42'%uri = 'redis://127.0.0.1:6379/0'%\" $CONFIG_TEMPLATE" if [[ "$PORT" != "80" ]]; then printinfo "Changing port in xo-server configuration file" # shellcheck disable=SC1117 - runcmd "sed -i \"s/port = 80/port = $PORT/\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s/port = 80/port = $PORT/\" $CONFIG_TEMPLATE" sleep 2 fi if [[ -n "$LISTEN_ADDRESS" ]]; then printinfo "Changing listen address in xo-server configuration file" - runcmd "sed -i \"s%^# hostname = 'localhost'%hostname = '$LISTEN_ADDRESS'%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%^# hostname = 'localhost'%hostname = '$LISTEN_ADDRESS'%\" $CONFIG_TEMPLATE" fi if [[ "$HTTPS" == "true" ]]; then printinfo "Enabling HTTPS in xo-server configuration file" # shellcheck disable=SC1117 - runcmd "sed -i \"s%# cert = '.\/certificate.pem'%cert = '$PATH_TO_HTTPS_CERT'%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%# cert = '.\/certificate.pem'%cert = '$PATH_TO_HTTPS_CERT'%\" $CONFIG_TEMPLATE" # shellcheck disable=SC1117 - runcmd "sed -i \"s%# key = '.\/key.pem'%key = '$PATH_TO_HTTPS_KEY'%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%# key = '.\/key.pem'%key = '$PATH_TO_HTTPS_KEY'%\" $CONFIG_TEMPLATE" if [[ "$AUTOCERT" == "true" ]]; then # shellcheck disable=SC1117 - runcmd "sed -i \"s%# autoCert = false%autoCert = true%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%# autoCert = false%autoCert = true%\" $CONFIG_TEMPLATE" fi if [[ "$ACME" == "true" ]]; then - runcmd "sed -i \"s%# \[\[http.listen\]\]%\[\[http.listen\]\]%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" - runcmd "sed -i \"s%# port = 443%port = 443%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" - runcmd "sed -i \"s%^# redirectToHttps = true%redirectToHttps = true%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" - runcmd "sed -i \"/^autoCert =.*/a acmeCa = '$ACME_CA'\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" - runcmd "sed -i \"/^autoCert = .*/a acmeDomain = '$ACME_DOMAIN'\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%# \[\[http.listen\]\]%\[\[http.listen\]\]%\" $CONFIG_TEMPLATE" + runcmd "sed -i \"s%# port = 443%port = 443%\" $CONFIG_TEMPLATE" + runcmd "sed -i \"s%^# redirectToHttps = true%redirectToHttps = true%\" $CONFIG_TEMPLATE" + runcmd "sed -i \"/^autoCert =.*/a acmeCa = '$ACME_CA'\" $CONFIG_TEMPLATE" + runcmd "sed -i \"/^autoCert = .*/a acmeDomain = '$ACME_DOMAIN'\" $CONFIG_TEMPLATE" if [[ -n "$ACME_EMAIL" ]]; then - runcmd "sed -i \"/^autoCert =.*/a acmeEmail = '$ACME_EMAIL'\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"/^autoCert =.*/a acmeEmail = '$ACME_EMAIL'\" $CONFIG_TEMPLATE" fi fi sleep 2 fi if [[ "$USESUDO" == "true" ]] && [[ "$XOUSER" != "root" ]]; then printinfo "Enabling useSudo in xo-server configuration file" - runcmd "sed -i \"s/#useSudo = false/useSudo = true/\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s/#useSudo = false/useSudo = true/\" $CONFIG_TEMPLATE" printinfo "Changing default mountsDir in xo-server configuration file" - runcmd "sed -i \"s%#mountsDir.*%mountsDir = '$INSTALLDIR/mounts'%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%#mountsDir.*%mountsDir = '$INSTALLDIR/mounts'%\" $CONFIG_TEMPLATE" runcmd "mkdir -p $INSTALLDIR/mounts" runcmd "chown $XOUSER:$XOUSER $INSTALLDIR/mounts" fi if [[ -n "$SYSLOG_TARGET" ]]; then printinfo "Enabling remote syslog in xo-server configuration file" - runcmd "sed -i \"s%#\[logs.transport.syslog\]%\[logs.transport.syslog\]%\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" - runcmd "sed -i \"/^\[logs.transport.syslog.*/a target = '$SYSLOG_TARGET'\" $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml" + runcmd "sed -i \"s%#\[logs.transport.syslog\]%\[logs.transport.syslog\]%\" $CONFIG_TEMPLATE" + runcmd "sed -i \"/^\[logs.transport.syslog.*/a target = '$SYSLOG_TARGET'\" $CONFIG_TEMPLATE" + fi + + # xo-server defaults / to the v6 web UI which requires a separate build. + # Point / to the v5 web UI unless INCLUDE_V6 is enabled. + if [[ "$INCLUDE_V6" != "true" ]]; then + printinfo "Setting default web UI to v5 (xo-web)" + runcmd "sed -i \"s%# '/' = '../xo-web/dist/'%'/' = '$INSTALLDIR/xo-web/dist/'%\" $CONFIG_TEMPLATE" fi printinfo "Activating modified configuration file" @@ -1546,7 +1564,7 @@ function InstallXO { runcmd "install -o $XOUSER -g $XOUSER -m 770 -d $CONFIGPATH/.config" fi runcmd "mkdir -p $CONFIGPATH/.config/xo-server" - runcmd "mv -f $INSTALLDIR/xo-builds/xen-orchestra-$TIME/packages/xo-server/sample.config.toml $CONFIGPATH/.config/xo-server/config.toml" + runcmd "mv -f $CONFIG_TEMPLATE $CONFIGPATH/.config/xo-server/config.toml" fi