diff --git a/installer/VERSION b/installer/VERSION new file mode 100644 index 0000000..49d5957 --- /dev/null +++ b/installer/VERSION @@ -0,0 +1 @@ +0.1 diff --git a/installer/installdebmirrorman.sh b/installer/installdebmirrorman.sh new file mode 100755 index 0000000..47e9e2b --- /dev/null +++ b/installer/installdebmirrorman.sh @@ -0,0 +1,279 @@ +#!/bin/bash + +VERSION="0.1" + +function doError { + if [[ -z ${1} ]]; then + echo "ERROR: An unknown error occurred..." >&2 + else + echo + echo -e "ERROR: ${1}" >&2 + fi + exit 1 +} + +function doFinalStep { + echo "declare laststep=99" > "/etc/debmirrorman/last.step" || doError "Failed to write /etc/debmirrorman/last.step..." +} + +function doMessageDelay { + if [[ -z ${1} ]]; then + echo "Function doMessageDelay requires message..." >&2 + echo "Example:" >&2 + echo "doMessageDelay \"Message goes here\" {delayinseconds}" >&2 + exit 2 + else + local message="${1}" + fi + + if [[ -z ${2} ]]; then + local seconds=3 + elif [[ ${2} =~ ^[0-9]+$ ]] && [[ ${2} -gt 0 ]] && [[ ${2} -lt 31 ]]; then + local seconds=${2} + else + echo "Function doMessageDelay {delayinseconds} must be an integer from 1 to 30..." >&2 + echo "Example:" >&2 + echo "doMessageDelay \"Message goes here\" {delayinseconds}" >&2 + exit 2 + fi + + echo + echo + echo + echo -en "${message}" + + for ((s=0; s<${seconds}; s++)); do + echo -n "." + sleep 1s + done + + echo + echo +} + +function doNextStep { + ((laststep++)) + echo "declare laststep=${laststep}" > "/etc/debmirrorman/last.step" || doError "Failed to write /etc/debmirrorman/last.step..." +} + +eval "$(cat /etc/os-release | grep ^ID)" +[[ "${ID,,}" == "debian" ]] || doError "This script will only work on a Debian server..." +unset ID + +[[ "$(whoami)" == "root" ]] || doError "You must be root to run this script...\nEither log in as root user or run the script using sudo..." + +mkdir -p "/etc/debmirrorman" || doError "Failed to create settings folder /etc/debmirrorman..." +[[ -f "/etc/debmirrorman/last.step" ]] || echo "declare laststep=0" > "/etc/debmirrorman/last.step" || doError "Failed to write /etc/debmirrorman/last.step..." +source "/etc/debmirrorman/last.step" || doError "Failed to read /etc/debmirrorman/last.step..." +[[ "${laststep}" == 99 ]] && echo -e "DebMirrorMan has already been installed...\nUse debmirrorman help to information on how to use it..." && exit + +doMessageDelay "First let's make sure Debian is fully updated" +apt update && apt -y upgrade || doError "Error running update..." + +doMessageDelay "If this installer script stops with an error, you can resolve the cause of the error\nand then just rerun this script and the installation will pick up from where it left off" 10 + +if [[ "${laststep}" -lt 1 ]]; then + doMessageDelay "Step $((laststep+1)): Let's first make sure that there is nothing already listening on ports 80 and/or 443" + ss -tuln | grep -qE ':(80|443)\s' && doError "Can't continue as there is already a service listening on ports 80 and/or 443...\nI would recommend you run this script on a vanilla server..." + doNextStep +fi + +if [[ "${laststep}" -lt 2 ]]; then + doMessageDelay "Step $((laststep+1)): Let's install required software" + apt -y install nginx debmirror ca-certificates gnupg curl || doError "Error installing required software..." + #wget https://git.3volve.net.za/thisiszeev...... + doNextStep +fi + +if [[ "${laststep}" -lt 3 ]]; then + doMessageDelay "Step $((laststep+1)): Let's setup Nginx" + echo -n "What is the FQDN you want for the repo server? " + read -r localfqdn + echo + [[ "${localfqdn}" =~ ^([a-z0-9](-?[a-z0-9])*\.)+[a-z]{2,}$ ]] || doError "Not a valid FQDN..." + mkdir -p "/var/www/${localfqdn}" && chown -R www-data:www-data "/var/www/${localfqdn}" || doError "Error creating webroot..." + echo "server {" > "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo " listen 80;" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo " server_name ${localfqdn};" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo "" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo " root /var/www/${localfqdn};" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo "" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo " location / {" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo " autoindex on;" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo " }" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + echo "}" >> "/etc/nginx/sites-available/${localfqdn}" || doError "Error creating VHost in /etc/nginx/sites-available..." + ln -s "/etc/nginx/sites-available/${localfqdn}" /etc/nginx/sites-enabled/ || doError "Error enabling VHost..." + rm -f "/etc/nginx/sites-enabled/default" || doError "Error disabling default VHost..." + nginx -t && systemctl reload nginx || doError "Error restarting Nginx..." + echo "declare localfqdn=\"${localfqdn}\"" > "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + doNextStep +fi + +if [[ "${laststep}" -lt 4 ]]; then + doMessageDelay "Step $((laststep+1)): Let's add our first repo to be mirrored" + if [[ -z "${localfqdn}" ]]; then + source "/etc/debmirrorman/settings.conf" || doError "Failed to read /etc/debmirrorman/settings.conf..." + fi + echo -n "What is the FQDN for the repo server you want to mirror? (Just press ENTER to skip this step) " + read -r remotefqdn + echo + if [[ -n "${remotefqdn}" ]]; then + [[ "${remotefqdn}" =~ ^([a-z0-9](-?[a-z0-9])*\.)+[a-z]{2,}$ ]] || doError "Not a valid FQDN..." + nslookup "${remotefqdn}" > /dev/null 2>&1 || doError "FQDN does not resolve to an IP address..." + echo -n "What is the name of the repo on ${remotefqdn} you want to mirror? " + read -r remoterepo + echo + [[ "${remoterepo}" =~ ^[a-z0-9]+([a-z0-9-]*[a-z0-9])?$ ]] || doError "Invalid repo name...\nMust use lowercase letter, digits and/or dash - (dash can not be the first or last character)..." + echo -n "What is the name you want to use for the repo on your local mirror server ${localfqdn}? (Press ENTER to use ${remoterepo}) " + read -r localrepo + echo + if [[ -z "${localrepo}" ]]; then + localrepo="${remoterepo}" + fi + [[ "${localrepo}" =~ ^[a-z0-9]+([a-z0-9-]*[a-z0-9])?$ ]] || doError "Invalid repo name...\nMust use lowercase letter, digits and/or dash - (dash can not be the first or last character)..." + echo -n "What is the name of the section(s) on ${remotefqdn}/${remoterepo} that you want to mirror? (Use a comma , as a delimeter if you want to use more than one - no spaces) " + read -r section + echo + [[ "${section}" =~ ^([a-z0-9]+([a-z0-9-]*[a-z0-9])?)(,([a-z0-9]+([a-z0-9-]*[a-z0-9])?))*$ ]] || doError "Invalid section(s) name(s)...\nMust use lowercase letter, digits and/or dash - (dash can not be the first or last character)...\nYou can use a comma , as a delimiter for multiple sections, but no spaces..." + echo -n "What is the name of the distribution(s) on ${remotefqdn}/${remoterepo} that you want to mirror? (Use a comma , as a delimeter if you want to use more than one - no spaces) " + read -r distribution + echo + [[ "${distribution}" =~ ^([a-z0-9]+([a-z0-9-]*[a-z0-9])?)(,([a-z0-9]+([a-z0-9-]*[a-z0-9])?))*$ ]] || doError "Invalid distribution(s) name(s)...\nMust use lowercase letter, digits and/or dash - (dash can not be the first or last character)...\nYou can use a comma , as a delimiter for multiple distributions, but no spaces..." + echo -n "What hardware architecture(s) do you want to mirror? (Use a comma , as a delimeter if you want to use more than one - no spaces) " + read -r architecture + echo + [[ "${architecture}" =~ ^([a-z0-9]+)(,[a-z0-9]+)*$ ]] || doError "Invalid architecture string given...\nMust use lowercase letter, digits and/or dash - (dash can not be the first or last character)...\nYou can use a comma , as a delimiter for multiple architectures, but no spaces..." + echo -n "Must we use HTTPS for the remote repo? (Y/n) " + read -r -n 1 method + echo + if [[ -z "${method}" || "${method,,}" == "y" ]]; then + echo "Using HTTPS..." + method="https" + else + echo "Using HTTP..." + method="http" + fi + echo -n "If this repo requires a GPG key then paste the URL to download it, or press just ENTER if there is no GPG key. " + echo -n "Do you want to mirror the Source data and files (Y/n) " + read -r keyringurl + echo + if [[ -z "${keyringurl}" ]]; then + echo "No keyring is being used..." + keyring=false + else + echo "Downloading keyring file and saving it to /etc/debmirrorman/${localrepo}.gpg..." + curl -sSLo "/etc/debmirrorman/${localrepo}.gpg" "${keyringurl}" || doError "Failed to down ${keyringurl}..." + keyring=true + fi + echo -n "Do you want to mirror the Debian Install data (normally only needed when mirroring the main Debian repos at deb.debian.org) (y/N) " + read -r -n 1 debinst + echo + if [[ -z "${debinst}" || "${debinst,,}" != "y" ]]; then + echo "Not including Debian Install data..." + debinst=false + else + echo "Including Debian Install data..." + debinst=true + fi + echo -n "Choose a memorable name that you can use to identify this repo in the future... " + read -r prettyname + echo "Writing configuration files..." + echo "declare -a localrepos=(" >> "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + echo " \"${localrepo}\"" >> "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + echo ")" >> "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + echo "declare -A prettynames=(" >> "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + echo " [${localrepo}]=\"${prettyname}\"" >> "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + echo ")" >> "/etc/debmirrorman/settings.conf" || doError "Failed to write /etc/debmirrorman/settings.conf" + echo "## Details for ${prettyname}" > "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare remotefqdn=\"${remotefqdn}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare remoterepo=\"${remoterepo}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare section=\"${section}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare distribution=\"${distribution}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare architecture=\"${architecture}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare method=\"${method}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare keyring=${keyring}" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare keyringurl=\"${keyringurl}\"" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare sources=${sources}" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "declare debinst=${debinst}" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + echo "## This file was last updated on $(date)" >> "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to write /etc/debmirrorman/${localrepo}.repo" + else + echo "Skipping..." + doNextStep + fi + doNextStep +fi + +if [[ ${laststep} -lt 5 ]]; then + doMessageDelay "Step $((laststep+1)): Going to do a dry-run test on your first repo" + if [[ -z "${localfqdn}" ]]; then + source "/etc/debmirrorman/settings.conf" || doError "Failed to read /etc/debmirrorman/settings.conf..." + [[ "${#localrepos[@]}" -gt 0 ]] || doError "No local repos found in /etc/debmirrorman/settings.conf..." + localrepo="${localrepos[0]}" + [[ "${#prettynames[@]}" -gt 0 ]] || doError "No pretty names for local repos found in /etc/debmirrorman/settings.conf..." + prettyname="${prettynames[${localrepo}]}" + echo "Loading details for ${prettyname} from /etc/debmirrorman/${localrepo}.repo..." + source "/etc/debmirrorman/${localrepo}.repo" || doError "Failed to read /etc/debmirrorman/${localrepo}.repo" + fi + if [[ "${sources}" == true ]]; then + sourcetag="--source" + else + sourcetag="--nosource" + fi + if [[ "${debinst}" == true ]]; then + if [[ "${keyring}" == true ]]; then + debmirror --host=${remotefqdn} --root=${remoterepo} --dist=${distribution} --section=${section} --arch=${architecture} --method=${method} --keyring=/etc/debmirrorman/${localrepo}.gpg ${sourcetag} --di-dist=${distribution} --di-arch=${architecture} --progress --dry-run /var/www/${localfqdn}/${localrepo} || doError "Dry run failed..." + else + debmirror --host=${remotefqdn} --root=${remoterepo} --dist=${distribution} --section=${section} --arch=${architecture} --method=${method} ${sourcetag} --di-dist=${distribution} --di-arch=${architecture} --progress --dry-run /var/www/${localfqdn}/${localrepo} || doError "Dry run failed..." + fi + else + if [[ "${keyring}" == true ]]; then + debmirror --host=${remotefqdn} --root=${remoterepo} --dist=${distribution} --section=${section} --arch=${architecture} --method=${method} --keyring=/etc/debmirrorman/${localrepo}.gpg ${sourcetag} --progress --dry-run /var/www/${localfqdn}/${localrepo} || doError "Dry run failed..." + else + debmirror --host=${remotefqdn} --root=${remoterepo} --dist=${distribution} --section=${section} --arch=${architecture} --method=${method} ${sourcetag} --progress --dry-run /var/www/${localfqdn}/${localrepo} || doError "Dry run failed..." + fi + fi + doNextStep +fi + +if [[ ${laststep} -lt 6 ]]; then + doMessageDelay "Step $((laststep+1)): Install is completed" + echo + echo "Now you can use debmirrorman to sync your repos or to add/edit/remove repos." + echo + echo "Example:" + echo " debmirrorman run" + echo " This will sync all repos" + echo " debmirrorman run {localreponame}" + echo " This will sync just the repo called {localreponame}" + echo " debmirrorman dryrun {localreponame}" + echo " This will test the repo called {localreponame}" + echo " debmirrorman list" + echo " This will list all your repos that are setup on the server" + echo " debmirrorman add" + echo " This will add a new repo for syncing" + echo " debmirrorman show {localreponame}" + echo " This will show you the details for the repo called {localreponame}" + echo " debmirrorman edit {localreponame}" + echo " This will allow you to edit the details for the repo called {localreponame}" + echo " debmirrorman remove {localreponame}" + echo " This will allow you to remove the repo, including all synced packages, for the repo called {localreponame}" + echo " debmirrorman usage" + echo " This will calculate how much storage space each repo is taking up, as well as give you a total of all repos" + echo " debmirrorman usage {localreponame}" + echo " This will calculate how much storage space is taken up, itemized by distribution and section, for the repo called {localreponame}" + echo " debmirrorman update" + echo " This will check if there is a newer version of DebMirror Manager and if so update the currently installed version with the newer version" + echo + echo "DebMirror Manager is released under the Gnu Public Lincense 3.0 and is Free to use, copy, study, modify and share." + echo "If you find DebMirror Manager to be useful, please consider making a donation at https://paypal.me/thisiszeev" + echo "My primary source of income is from donations by people like you... and it motivates me to keep developing more useful tools and scripts." + echo " \$5 will buy me a cup of coffee" + echo " \$10 will buy me a nice burger" + echo " \$20 will buy me a bottle of wine" + echo "Anything above that will also be greatly appreciate..." + echo + echo "If you donate, or even if you just have a query, found a bug or need assistance, please message me on Reddit (u/thisiszeev)." + echo "Be patient if I do not reply promptly, I have been struggling with my health lately so I am not on Reddit as much as I would like." + echo "But I will reply as soon as I can..." + doFinalStep +fi