#!/bin/bash # Purpose: clean ngcpcfg config/templates ################################################################################ set -e set -u # support testsuite FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}" HELPER="${HELPER:-/usr/share/ngcp-ngcpcfg/helper/}" SCRIPTS="${SCRIPTS:-/usr/share/ngcp-ngcpcfg/scripts/}" if ! [ -r "${FUNCTIONS}"/main ] ; then printf "Error: %s/main could not be read. Exiting.\n" "${FUNCTIONS}" >&2 exit 1 fi # shellcheck disable=SC1090 . "${FUNCTIONS}"/main cd "${NGCPCTL_MAIN}" ## functions {{{ clean_help() { export TIME_FORMAT='' log_info "Sample:" log_info " ngcpcfg clean [--all] [--branches] [--force] [--help] [--reset-master] [--stashes] [--tracked-files] [--untracked-files]" log_info "" log_info "Run 'man ngcpcfg' for more information." } request_confirmation() { if "${force:-false}" ; then log_info "Forcing action due to option '--force'" return 0 fi if ! tty -s; then log_warn "Cannot request confirmation (no tty). Hint: use option '--force'." return 1 fi log_info_n "Please confirm 'yes' or 'no': " # clearing STDIN while read -r -t 0; do read -r; done while true ; do read -r a case "${a,,}" in yes) return 0 ;; no) return 1 ;; *) log_info_n "Please answer 'yes' or 'no': " ;; esac unset a done } clean_ensure_branch_master () { log_info "Ensure local branch is 'master':" local current_branch log_debug "Call: compare_active_branch 'master'" current_branch=$(compare_active_branch 'master') if [ "${current_branch}" = 'master' ] ; then log_info "OK: branch master active" else log_debug "git checkout master" if git checkout master ; then log_info "OK: checked out branch master " else log_error "Cannot checkout branch 'master'" return 1 fi fi return 0 } clean_tracked() { log_info "Removing all local changes (if any):" if is_git_clean ; then log_info "OK: no local changes found, nothing to clean here." return 0 fi log_info "Found local changes in '${NGCPCTL_MAIN}':" log_debug "git status -s -uall" git status -s -uall log_info "Should we remove changes above?" if ! request_confirmation ; then log_info "Skipping cleanup of local changes as requested." return 0 fi log_debug "git reset --hard HEAD" if git reset --hard HEAD ; then log_info "OK: Done" else log_error "Cannot reset local changes" return 1 fi return 0 } clean_untracked() { log_info "Removing all untracked files (if any):" local tmp tmp=$(mktemp -t ngcpcfg-clean-untracked.XXXXXXXXXX) log_debug "git clean -n -f -d -x" if ! git clean -n -f -d -x > "${tmp}" ; then log_error "Failed to collect list of untracked files" return 1 fi if [ "$(wc -l < "${tmp}")" = "0" ] ; then log_info "OK: No untracked files found, nothing to clean here." return 0 fi log_info "Found untracked files in '${NGCPCTL_MAIN}':" cat "${tmp}" log_info "Should we remove untracked files above?" if ! request_confirmation ; then log_info "Skipping cleanup of untracked files as requested." return 0 fi log_debug "git clean -f -d -x" if ! git clean -f -d -x ; then log_error "Failed to clean list of untracked files" return 1 fi if [ -n "${DEBUG:-}" ] ; then log_debug "Not removing temporary file ${tmp}" else rm -f "${tmp}" fi return 0 } clean_stash() { log_info "Removing all stashes (if any):" local tmp tmp=$(mktemp -t ngcpcfg-clean-stash.XXXXXXXXXX) log_debug "git stash list" if ! git stash list > "${tmp}" ; then log_error "Failed to collect list of stashes" return 1 fi if [ "$(wc -l < "${tmp}")" = "0" ] ; then log_info "OK: No stashes found, nothing to clean here." return 0 fi log_info "Found stashes in '${NGCPCTL_MAIN}':" cat "${tmp}" log_info "Should we remove the stashes above?" if ! request_confirmation ; then log_info "Skipping stash cleanup as requested." return 0 fi log_debug "git stash clear" if ! git stash clear ; then log_error "Failed to clean git stashes" return 1 fi if [ -n "${DEBUG:-}" ] ; then log_debug "Not removing temporary file ${tmp}" else rm -f "${tmp}" fi return 0 } clean_old_local_branches() { log_info "Removing all old branches (if any):" local tmp tmp=$(mktemp -t ngcpcfg-clean-oldbranch.XXXXXXXXXX) log_debug "git branch" if ! git branch > "${tmp}" ; then log_error "Failed to collect list of available branches" return 1 fi # do not propose to delete active branch and branch 'master' sed -i -r '/^\* /d' "${tmp}" sed -i -r '/ master$/d' "${tmp}" if [ "$(wc -l < "${tmp}")" = "0" ] ; then log_info "OK: No branches to delete found, nothing to clean here." return 0 fi log_info "Found branches to be removed:" cat "${tmp}" log_info "Should we remove the branches above?" if ! request_confirmation ; then log_info "Skipping branches cleanup as requested." return 0 fi while read -r branch ; do log_debug "git branch -D '${branch}'" if ! git branch -D "${branch}" ; then log_error "Failed to delete local branch '${branch}'" return 1 fi done < "${tmp}" if [ -n "${DEBUG:-}" ] ; then log_debug "Not removing temporary file ${tmp}" else rm -f "${tmp}" fi return 0 } clean_reset_master() { log_debug "checking 'origin' availability using: git remote show origin" if ! git remote show origin >/dev/null 2>&1 ; then log_error "Missing git origin 'origin'. Aborting" return 1 fi local tmp tmp=$(mktemp -t ngcpcfg-clean-reset.XXXXXXXXXX) log_debug "git diff origin/master..master" if ! git diff origin/master..master > "${tmp}" ; then log_error "Failed to collect diff of 'origin/master' and 'master' branches" return 1 fi if [ "$(wc -l < "${tmp}")" = "0" ] ; then log_info "OK: No committed and push pending changes found, nothing to clean here." return 0 fi log_info "Found committed local changes to be removed:" cat "${tmp}" log_info "Should we restore branch 'master' from origin to remove local commit?" if ! request_confirmation ; then log_info "Skipping restore branch 'master' from origin as requested." return 0 fi log_info "Restoring branch 'master' from origin:" local new_branch new_branch="ngcpcfg_backup_$(date +%s)" log_debug "git branch -m '${new_branch}'" if ! git branch -m "${new_branch}" ; then log_error "Cannot rename current branch to '${new_branch}'. Aborting" return 1 fi log_info "OK: renamed current branch to '${new_branch}'. Feel free to clean it." log_debug "git checkout -b master --track origin/master" if ! git checkout -b master --track origin/master ; then log_error "Cannot checkout branch 'master'" return 1 fi log_info "OK: created local branch 'master' to track 'origin/master'" return 0 } ## }}} RC=0 branches=false force=false help=false reset_master=false stashes=false tracked_files=false untracked_files=false clean_options="all,branches,force,help,reset-master,stashes,tracked-files,untracked-files" if ! _opt_temp=$(getopt --name "$0" -o +h --long ${clean_options} -- "$@") ; then log_error "Try 'ngcpcfg clean --help' for more information." exit 1 fi eval set -- "${_opt_temp}" [ "$1" = "--" ] && help=true while : ; do case "$1" in --all) branches=true reset_master=true stashes=true tracked_files=true untracked_files=true ;; --branches) branches=true ;; --force) force=true ;; --help) help=true ;; --reset-master) reset_master=true ;; --stashes) stashes=true ;; --tracked-files) tracked_files=true ;; --untracked-files) untracked_files=true ;; --) shift ; break ;; *) log_error "Unknown option '$1'" ; exit 1 ;; esac shift done if ${help} ; then clean_help exit 0 fi if [ ! -f "${FUNCTIONS}/ha_features" ] ; then log_debug "Skip function 'clean_reset_master'. It is for HA installation only." reset_master=false fi if ! clean_ensure_branch_master ; then exit 1 fi if ${tracked_files} ; then clean_tracked || RC=$((RC + $?)) fi if ${untracked_files} ; then clean_untracked || RC=$((RC + $?)) fi if ${stashes} ; then clean_stash || RC=$((RC + $?)) fi if ${reset_master} ; then clean_reset_master || RC=$((RC + $?)) fi if ${branches} ; then clean_old_local_branches || RC=$((RC + $?)) fi if [ "${RC}" = "0" ] ; then log_info "All operations were finished successfully. Good to be clean!" else log_error "Some operations finished with an error, see details above." fi exit "${RC}" ## END OF FILE #################################################################