X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=dotfiles.sh;h=d838e60a38df79987ac4d1040f866c474073d43b;hb=193ed92127b56c8536ee269c47657c20e38f54ff;hp=45c3f974f38986b54868e53e96e0a639c1be0e7b;hpb=13c41a039fc5d3d537e4a2356c886660fad5a7b0;p=dotfiles-framework.git diff --git a/dotfiles.sh b/dotfiles.sh index 45c3f97..d838e60 100755 --- a/dotfiles.sh +++ b/dotfiles.sh @@ -23,6 +23,19 @@ TAR=$(which tar) TOUCH=$(which touch) WGET=$(which wget) +##### +# Compatibility checks + +BASH="${BASH_VERSION%.*}" +BASH_MAJOR="${BASH%.*}" +BASH_MINOR="${BASH#*.}" + +if [ "${BASH_MAJOR}" -eq 3 ] && [ "${BASH_MINOR}" -eq 0 ]; then + echo "ERROR: ${0} requires Bash version >= 3.1" >&2 + echo "you're running ${BASH}, which doesn't support += array assignment" >&2 + exit 1 +fi + ##### # Utility functions @@ -76,12 +89,15 @@ function get_selection() function run_on_all_repos() { COMMAND="${1}" + shift if [ -z "${REPO}" ]; then # run on all repositories for REPO in *; do if [ "${REPO}" = '*' ]; then break # no known repositories + elif [ -f "${REPO}" ]; then + continue # repositories are directories fi - "${COMMAND}" "${REPO}" || return 1 + "${COMMAND}" "${@}" "${REPO}" || return 1 done return fi @@ -100,10 +116,18 @@ function list_files() } # Global variable to allow passing associative arrats between functions -declare -A REPO_SOURCE_DATA + +if [ "${BASH_MAJOR}" -ge 4 ]; then + declare -A REPO_SOURCE_DATA +fi function set_repo_source() { + if [ "${BASH_MAJOR}" -lt 4 ]; then + echo "ERROR: ${0}'s set_repo_source requires Bash version >= 4.0" >&2 + echo "you're running ${BASH}, which doesn't support associative arrays" >&2 + return 1 + fi REPO=$(nonempty_option 'set_repo_source' 'REPO' "${1}") || return 1 > "${REPO}/source_cache" || return 1 for KEY in "${!REPO_SOURCE_DATA[@]}"; do @@ -114,6 +138,11 @@ function set_repo_source() # usage: get_repo_source REPO function get_repo_source() { + if [ "${BASH_MAJOR}" -lt 4 ]; then + echo "ERROR: ${0}'s get_repo_source() requires Bash version >= 4.0" >&2 + echo "you're running ${BASH}, which doesn't support associative arrays" >&2 + return 1 + fi REPO=$(nonempty_option 'get_repo_source' 'REPO' "${1}") || return 1 REPO_SOURCE_DATA=() if [ -f "${REPO}/source_cache" ]; then @@ -136,6 +165,17 @@ function get_repo_source() fi } +function git_fetch() +{ + REPO=$(nonempty_option 'git_fetch' 'REPO' "${1}") || return 1 + REMOTES=$(cd "${REPO}" && "${GIT}" remote) || return 1 + if [ -n "${REMOTES}" ]; then + (cd "${REPO}" && "${GIT}" pull) || return 1 + else + echo "no remote repositories found for ${REPO}" + fi +} + function wget_fetch() { REPO=$(nonempty_option 'wget_fetch' 'REPO' "${1}") || return 1 @@ -161,11 +201,9 @@ function wget_fetch() if [ -n "${SERVER_ETAG}" ]; then # store new ETag REPO_SOURCE_DATA['etag']="${SERVER_ETAG}" set_repo_source "${REPO}" || return 1 - else - if [ -n "${ETAG}" ]; then # clear old ETag - unset "${REPO_SOURCE_DATA['etag']}" - set_repo_source "${REPO}" || return 1 - fi + elif [ -n "${ETAG}" ]; then # clear old ETag + unset "${REPO_SOURCE_DATA['etag']}" + set_repo_source "${REPO}" || return 1 fi echo "extracting ${BUNDLE} to ${REPO}" "${TAR}" -xf "${BUNDLE}" -C "${REPO}" --strip-components 1 --overwrite || return 1 @@ -175,6 +213,7 @@ function wget_fetch() fi } + # usage: link_file REPO FILE # # Create the symbolic link to the version of FILE in the REPO @@ -253,7 +292,6 @@ function clone() echo "ERROR: destination path (${REPO}) already exists." >&2 return 1 fi - mkdir -p "${REPO}" CACHE_SOURCE='yes' FETCH='yes' case "${TRANSFER}" in @@ -263,6 +301,7 @@ function clone() "${GIT}" clone "${URL}" "${REPO}" || return 1 ;; 'wget') + mkdir -p "${REPO}" ;; *) echo "PROGRAMMING ERROR: add ${TRANSFER} support to clone command" >&2 @@ -301,10 +340,15 @@ function fetch() # multi-repo case handled in main() by run_on_all_repos() REPO=$(nonempty_option 'fetch' 'REPO' "${1}") || return 1 maxargs 'fetch' 1 "${@}" || return 1 - get_repo_source "${REPO}" || return 1 - TRANSFER=$(nonempty_option 'fetch' 'TRANSFER' "${REPO_SOURCE_DATA['transfer']}") || return 1 + if [ "${BASH_MAJOR}" -ge 4 ]; then + get_repo_source "${REPO}" || return 1 + TRANSFER=$(nonempty_option 'fetch' 'TRANSFER' "${REPO_SOURCE_DATA['transfer']}") || return 1 + else + echo "WARNING: Bash version < 4.0, assuming all repos use git transfer" >&2 + TRANSFER='git' + fi if [ "${TRANSFER}" = 'git' ]; then - "${GIT}" --git-dir "${REPO}/.git" pull || return 1 + git_fetch "${REPO}" || return 1 elif [ "${TRANSFER}" = 'wget' ]; then wget_fetch "${REPO}" || return 1 else @@ -348,17 +392,17 @@ function diff() { MODE='standard' while [ "${1::2}" = '--' ]; do - case "${1}" in - '--removed') - MODE='removed' - ;; - '--local-patch') - MODE='local-patch' - ;; - *) - echo "ERROR: invalid option to diff (${1})" >&2 - return 1 - esac + case "${1}" in + '--removed') + MODE='removed' + ;; + '--local-patch') + MODE='local-patch' + ;; + *) + echo "ERROR: invalid option to diff (${1})" >&2 + return 1 + esac shift done # multi-repo case handled in main() by run_on_all_repos() @@ -378,7 +422,7 @@ function diff() exec 3<&1 # save stdout to file descriptor 3 echo "save local removed to ${REPO}/local-patch/000-local.remove" exec 1>"${REPO}/local-patch/000-local.remove" # redirect stdout - diff "${REPO}" --removed + diff --removed "${REPO}" exec 1<&3 # restore old stdout exec 3<&- # close temporary fd 3 return @@ -389,10 +433,8 @@ function diff() if [ ! -e "${TARGET}/${FILE}" ]; then echo "${FILE}" fi - else - if [ -f "${TARGET}/${FILE}" ]; then - (cd "${REPO}/src" && "${DIFF}" -u "${FILE}" "${TARGET}/${FILE}") - fi + elif [ -f "${TARGET}/${FILE}" ]; then + (cd "${REPO}/src" && "${DIFF}" -u "${FILE}" "${TARGET}/${FILE}") fi done <<-EOF $(list_files "${REPO}/src") @@ -432,7 +474,7 @@ function patch() if [ -f "${FILE}" ]; then echo "apply ${FILE}" pushd "${REPO}/patched-src/" > /dev/null || return 1 - "${PATCH}" -p0 < "../../${FILE}" || return 1 + "${PATCH}" -p1 < "../../${FILE}" || return 1 popd > /dev/null || return 1 fi done @@ -440,6 +482,7 @@ function patch() # remove any files marked for removal in local-patch for REMOVE in "${REPO}/local-patch"/*.remove; do if [ -f "${REMOVE}" ]; then + echo "apply ${FILE}" while read LINE; do if [ -z "${LINE}" ] || [ "${LINE:0:1}" = '#' ]; then continue # ignore blank lines and comments @@ -483,23 +526,23 @@ function link() DRY_RUN='no' # If 'yes', disable any actions that change the filesystem BACKUP='yes' while [ "${1::2}" = '--' ]; do - case "${1}" in - '--force') - FORCE='yes' - ;; - '--force-file') - FORCE='file' - ;; - '--dry-run') - DRY_RUN='yes' - ;; - '--no-backup') - BACKUP='no' - ;; - *) - echo "ERROR: invalid option to link (${1})" >&2 - return 1 - esac + case "${1}" in + '--force') + FORCE='yes' + ;; + '--force-file') + FORCE='file' + ;; + '--dry-run') + DRY_RUN='yes' + ;; + '--no-backup') + BACKUP='no' + ;; + *) + echo "ERROR: invalid option to link (${1})" >&2 + return 1 + esac shift done # multi-repo case handled in main() by run_on_all_repos() @@ -689,27 +732,27 @@ function main() { COMMAND='' while [ "${1::2}" = '--' ]; do - case "${1}" in - '--help') - main_help || return 1 - return - ;; - '--version') - echo "${VERSION}" - return - ;; - '--dotfiles-dir') - DOTFILES_DIR="${2}" - shift - ;; - '--target') - TARGET="${2}" - shift - ;; - *) - echo "ERROR: invalid option to ${0} (${1})" >&2 - return 1 - esac + case "${1}" in + '--help') + main_help || return 1 + return + ;; + '--version') + echo "${VERSION}" + return + ;; + '--dotfiles-dir') + DOTFILES_DIR="${2}" + shift + ;; + '--target') + TARGET="${2}" + shift + ;; + *) + echo "ERROR: invalid option to ${0} (${1})" >&2 + return 1 + esac shift done COMMAND=$(get_selection "${1}" "${COMMANDS[@]}") || return 1 @@ -728,7 +771,7 @@ function main() shift done if [ "${#}" -eq 0 ]; then - run_on_all_repos "${COMMAND}" "$OPTIONS[@]" || return 1 + run_on_all_repos "${COMMAND}" "${OPTIONS[@]}" || return 1 else maxargs "${0}" 1 "${@}" || return 1 "${COMMAND}" "${OPTIONS[@]}" "${1}" || return 1