diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index cf57800c..5b402e8f 100755 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -18,15 +18,11 @@ source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" # is_repo() sourced from basic-install.sh # setupVars set in basic-install.sh # check_download_exists sourced from basic-install.sh -# fully_fetch_repo sourced from basic-install.sh -# get_available_branches sourced from basic-install.sh -# fetch_checkout_pull_branch sourced from basic-install.sh -# checkout_pull_branch sourced from basic-install.sh source "${setupVars}" warning1() { - echo " Please note that changing branches severely alters your Pi-hole subsystems" + echo " Please note that changing branches or tags severely alters your Pi-hole subsystems" echo " Features that work on the master branch, may not on a development branch" echo -e " ${COL_LIGHT_RED}This feature is NOT supported unless a Pi-hole developer explicitly asks!${COL_NC}" read -r -p " Have you read and understood this? [y/N] " response @@ -36,15 +32,125 @@ warning1() { return 0 ;; *) - echo -e "\\n ${INFO} Branch change has been canceled" + echo -e "\\n ${INFO} Branch/Tag change has been canceled" return 1 ;; esac } +warning2() { + echo "" + echo " ${INFO} Your are trying to checkout the tag ${1} for Pihole's core component" + echo " ${COL_LIGHT_RED}Please note that checking out core tags before v5.17 will break your installation${COL_NC}" + read -r -p " Proceed anyway? [y/N] " response + case "${response}" in + [yY][eE][sS]|[yY]) + echo "" + return 0 + ;; + *) + echo -e "\\n ${INFO} Branch/Tag change has been canceled" + return 1 + ;; + esac +} + +fully_fetch_repo() { + # Add upstream branches/tags to shallow clone + local directory="${1}" + + cd "${directory}" || return 1 + if is_repo "${directory}"; then + git remote set-branches origin '*' || return 1 + git fetch --tags --quiet || return 1 + else + return 1 + fi + return 0 +} + +get_available_refs() { + # Return available branches and tags + local directory + directory="${1}" + local output + + cd "${directory}" || return 1 + # Get reachable remote branches and tags, but store STDERR as STDOUT variable + output=$( { git ls-remote --heads --tags --quiet | cut -d'/' -f3- -; } 2>&1 ) + # echo status for calling function to capture + echo "$output" + return +} + +fetch_checkout_pull_branch() { + # Check out specified branch + local directory + directory="${1}" + local branch + branch="${2}" + + # Set the reference for the requested branch, fetch, check it put and pull it + cd "${directory}" || return 1 + git remote set-branches origin "${branch}" || return 1 + git stash --all --quiet &> /dev/null || true + git clean --quiet --force -d || true + git fetch --quiet || return 1 + checkout_pull_ref "${directory}" "${branch}" || return 1 +} + +checkout_pull_ref() { + # Check out specified branch/tag + local directory + directory="${1}" + local ref + ref="${2}" + local oldref + local ref_is_tag + + cd "${directory}" || return 1 + + # Get current branch name. + oldref="$(git symbolic-ref --quiet HEAD)" + + str="Switching to branch/tag: '${ref}' from '${oldref}'" + printf " %b %s\\n" "${INFO}" "$str" + + # check if the ref is a branch or a tag on the remote repo + if git show-ref -q --verify "refs/remotes/origin/$ref" 2>/dev/null; then + ref_is_tag=false + elif git show-ref -q --verify "refs/tags/$ref" 2>/dev/null; then + ref_is_tag=true + fi + + if [ "$ref_is_tag" = true ]; then + # In case we are checking out tags on 'core' show an additional warning and allow to cancel checkout + if [ "${directory}" = "${PI_HOLE_FILES_DIR}" ]; then + if ! warning2 "${ref}" ; then + exit 1 + fi + + fi + # in case we want to checkout a tag we need explicitly create a new branch/reset the existing one + git checkout -B "${ref}" "refs/tags/${ref}" --quiet || return 1 + printf "%b %b %s\\n" "${OVER}" "${TICK}" "$str" + else + # checkout a branch, this also sets tracking branch + git checkout "${ref}" --quiet || return 1 + printf "%b %b %s\\n" "${OVER}" "${TICK}" "$str" + git_pull=$(git pull --no-rebase || return 1) + printf " %b %s\\n" "${INFO}" "${git_pull}" + fi + + # Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git) + chmod -R a+rX "${directory}" + + return 0 +} + checkout() { - local corebranches - local webbranches + local corerefs + local webrefs # Check if FTL is installed - do this early on as FTL is a hard dependency for Pi-hole local funcOutput @@ -111,59 +217,59 @@ checkout() { echo "master" > /etc/pihole/ftlbranch chmod 644 /etc/pihole/ftlbranch elif [[ "${1}" == "core" ]] ; then - str="Fetching branches from ${piholeGitUrl}" + str="Fetching branches/tags from ${piholeGitUrl}" echo -ne " ${INFO} $str" if ! fully_fetch_repo "${PI_HOLE_FILES_DIR}" ; then echo -e "${OVER} ${CROSS} $str" exit 1 fi - corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}")) + mapfile -t corerefs < <(get_available_refs "${PI_HOLE_FILES_DIR}") - if [[ "${corebranches[*]}" == *"master"* ]]; then + if [[ "${corerefs[*]}" == *"master"* ]]; then echo -e "${OVER} ${TICK} $str" - echo -e " ${INFO} ${#corebranches[@]} branches available for Pi-hole Core" + echo -e " ${INFO} ${#corerefs[@]} branches/tags available for Pi-hole Core" else - # Print STDERR output from get_available_branches - echo -e "${OVER} ${CROSS} $str\\n\\n${corebranches[*]}" + # Print STDERR output from get_available_refs + echo -e "${OVER} ${CROSS} $str\\n\\n${corerefs[*]}" exit 1 fi echo "" - # Have the user choose the branch they want - if ! (for e in "${corebranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then - echo -e " ${INFO} Requested branch \"${2}\" is not available" - echo -e " ${INFO} Available branches for Core are:" - for e in "${corebranches[@]}"; do echo " - $e"; done + # Have the user choose the branch/tag they want + if ! (for e in "${corerefs[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then + echo -e " ${INFO} Requested branch/tag \"${2}\" is not available" + echo -e " ${INFO} Available branches/tags for Core are:" + for e in "${corerefs[@]}"; do echo " - $e"; done exit 1 fi - checkout_pull_branch "${PI_HOLE_FILES_DIR}" "${2}" + checkout_pull_ref "${PI_HOLE_FILES_DIR}" "${2}" elif [[ "${1}" == "web" ]] && [[ "${INSTALL_WEB_INTERFACE}" == "true" ]] ; then - str="Fetching branches from ${webInterfaceGitUrl}" + str="Fetching branches/tags from ${webInterfaceGitUrl}" echo -ne " ${INFO} $str" if ! fully_fetch_repo "${webInterfaceDir}" ; then echo -e "${OVER} ${CROSS} $str" exit 1 fi - webbranches=($(get_available_branches "${webInterfaceDir}")) + mapfile -t webrefs < <(get_available_refs "${webInterfaceDir}") - if [[ "${webbranches[*]}" == *"master"* ]]; then + if [[ "${webrefs[*]}" == *"master"* ]]; then echo -e "${OVER} ${TICK} $str" - echo -e " ${INFO} ${#webbranches[@]} branches available for Web Admin" + echo -e " ${INFO} ${#webrefs[@]} branches/tags available for Web Admin" else - # Print STDERR output from get_available_branches - echo -e "${OVER} ${CROSS} $str\\n\\n${webbranches[*]}" + # Print STDERR output from get_available_refs + echo -e "${OVER} ${CROSS} $str\\n\\n${webrefs[*]}" exit 1 fi echo "" - # Have the user choose the branch they want - if ! (for e in "${webbranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then - echo -e " ${INFO} Requested branch \"${2}\" is not available" - echo -e " ${INFO} Available branches for Web Admin are:" - for e in "${webbranches[@]}"; do echo " - $e"; done + # Have the user choose the branch/tags they want + if ! (for e in "${webrefs[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then + echo -e " ${INFO} Requested branch/tag \"${2}\" is not available" + echo -e " ${INFO} Available branches/tags for Web Admin are:" + for e in "${webrefs[@]}"; do echo " - $e"; done exit 1 fi - checkout_pull_branch "${webInterfaceDir}" "${2}" + checkout_pull_ref "${webInterfaceDir}" "${2}" # Update local and remote versions via updatechecker /opt/pihole/updatecheck.sh elif [[ "${1}" == "ftl" ]] ; then @@ -184,7 +290,7 @@ checkout() { /opt/pihole/updatecheck.sh else echo " ${CROSS} Requested branch \"${2}\" is not available" - ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') ) + ftlbranches=("$(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}')") echo -e " ${INFO} Available branches for FTL are:" for e in "${ftlbranches[@]}"; do echo " - $e"; done exit 1 diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 1b9997a6..ec03af2f 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -36,15 +36,17 @@ source "/opt/pihole/COL_TABLE" GitCheckUpdateAvail() { local directory local curBranch + local ref_is_tag directory="${1}" curdir=$PWD cd "${directory}" || return # Fetch latest changes in this repo - git fetch --quiet origin + git fetch --tags --quiet origin # Check current branch. If it is master, then check for the latest available tag instead of latest commit. - curBranch=$(git rev-parse --abbrev-ref HEAD) + # Remove any leading "heads/" as $curBranch is later used to check if local branch is remote branch or tag + curBranch=$(git rev-parse --abbrev-ref HEAD| sed "s/^heads\///g") if [[ "${curBranch}" == "master" ]]; then # get the latest local tag LOCAL=$(git describe --abbrev=0 --tags master) @@ -56,14 +58,26 @@ GitCheckUpdateAvail() { # need @{0} LOCAL="$(git rev-parse "@{0}")" - # The suffix @{upstream} to a branchname - # (short form @{u}) refers - # to the branch that the branch specified - # by branchname is set to build on top of# - # (configured with branch..remote and - # branch..merge). A missing branchname - # defaults to the current one. - REMOTE="$(git rev-parse "@{upstream}")" + # check if the local branch ref is a branch or a tag on remote repo + if git show-ref -q --verify "refs/remotes/origin/$curBranch" 2>/dev/null; then + ref_is_tag=false + elif git show-ref -q --verify "refs/tags/$curBranch" 2>/dev/null; then + ref_is_tag=true + fi + if [ "$ref_is_tag" = true ]; then + # as there is no tracking upstream branch for a checked out tag, there is need to + # check for updates + REMOTE="${LOCAL}" + else + # The suffix @{upstream} to a branchname + # (short form @{u}) refers + # to the branch that the branch specified + # by branchname is set to build on top of# + # (configured with branch..remote and + # branch..merge). A missing branchname + # defaults to the current one. + REMOTE="$(git rev-parse "@{upstream}")" + fi fi diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 4c69788f..18a7211c 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -460,6 +460,7 @@ update_repo() { # This helps prevent the wrong value from being assigned if you were to set the variable as a GLOBAL one local directory="${1}" local curBranch + local ref_is_branch # A variable to store the message we want to display; # Again, it's useful to store these in variables in case we need to reuse or change the message; @@ -472,11 +473,26 @@ update_repo() { # Stash any local commits as they conflict with our working code git stash --all --quiet &> /dev/null || true # Okay for stash failure git clean --quiet --force -d || true # Okay for already clean directory - # Pull the latest commits - git pull --no-rebase --quiet &> /dev/null || return $? - # Check current branch. If it is master, then reset to the latest available tag. + + # Check current branch. + # Remove any leading "heads/" as $curBranch is later used to check if local branch is remote branch or tag + curBranch=$(git rev-parse --abbrev-ref HEAD| sed "s/^heads\///g") + + # check if the local branch ref is a branch or a tag on remote repo + if git show-ref -q --verify "refs/remotes/origin/$curBranch" 2>/dev/null; then + ref_is_branch=true + elif git show-ref -q --verify "refs/tags/$curBranch" 2>/dev/null; then + ref_is_branch=false + fi + + # only pull if we are on a branch not a tag + if [ "$ref_is_branch" = true ]; then + # Pull the latest commits + git pull --no-rebase --quiet &> /dev/null || return $? + fi + + # If the current branch is master, then reset to the latest available tag. # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) - curBranch=$(git rev-parse --abbrev-ref HEAD) if [[ "${curBranch}" == "master" ]]; then git reset --hard "$(git describe --abbrev=0 --tags)" || return $? fi @@ -2098,76 +2114,6 @@ check_download_exists() { fi } -fully_fetch_repo() { - # Add upstream branches to shallow clone - local directory="${1}" - - cd "${directory}" || return 1 - if is_repo "${directory}"; then - git remote set-branches origin '*' || return 1 - git fetch --quiet || return 1 - else - return 1 - fi - return 0 -} - -get_available_branches() { - # Return available branches - local directory - directory="${1}" - local output - - cd "${directory}" || return 1 - # Get reachable remote branches, but store STDERR as STDOUT variable - output=$( { git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1 ) - # echo status for calling function to capture - echo "$output" - return -} - -fetch_checkout_pull_branch() { - # Check out specified branch - local directory - directory="${1}" - local branch - branch="${2}" - - # Set the reference for the requested branch, fetch, check it put and pull it - cd "${directory}" || return 1 - git remote set-branches origin "${branch}" || return 1 - git stash --all --quiet &> /dev/null || true - git clean --quiet --force -d || true - git fetch --quiet || return 1 - checkout_pull_branch "${directory}" "${branch}" || return 1 -} - -checkout_pull_branch() { - # Check out specified branch - local directory - directory="${1}" - local branch - branch="${2}" - local oldbranch - - cd "${directory}" || return 1 - - oldbranch="$(git symbolic-ref HEAD)" - - str="Switching to branch: '${branch}' from '${oldbranch}'" - printf " %b %s" "${INFO}" "$str" - git checkout "${branch}" --quiet || return 1 - printf "%b %b %s\\n" "${OVER}" "${TICK}" "$str" - # Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git) - chmod -R a+rX "${directory}" - - git_pull=$(git pull --no-rebase || return 1) - - printf " %b %s\\n" "${INFO}" "${git_pull}" - - return 0 -} - clone_or_update_repos() { # If the user wants to reconfigure, if [[ "${reconfigure}" == true ]]; then