diff --git a/README.md b/README.md index cd7e47e..45d0310 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,21 @@ To update an installed main-branch copy, switch it to V2, and immediately load the V2 menu: tapm V2 + +To change the TA-ProxMenu `origin` URL on every installed node in the current +Proxmox cluster, either select `G` in the legacy menu or run: + + tapm git-url + +The command prompts for the new repository URL, discovers cluster nodes with +`pvecm`, verifies that each node can read the new repository's `main` branch, +and only then changes that node's `origin`. A URL can also be supplied: + + tapm git-url https://git.example.com/organization/TA-ProxMenu.git + +Add `--yes` as the third argument for a non-interactive confirmation: + + tapm git-url https://git.example.com/organization/TA-ProxMenu.git --yes + +Private repositories require read credentials to be configured on every node +before migration. Repository credentials must not be embedded in the URL. diff --git a/defaults.inc b/defaults.inc index 70652dd..7d0580c 100755 --- a/defaults.inc +++ b/defaults.inc @@ -3,7 +3,7 @@ action="$1" FOLDER='/opt/idssys/ta-proxmenu' -VERS='2026.7.25-4' +VERS='2026.7.28-1' noupdate=' ' @@ -32,4 +32,3 @@ VIRTIO_FILE=${VIRTIO_DOWNLOAD_URL##*/} # apt-get update # fi - diff --git a/proxmenu-scripts.sh b/proxmenu-scripts.sh index 28dcf78..df3ccf3 100755 --- a/proxmenu-scripts.sh +++ b/proxmenu-scripts.sh @@ -372,6 +372,7 @@ MAIN_MENU() { echo -e "${idsCL[White]} [${idsCL[LightYellow]}R${idsCL[Default]}] ${idsCL[White]}Restart Proxmox Services${idsCL[Default]}" echo echo -e "${idsCL[White]} [${idsCL[LightYellow]}K${idsCL[Default]}] ${idsCL[White]}Install Keepalive on all Hosts${idsCL[Default]}" + echo -e "${idsCL[White]} [${idsCL[LightYellow]}G${idsCL[Default]}] ${idsCL[White]}Change TA-ProxMenu Git URL on all Hosts${idsCL[Default]}" echo echo -e "${idsCL[White]} [${idsCL[LightYellow]}Q${idsCL[Default]}] ${idsCL[White]}Quit${idsCL[Default]}" echo @@ -394,6 +395,7 @@ MAIN_MENU() { [Mm]) MAINTENANCE_MODE;; [Rr]) RESTART_PVE_SERVICES;; [Kk]) INSTALL_KEEPALIVE;; + [Gg]) exec /opt/idssys/ta-proxmenu/run.sh git-url;; [Qq]) EXIT1; exit 0;; *) echo -e "Thats an invaild option,\nplease select a valid option only."; sleep 1;; esac @@ -414,6 +416,7 @@ if [ ${action-x} ]; then cpu) DETECT_CPU;; mm) MAINTENANCE_MODE;; timeout) SET_VM_SHUTDOWNTIMEOUT ${2};; + git-url) exec /opt/idssys/ta-proxmenu/run.sh git-url "${2:-}";; *) MAIN_MENU;; esac else @@ -424,4 +427,3 @@ exit 0 - diff --git a/run.sh b/run.sh index 0910f91..1fc3b46 100755 --- a/run.sh +++ b/run.sh @@ -5,6 +5,116 @@ source /opt/idssys/defaults/default.inc source /opt/idssys/ta-proxmenu/defaults.inc +VALID_GIT_URL() { + [[ "$1" =~ ^https?://[A-Za-z0-9._-]+(:[0-9]+)?/[A-Za-z0-9._/-]+$ ]] +} + +SWITCH_GIT_URL() { + local repository='/opt/idssys/ta-proxmenu' + local new_url="${1:-}" + local assume_yes="${2:-}" + local local_hostname + local local_short_hostname + local node + local node_output + local successful=0 + local failed=0 + local -a cluster_nodes=() + + if [[ ! -d "${repository}/.git" ]]; then + echo -e "${idsCL[Red]}TA-ProxMenu is not a Git repository.${idsCL[Default]}" + return 1 + fi + + if [[ -z "$new_url" ]]; then + echo -en "${idsCL[LightCyan]}New TA-ProxMenu Git URL: ${idsCL[Default]}" + read -r -e new_url + fi + if ! VALID_GIT_URL "$new_url"; then + echo -e "${idsCL[Red]}Enter a complete HTTP or HTTPS repository URL without embedded credentials.${idsCL[Default]}" + echo -e "${idsCL[LightYellow]}Example: https://git.example.com/organization/TA-ProxMenu.git${idsCL[Default]}" + return 1 + fi + + if command -v pvecm >/dev/null 2>&1; then + mapfile -t cluster_nodes < <( + pvecm nodes 2>/dev/null | + awk ' + $1 ~ /^(0x)?[[:xdigit:]]+$/ && + $2 ~ /^[0-9]+$/ && + $3 != "" { print $3 } + ' + ) + fi + if (( ${#cluster_nodes[@]} == 0 )); then + cluster_nodes=("$(hostname -s)") + fi + + echo + echo -e "${idsCL[LightCyan]}Repository URL:${idsCL[Default]} $new_url" + echo -e "${idsCL[LightCyan]}Target nodes:${idsCL[Default]} ${cluster_nodes[*]}" + if [[ "$assume_yes" != "--yes" ]]; then + echo -en "${idsCL[LightYellow]}Change origin on every listed node (y/N)? ${idsCL[Default]}" + read -r -n 1 choice + echo + [[ "$choice" == [Yy] ]] || { + echo -e "${idsCL[LightYellow]}Git URL migration cancelled.${idsCL[Default]}" + return 1 + } + fi + + local_hostname="$(hostname)" + local_short_hostname="$(hostname -s)" + for node in "${cluster_nodes[@]}"; do + echo -en "${idsCL[LightCyan]}${node}:${idsCL[Default]} " + if [[ "$node" == "$local_hostname" || "$node" == "$local_short_hostname" ]]; then + if ! GIT_TERMINAL_PROMPT=0 timeout 20 \ + git -C "$repository" ls-remote --exit-code \ + "$new_url" refs/heads/main >/dev/null 2>&1; then + echo -e "${idsCL[Red]}new repository is unavailable or requires credentials${idsCL[Default]}" + ((failed += 1)) + continue + fi + if git -C "$repository" remote set-url origin "$new_url"; then + echo -e "${idsCL[Green]}origin updated${idsCL[Default]}" + ((successful += 1)) + else + echo -e "${idsCL[Red]}could not update origin${idsCL[Default]}" + ((failed += 1)) + fi + continue + fi + + node_output="$( + ssh \ + -o BatchMode=yes \ + -o ConnectTimeout=10 \ + "root@${node}" \ + "test -d '${repository}/.git' && + GIT_TERMINAL_PROMPT=0 timeout 20 git -C '${repository}' ls-remote --exit-code '${new_url}' refs/heads/main >/dev/null 2>&1 && + git -C '${repository}' remote set-url origin '${new_url}'" \ + 2>&1 + )" + if [[ $? -eq 0 ]]; then + echo -e "${idsCL[Green]}origin updated${idsCL[Default]}" + ((successful += 1)) + else + echo -e "${idsCL[Red]}failed${idsCL[Default]}" + [[ -n "$node_output" ]] && echo " $node_output" + ((failed += 1)) + fi + done + + echo + echo -e "${idsCL[Green]}Updated nodes: ${successful}${idsCL[Default]}" + if ((failed > 0)); then + echo -e "${idsCL[Red]}Failed nodes: ${failed}${idsCL[Default]}" + echo -e "${idsCL[LightYellow]}A failed node was left on its existing origin URL.${idsCL[Default]}" + return 1 + fi + echo -e "${idsCL[Green]}Every installed cluster node now uses the new Git URL.${idsCL[Default]}" +} + SWITCH_TO_V2() { local repository='/opt/idssys/ta-proxmenu' local current_branch @@ -86,8 +196,14 @@ if [[ "${1:-}" == "V2" ]]; then exit $? fi +if [[ "${1:-}" == "git-url" ]]; then + SWITCH_GIT_URL "${2:-}" "${3:-}" + exit $? +fi + if [[ "${noupdate}" != *" ${1} "* ]] && [[ "${noupdate}" != *" ${2} "* ]]; then - if curl -m 3 -s --head --request GET https://git.scity.us | grep "HTTP/2 200" > /dev/null; then + if GIT_TERMINAL_PROMPT=0 timeout 15 git -C /opt/idssys/ta-proxmenu \ + ls-remote --exit-code origin refs/heads/main >/dev/null 2>&1; then if [ "${1}" != "tapm" ]; then echo -en "${idsCL[LightCyan]}Checking for updates...${idsCL[Default]}" echo "" @@ -140,7 +256,7 @@ if [[ "${noupdate}" != *" ${1} "* ]] && [[ "${noupdate}" != *" ${2} "* ]]; then fi else - echo -e "${idsCL[Red]}Could not connect to 'git.scity.us' for updates${idsCL[Default]}" + echo -e "${idsCL[Red]}Could not connect to the configured TA-ProxMenu Git origin for updates${idsCL[Default]}" fi fi