diff --git a/webmin-setup-repo.sh b/webmin-setup-repo.sh index 46a3bfe33..dc0868d5d 100644 --- a/webmin-setup-repo.sh +++ b/webmin-setup-repo.sh @@ -1,18 +1,37 @@ #!/bin/sh # shellcheck disable=SC1090 disable=SC2059 disable=SC2164 disable=SC2181 disable=SC2317 # webmin-setup-repo.sh -# Sets up a production or testing repository for Webmin and Usermin packages -# on Debian-based and RPM-based systems +# Sets up a stable, prerelease, or unstable repository to provide Webmin and +# Usermin packages on Debian-based and RPM-based systems + +# Default values that can be overridden +repo_host="download.webmin.com" +repo_download="https://$repo_host" +repo_download_prerelease="https://rc.download.webmin.dev" +repo_download_unstable="https://download.webmin.dev" +repo_key="developers-key.asc" +repo_key_download="$repo_download/$repo_key" +repo_key_suffix="webmin-developers" +repo_name="webmin-stable" +repo_name_prerelease="webmin-prerelease" +repo_name_unstable="webmin-unstable" +repo_component="main" +repo_dist="stable" +repo_section="contrib" +repo_description="Webmin Releases" +repo_description_prerelease="Webmin Prerelease" +repo_description_unstable="Webmin Development Builds" +install_check_binary="/usr/bin/webmin" +install_message="Webmin and Usermin can be installed with:" +install_packages="webmin usermin" +repo_auth_user="" +repo_auth_pass="" + +# Repository mode (stable, prerelease, unstable) +repo_mode="stable" -webmin_host="download.webmin.com" -webmin_download="https://$webmin_host" -webmin_download_testing="https://download.webmin.dev" -webmin_key="developers-key.asc" -webmin_key_download="$webmin_download/$webmin_key" -webmin_key_suffix="webmin-developers" download_curl="/usr/bin/curl" download="$download_curl -f -s -L -O" -testing_mode=0 force_setup=0 # Colors @@ -23,27 +42,154 @@ BOLD="$(tput bold 2>/dev/null || echo '')" ITALIC="$(tput sitm 2>/dev/null || echo '')" usage() { - echo "${RED}Error:${NORMAL} Unknown or invalid argument." - echo "Usage: $0 [-t|--testing] [-f|--force] [-h|--help]" - exit "$1" + if [ -n "${1-}" ]; then + echo "${RED}Error:${NORMAL} Unknown or invalid argument: $1" + fi + echo + cat << EOF +Usage: $(basename "$0") [OPTIONS] + +General options: + -f, --force Force setup without confirmation + -h, --help Display this help message + +Repository types: + --stable Set up the stable repo, built with extra testing + --prerelease Set up the prerelease repo built from latest tag + --unstable Set up unstable repo built from the latest commit + +Repository configuration: + --host= Main repository host + --prerelease-host= Prerelease repository host + --unstable-host= Unstable repository host + --key= Repository signing key file + --key-suffix= Repository key suffix for file naming + --auth-user= Repository authentication username + --auth-pass= Repository authentication password + +Repository metadata: + --name= Base name for repository (default: webmin) + --description= Description for repository (default: Webmin) + --component= Repository component (default: main) + --section= Repository section (default: contrib) + --dist= Distribution name (default: stable) + +Post-installation options: + --check-binary= Binary to check in post-install + --install-message= Message to show in post-install if binary not found + --install-packages= Packages to suggest for installation + +EOF + exit 1 +} + +post_status() { + status="$1" + message="$2" + if [ "$status" -eq 0 ]; then + echo " .. done" + else + if [ -n "$message" ]; then + echo " .. failed : $message" + else + echo " .. failed" + fi + exit "$status" + fi } process_args() { for arg in "$@"; do case "$arg" in - -t|--testing) testing_mode=1 ;; + --stable) repo_mode="stable" ;; + --prerelease|--rc) repo_mode="prerelease" ;; + --unstable|--testing|-t) repo_mode="unstable" ;; -f|--force) force_setup=1 ;; - -h|--help) - echo "Usage: $0 [-t|--testing] [-f|--force] [-h|--help]" - exit 0 + -h|--help) usage ;; + --host=*) + repo_host="${arg#*=}" + repo_download="https://$repo_host" + repo_key_download="$repo_download/$repo_key" + ;; + --prerelease-host=*) + repo_download_prerelease="https://${arg#*=}" + ;; + --unstable-host=*) + repo_download_unstable="https://${arg#*=}" + ;; + --key=*) + repo_key="${arg#*=}" + repo_key_download="$repo_download/$repo_key" + ;; + --key-suffix=*) + repo_key_suffix="${arg#*=}" + ;; + --auth-user=*) + repo_auth_user="${arg#*=}" + ;; + --auth-pass=*) + repo_auth_pass="${arg#*=}" + ;; + --name=*) + base_name="${arg#*=}" + repo_name="$base_name" + repo_name_prerelease="${base_name}-prerelease" + repo_name_unstable="${base_name}-unstable" + ;; + --description=*) + base_description="${arg#*=}" + repo_description="$base_description Releases" + repo_description_prerelease="${base_description} Prerelease" + repo_description_unstable="${base_description} Development Builds" + ;; + --component=*) + repo_component="${arg#*=}" + ;; + --section=*) + repo_section="${arg#*=}" + ;; + --dist=*) + repo_dist="${arg#*=}" + ;; + --check-binary=*) + install_check_binary="${arg#*=}" + ;; + --install-message=*) + install_message="${arg#*=}" + ;; + --install-packages=*) + install_packages="${arg#*=}" ;; *) - echo "${RED}Error:${NORMAL} Unknown argument: $arg" - echo "Usage: $0 [-t|--testing] [-f|--force] [-h|--help]" - exit 1 + usage "$arg" ;; esac done + + # Set active repo variables based on mode + case "$repo_mode" in + prerelease) + active_repo_name="$repo_name_prerelease" + active_repo_description="$repo_description_prerelease" + active_repo_download="$repo_download_prerelease" + if [ "$repo_dist" = "stable" ]; then + repo_dist="webmin" + fi + ;; + unstable) + active_repo_name="$repo_name_unstable" + active_repo_description="$repo_description_unstable" + active_repo_download="$repo_download_unstable" + if [ "$repo_dist" = "stable" ]; then + repo_dist="webmin" + fi + ;; + *) + active_repo_name="$repo_name" + active_repo_description="$repo_description" + active_repo_download="$repo_download" + ;; + esac } check_permission() { @@ -99,16 +245,19 @@ detect_os() { install_cmd="dnf install" install="$install_cmd -y" clean="dnf clean all" + update="dnf makecache" else install_cmd="yum install" install="$install_cmd -y" clean="yum clean all" + update="yum makecache" fi elif [ -n "$osid_suse_like" ]; then package_type=rpm install_cmd="zypper install" install="$install_cmd -y" clean="zypper clean" + update="zypper refresh" repo_extra_opts="autorefresh=1" else echo "${RED}Error:${NORMAL} Unknown OS : $osid" @@ -118,29 +267,33 @@ detect_os() { set_os_variables() { # Debian-based - debian_repo_file="/etc/apt/sources.list.d/webmin.list" - debian_repo_file_testing="/etc/apt/sources.list.d/webmin-testing.list" + debian_repo_file="/etc/apt/sources.list.d/$active_repo_name.list" # RPM-based rpm_repo_dir="/etc/yum.repos.d" if [ -n "$osid_suse_like" ]; then rpm_repo_dir="/etc/zypp/repos.d" fi - rpm_repo_file="$rpm_repo_dir/webmin.repo" - rpm_repo_file_testing="$rpm_repo_dir/webmin-testing.repo" + rpm_repo_file="$rpm_repo_dir/$active_repo_name.repo" } ask_confirmation() { + repo_desc_formatted=$(echo "$active_repo_description" | \ + sed 's/\([^ ]*\)\(.*\)/\1\L\2/') + case "$repo_mode" in + prerelease) + printf \ +"\e[47;1;31;82mPrerelease builds are automated from the latest tagged release\e[0m\n" + ;; + unstable) + printf \ +"\e[47;1;31;82mUnstable builds are automated experimental versions designed for\e[0m\n" + printf \ +"\e[47;1;31;82mdevelopment, often containing critical bugs and breaking changes\e[0m\n" + ;; + esac if [ "$force_setup" != "1" ]; then - if [ "$testing_mode" = "1" ]; then - printf \ -"\e[47;1;31;82mRolling builds are experimental and unstable versions used for testing\e[0m\n" - printf \ -"\e[47;1;31;82mand development purposes, may have critical bugs and breaking changes!\e[0m\n" - printf "Setup Webmin testing repository? (y/N) " - else - printf "Setup Webmin repository? (y/N) " - fi + printf "Setup ${repo_desc_formatted} repository? (y/N) " read -r sslyn if [ "$sslyn" != "y" ] && [ "$sslyn" != "Y" ]; then exit 0 @@ -156,13 +309,8 @@ check_downloader() { download="/usr/bin/fetch" else echo " Installing required ${ITALIC}curl${NORMAL} from OS repos .." - $install curl 1>/dev/null 2>&1 - if [ $? -ne 0 ]; then - echo " .. failed to install 'wget'!" - exit 1 - else - echo " .. done" - fi + install_output=$($install curl 2>&1) + post_status $? "$(echo "$install_output" | tr '\n' ' ')" fi fi } @@ -170,89 +318,106 @@ check_downloader() { check_gpg() { if [ -n "$osid_debian_like" ]; then if [ ! -x /usr/bin/gpg ]; then + echo " Installing required ${ITALIC}gnupg${NORMAL} from OS repos .." $update 1>/dev/null 2>&1 - $install gnupg 1>/dev/null 2>&1 + install_output=$($install gnupg 2>&1) + post_status $? "$(echo "$install_output" | tr '\n' ' ')" fi fi } download_key() { - rm -f "/tmp/$webmin_key" - echo " Downloading Webmin key .." - download_out=$($download "$webmin_key_download" 2>&1) - if [ $? -ne 0 ]; then - download_out=$(echo "$download_out" | tr '\n' ' ') - echo " ..failed : $download_out" - exit 1 - fi - echo " .. done" + rm -f "/tmp/$repo_key" + echo " Downloading Webmin developers key .." + download_out=$($download "$repo_key_download" 2>&1) + post_status $? "$(echo "$download_out" | tr '\n' ' ')" } setup_repos() { + repo_desc_formatted=$(echo "$active_repo_description" | \ + sed 's/\([^ ]*\)\(.*\)/\1\L\2/') + + # Construct auth URL if credentials provided + repo_auth_url="$active_repo_download" + if [ -n "$repo_auth_user" ] && [ -n "$repo_auth_pass" ]; then + protocol="${repo_auth_url%%://*}" + rest="${repo_auth_url#*://}" + repo_auth_url="${protocol}://$repo_auth_user:$repo_auth_pass@$rest" + fi + case "$package_type" in rpm) - echo " Installing Webmin key .." - rpm --import "$webmin_key" + echo " Installing Webmin developers key .." + rpm --import "$repo_key" mkdir -p "/etc/pki/rpm-gpg" - cp -f "$webmin_key" \ - "/etc/pki/rpm-gpg/RPM-GPG-KEY-$webmin_key_suffix" + cp -f "$repo_key" \ + "/etc/pki/rpm-gpg/RPM-GPG-KEY-$repo_key_suffix" echo " .. done" - if [ "$testing_mode" = "1" ]; then - echo " Setting up Webmin testing repository .." - cat << EOF > "$rpm_repo_file_testing" -[webmin-testing-noarch] -name=Webmin Testing -baseurl=$webmin_download_testing -enabled=1 -gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$webmin_key_suffix -gpgcheck=1 -$repo_extra_opts -EOF + echo " Setting up ${repo_desc_formatted} repository .." + if [ "$repo_mode" = "stable" ]; then + repo_url="$active_repo_download/download/newkey/yum" else - echo " Setting up Webmin repository .." - cat << EOF > "$rpm_repo_file" -[webmin-noarch] -name=Webmin Stable -baseurl=$webmin_download/download/newkey/yum + repo_url="$repo_auth_url" + fi + cat << EOF > "$rpm_repo_file" +[$active_repo_name-noarch] +name=$active_repo_description +baseurl=$repo_url enabled=1 -gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$webmin_key_suffix +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$repo_key_suffix gpgcheck=1 $repo_extra_opts EOF - fi echo " .. done" + echo " Downloading repository metadata .." + update_output=$($update 2>&1) + post_status $? "$update_output" ;; deb) rm -f \ -"/usr/share/keyrings/debian-$webmin_key_suffix.gpg" \ -"/usr/share/keyrings/$repoid_debian_like-$webmin_key_suffix.gpg" - echo " Installing Webmin key .." - gpg --import "$webmin_key" 1>/dev/null 2>&1 - gpg --dearmor < "$webmin_key" \ - > "/usr/share/keyrings/$repoid_debian_like-$webmin_key_suffix.gpg" - echo " .. done" - sources_list=$(grep -v "$webmin_host" /etc/apt/sources.list) +"/usr/share/keyrings/debian-$repo_key_suffix.gpg" \ +"/usr/share/keyrings/$repoid_debian_like-$repo_key_suffix.gpg" + echo " Installing Webmin developers key .." + gpg --import "$repo_key" 1>/dev/null 2>&1 + gpg --dearmor < "$repo_key" \ + > "/usr/share/keyrings/$repoid_debian_like-$repo_key_suffix.gpg" + post_status $? + sources_list=$(grep -v "$repo_host" /etc/apt/sources.list) echo "$sources_list" > /etc/apt/sources.list - if [ "$testing_mode" = "1" ]; then - echo " Setting up Webmin testing repository .." - echo \ -"deb [signed-by=/usr/share/keyrings/$repoid_debian_like-$webmin_key_suffix.gpg] \ -$webmin_download_testing webmin main" \ - > "$debian_repo_file_testing" + echo " Setting up ${repo_desc_formatted} repository .." + if [ "$repo_mode" = "stable" ]; then + repo_line="deb [signed-by=/usr/share/keyrings/$repoid_debian_like-$repo_key_suffix.gpg] \ +$active_repo_download/download/newkey/repository $repo_dist $repo_section" else - echo " Setting up Webmin repository .." - echo \ -"deb [signed-by=/usr/share/keyrings/$repoid_debian_like-$webmin_key_suffix.gpg] \ -$webmin_download/download/newkey/repository stable contrib" \ - > "$debian_repo_file" + repo_line="deb [signed-by=/usr/share/keyrings/$repoid_debian_like-$repo_key_suffix.gpg] \ +$active_repo_download $repo_dist $repo_component" fi + echo "$repo_line" > "$debian_repo_file" + + # Handle APT authentication if credentials provided + if [ -n "$repo_auth_user" ] && [ -n "$repo_auth_pass" ]; then + mkdir -p "/etc/apt/auth.conf.d" + auth_file="/etc/apt/auth.conf.d/$active_repo_name.conf" + auth_domain="${active_repo_download#*://}" + auth_domain="${auth_domain%%/*}" + + # Remove existing entry for this domain if exists + if [ -f "$auth_file" ]; then + sed -i "/machine $auth_domain/d" "$auth_file" + fi + + # Add new authentication entry + echo "machine $auth_domain login $repo_auth_user password $repo_auth_pass" >> "$auth_file" + chmod 600 "$auth_file" + fi + echo " .. done" echo " Cleaning repository metadata .." $clean 1>/dev/null 2>&1 echo " .. done" echo " Downloading repository metadata .." - $update 1>/dev/null 2>&1 - echo " .. done" + update_output=$($update 2>&1) + post_status $? "$update_output" ;; *) echo "${RED}Error:${NORMAL} Cannot set up repositories on this system." @@ -262,10 +427,11 @@ $webmin_download/download/newkey/repository stable contrib" \ } final_msg() { - if [ ! -x "/usr/bin/webmin" ]; then - echo "Webmin and Usermin can be installed with:" - echo " ${GREEN}${BOLD}${ITALIC}$install_cmd webmin usermin${NORMAL}" + if [ "$install_check_binary" != "0" ] && [ ! -x "$install_check_binary" ]; then + echo "$install_message" + echo " ${GREEN}${BOLD}${ITALIC}$install_cmd $install_packages${NORMAL}" fi + exit 0 }