#!/bin/sh # Copyright (c) 2000-2023, Board of Trustees of Leland Stanford Jr. University # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # Version # VERSION='1.4.0' # # Program name # _0='lockss-downloader' # # Constants # DEFAULT_GIT_BRANCH='master' DEFAULT_GIT_PROJECT='https://github.com/lockss/lockss-installer' # # Fatal # $1: optional message # Fatal() { if [ -n "${1}" ] ; then echo "${_0}: error: ${1}" >&2 fi exit 1 } # # Graceful # Graceful() { exit 0 } # # Check_Root # Check_Root() { if [ "$(id -u)" = '0' ] ; then Fatal 'cannot be run as root' fi } # # Create_Download_Dir # Create_Download_Dir() { if [ -d "${DOWNLOAD_DIR}" ] ; then return fi if [ -e "${DOWNLOAD_DIR}" ] ; then Fatal "${DOWNLOAD_DIR} already exists but is not a directory" fi mkdir -p "${DOWNLOAD_DIR}" || Fatal "could not create ${DOWNLOAD_DIR}" } # # Display_Summary # Display_Summary() { if [ "${QUIET}" != 'true' ] ; then case "${GIT_BRANCH}${GIT_COMMIT}${GIT_TAG}" in "${GIT_BRANCH}") _s="branch: ${GIT_BRANCH}" ;; "${GIT_COMMIT}") _s="commit: ${GIT_COMMIT}" ;; "${GIT_TAG}") _s="tag: ${GIT_TAG}" ;; *) Fatal 'invalid Git state' ;; # shouldn't happen esac echo "https://github.com/${GIT_PROJECT} downloaded into ${DOWNLOAD_DIR} (${_s})" fi } # # Download_From_Github # Download_From_Github() { case "${GIT_BRANCH}${GIT_COMMIT}${GIT_TAG}" in "${GIT_BRANCH}") _url="https://github.com/${GIT_PROJECT}/archive/refs/heads/${GIT_BRANCH}" ;; "${GIT_COMMIT}") _url="https://github.com/${GIT_PROJECT}/archive/${GIT_COMMIT}" ;; "${GIT_TAG}") _url="https://github.com/${GIT_PROJECT}/archive/refs/tags/${GIT_TAG}" ;; *) Fatal 'unknown Git reference' ;; # shouldn't happen esac case "${ARCHIVER}" in gtar|tar) _url="${_url}.tar.gz" ;; *) Fatal "unknown archiver: ${ARCHIVER}" ;; # shouldn't happen esac ( case "${DOWNLOADER}" in curl) curl -sSfL "${_url}" ;; wget) wget -qO- "${_url}" ;; http) http -qd "${_url}" ;; *) Fatal "unknown downloader: ${DOWNLOADER}" ;; # shouldn't happen esac \ | case "${ARCHIVER}" in gtar|tar) "$ARCHIVER" xzf - --directory="${DOWNLOAD_DIR}" --strip-components=1 ;; *) Fatal "unknown archiver: ${ARCHIVER}" ;; # shouldn't happen ;; esac ) || Fatal "could not download and extract ${_url}" } # # Print_Help # Print_Help() { cat < where is derived from the GitHub project reference https://github.com/X/Y) Fetch Options: --curl, -curl, -C force the use of Curl --httpie, -httpie, -H force the use of HTTPie --wget, -wget, -W force the use of Wget MyEOF } # # Parse_Command_Line # Must be called with "$@" # Parse_Command_Line() { while [ $# -gt 0 ] ; do case "${1}" in --curl|-curl|-C) CURL='true' ; shift ;; --download-dir=*) Set_Download_Dir "${1#*=}" ; shift ;; --download-dir|-download-dir|-d) Set_Download_Dir "${2}" ; shift ; shift ;; --git-branch=*) Set_Git_Branch "${1#*=}" ; shift ;; --git-branch|-git-branch|-b) Set_Git_Branch "${2}" ; shift ; shift ;; --git-commit=*) Set_Git_Commit "${1#*=}" ; shift ;; --git-commit|-git-commit|-c) Set_Git_Commit "${2}" ; shift ; shift ;; --git-tag=*) Set_Git_Tag "${1#*=}" ; shift ;; --git-tag|-git-tag|-t) Set_Git_Tag "${2}" ; shift ; shift ;; --help|-help|-h) Print_Help ; Graceful ;; --httpie|-httpie|-H) HTTPIE='true' ; shift ;; --quiet|-quiet|-q) QUIET='true' ; shift ;; --version|-version) echo "${VERSION}" ; Graceful ;; --wget|-wget|-W) WGET='true' ; shift ;; -*) Fatal "unknown option: ${1}" ;; *) Set_Git_Project "${1}" ; shift ;; esac done case "${CURL}${HTTPIE}${WGET}" in ''|"${CURL}"|"${HTTPIE}"|"${WGET}") ;; *) Fatal 'at most one of --curl, --httpie, --wget can be used' ;; esac case "${GIT_BRANCH}${GIT_COMMIT}${GIT_TAG}" in '') GIT_BRANCH="${DEFAULT_GIT_BRANCH}" ;; "${GIT_BRANCH}"|"${GIT_COMMIT}"|"${GIT_TAG}") ;; *) Fatal 'at most one of --git-branch, --git-commit, --git-tag can be used' ;; esac # Must be before DOWNLOAD_DIR section if [ -z "${GIT_PROJECT}" ] ; then Set_Git_Project "${DEFAULT_GIT_PROJECT}" fi if [ -z "${DOWNLOAD_DIR}" ] ; then Set_Download_Dir "${HOME}/$(basename "${GIT_PROJECT}")" fi } # # Set_Archiver # Set_Archiver() { if [ "$(uname -s)" = 'Darwin' ] && command -v gtar > /dev/null ; then ARCHIVER='gtar' elif command -v tar > /dev/null ; then ARCHIVER='tar' fi if [ -z "${ARCHIVER}" ] ; then Fatal 'no tar (or gtar) on the PATH' fi } # # Set_Download_Dir # $1: directory path # Set_Download_Dir() { if [ -z "${1}" ] ; then Fatal '--download-dir: empty directory path' fi if [ -z "${DOWNLOAD_DIR}" ] ; then DOWNLOAD_DIR="${1}" elif [ "${1}" != "${DOWNLOAD_DIR}" ] ; then Fatal "--download-dir: ${1} conflicts with ${DOWNLOAD_DIR}" fi } # # Set_Downloader # $1: executable # Set_Downloader() { case "${CURL}${HTTPIE}${WGET}" in '') for _d in curl wget http ; do if command -v "${_d}" > /dev/null ; then DOWNLOADER="${_d}" break fi done if [ -z "${DOWNLOADER}" ] ; then Fatal 'at least one of curl, wget, http must be on the PATH' fi ;; "${CURL}") if command -v curl > /dev/null ; then DOWNLOADER='curl' else Fatal '--curl: curl must be on the PATH' fi ;; "${HTTPIE}") if command -v http > /dev/null ; then DOWNLOADER='http' else Fatal '--httpie: http must be on the PATH' fi ;; "${WGET}") if command -v wget > /dev/null ; then DOWNLOADER='wget' else Fatal '--wget: wget must be on the PATH' fi ;; *) Fatal 'invalid downloader' ;; # shouldn't happen esac } # # Set_Git_Branch # $1: branch name # Set_Git_Branch() { if [ -z "${1}" ] ; then Fatal '--git-branch: empty branch name' fi if [ -z "${GIT_BRANCH}" ] ; then GIT_BRANCH="${1}" elif [ "${1}" != "${GIT_BRANCH}" ] ; then Fatal "--git-branch: ${1} conflicts with ${GIT_BRANCH}" fi } # # Set_Git_Commit # $1: commit name # Set_Git_Commit() { if [ -z "${1}" ] ; then Fatal '--git-commit: empty commit name' fi if [ -z "${GIT_COMMIT}" ] ; then GIT_COMMIT="${1}" elif [ "${1}" != "${GIT_COMMIT}" ] ; then Fatal "--git-commit: ${1} conflicts with ${GIT_COMMIT}" fi } # # Set_Git_Project # $1: project reference (foo/bar, https://github.com/foo/bar, https://github.com/foo/bar.git, git@github.com:foo/bar, git@github.com:foo/bar.git) # Set_Git_Project() { if [ -z "${1}" ] ; then Fatal 'empty project reference' fi case "${1}" in https://github.com/*/*) _proj="${1%.git}" ; _proj="${_proj#https://github.com/}" ;; git@github.com:*/*) _proj="${1%.git}" ; _proj="${_proj#git@github.com:}" ;; */*) _proj="${1}" ;; *) Fatal "invalid project reference: ${1}" ;; esac if [ "${_proj%/*}/${_proj#*/}" != "${_proj}" ] ; then Fatal "invalid project reference: ${1}" fi if [ -z "${GIT_PROJECT}" ] ; then GIT_PROJECT="${_proj}" elif [ "${_proj}" != "${GIT_PROJECT}" ] ; then Fatal "${_proj} conflicts with ${GIT_PROJECT}" fi } # # Set_Git_Tag # $1: tag name # Set_Git_Tag() { if [ -z "${1}" ] ; then Fatal '--git-tag: empty tag name' fi if [ -z "${GIT_TAG}" ] ; then GIT_TAG="${1}" elif [ "${1}" != "${GIT_TAG}" ] ; then Fatal "--git-tag: ${1} conflicts with ${GIT_TAG}" fi } # # Variables # ARCHIVER='' CURL='' DOWNLOAD_DIR='' DOWNLOADER='' GIT_BRANCH='' GIT_COMMIT='' GIT_PROJECT='' GIT_TAG='' HTTPIE='' QUIET='' WGET='' # # Main # Parse_Command_Line "$@" Check_Root Set_Downloader Set_Archiver Create_Download_Dir Download_From_Github Display_Summary Graceful