diff --git a/go-template b/go-template index fac1110..1bf570f 100755 --- a/go-template +++ b/go-template @@ -6,9 +6,9 @@ # description. You may remove any other comments from this template as well. # # This template automatically checks for the presence of the go-script-bash -# sources and makes a shallow clone of the the go-script-bash repository if -# necessary before dispatching commands. (If you prefer, you can change the -# logic to create a regular clone instead.) This allows users to set up the +# sources and downloads the go-script-bash repository contents if necessary +# before dispatching commands. (If you prefer, you can change the logic to +# create a shallow or regular clone instead.) This allows users to set up the # framework without taking any extra steps when running the command for the # first time, without the need to commit the framework to your repository. # @@ -35,7 +35,10 @@ export _GO_STANDALONE= declare GO_SCRIPTS_DIR="${GO_SCRIPTS_DIR:-scripts}" # The `GO_SCRIPT_BASH_REPO_URL` tag or branch you wish to use -declare GO_SCRIPT_BASH_VERSION="${GO_SCRIPT_BASH_VERSION:-v1.4.0}" +declare GO_SCRIPT_BASH_VERSION="${GO_SCRIPT_BASH_VERSION:-v1.5.0}" + +# The target version string, removing the leading 'v' +declare _GO_SCRIPT_BASH_VERSION_NUMBER="${GO_SCRIPT_BASH_VERSION:1}" # The go-script-bash installation directory within your project declare GO_SCRIPT_BASH_CORE_DIR="${GO_SCRIPT_BASH_CORE_DIR:-${0%/*}/$GO_SCRIPTS_DIR/go-script-bash}" @@ -43,15 +46,56 @@ declare GO_SCRIPT_BASH_CORE_DIR="${GO_SCRIPT_BASH_CORE_DIR:-${0%/*}/$GO_SCRIPTS_ # The URL of the go-script-bash framework sources declare GO_SCRIPT_BASH_REPO_URL="${GO_SCRIPT_BASH_REPO_URL:-https://github.com/mbland/go-script-bash.git}" +# URL with the release files +declare GO_SCRIPT_BASH_DOWNLOAD_URL="${GO_SCRIPT_BASH_DOWNLOAD_URL:-${GO_SCRIPT_BASH_REPO_URL%.git}/archive}/$GO_SCRIPT_BASH_VERSION.tar.gz" + if [[ ! -e "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" ]]; then - printf "Cloning framework from '%s'...\n" "$GO_SCRIPT_BASH_REPO_URL" - if ! git clone --depth 1 -c advice.detachedHead=false \ - -b "$GO_SCRIPT_BASH_VERSION" "$GO_SCRIPT_BASH_REPO_URL" \ - "$GO_SCRIPT_BASH_CORE_DIR"; then - printf "Failed to clone '%s'; aborting.\n" "$GO_SCRIPT_BASH_REPO_URL" >&2 - exit 1 + declare PIPEFAIL_BACKUP + PIPEFAIL_BACKUP=$(shopt -op | grep pipefail) + set -o pipefail + + # Using a function to allow for multiple return points + curl_download(){ + if ! command curl -V >/dev/null; then + printf "Failed to find cURL or tar\n" + return 1 + fi + if ! command tar --help >/dev/null; then + printf "Failed to find cURL or tar\n" + return 1 + fi + printf "Downloading framework from '%s'...\n" "$GO_SCRIPT_BASH_DOWNLOAD_URL" + if ! curl -LfsS "$GO_SCRIPT_BASH_DOWNLOAD_URL" | tar -xz 2>/dev/null ; then + printf "Failed to download from '%s'.\n" "$GO_SCRIPT_BASH_DOWNLOAD_URL" >&2 + return 1 + fi + if ! mkdir -p $GO_SCRIPTS_DIR ; then + printf "Failed to create scripts dir '%s'\n" $GO_SCRIPTS_DIR >&2 + rm -rf go-script-bash-$_GO_SCRIPT_BASH_VERSION_NUMBER + return 1 + fi + if ! mv go-script-bash-$_GO_SCRIPT_BASH_VERSION_NUMBER $GO_SCRIPT_BASH_CORE_DIR; then + printf "Failed to install downloaded directory in '%s'\n" $GO_SCRIPT_BASH_CORE_DIR >&2 + rm -rf go-script-bash-$_GO_SCRIPT_BASH_VERSION_NUMBER + return 1 + fi + printf "Download of '%s' successful.\n\n" "$GO_SCRIPT_BASH_DOWNLOAD_URL" + return 0 + } + + if ! curl_download; then + printf "Using git clone as fallback\n" + printf "Cloning framework from '%s'...\n" "$GO_SCRIPT_BASH_REPO_URL" + if ! git clone --depth 1 -c advice.detachedHead=false \ + -b "$GO_SCRIPT_BASH_VERSION" "$GO_SCRIPT_BASH_REPO_URL" \ + "$GO_SCRIPT_BASH_CORE_DIR"; then + printf "Failed to clone '%s'; aborting.\n" "$GO_SCRIPT_BASH_REPO_URL" >&2 + $PIPEFAIL_BACKUP + exit 1 + fi + printf "Clone of '%s' successful.\n\n" "$GO_SCRIPT_BASH_REPO_URL" fi - printf "Clone of '%s' successful.\n\n" "$GO_SCRIPT_BASH_REPO_URL" + $PIPEFAIL_BACKUP fi . "$GO_SCRIPT_BASH_CORE_DIR/go-core.bash" "$GO_SCRIPTS_DIR" diff --git a/tests/template.bats b/tests/template.bats index ce2bf4e..ce783d1 100644 --- a/tests/template.bats +++ b/tests/template.bats @@ -2,31 +2,17 @@ load environment -# By default, the test will try to clone its own repo to avoid flakiness due to -# an external dependency. However, doing so causes a failure on Travis, since it -# uses shallow clones to produce test runs, resulting in the error: -# -# fatal: attempt to fetch/clone from a shallow repository -# # However, since Travis already depends on having a good connection to GitHub, # we'll use the real URL. Alternatively, `git` could be stubbed out via # `stub_program_in_path` from `_GO_CORE_DIR/lib/bats/helpers`, but the potential # for neither flakiness nor complexity seems that great, and this approach -# provides extra confidence that the mechanism works as advertised. -# -# A developer can also run the test locally against the real URL by setting -# `TEST_USE_REAL_URL` on the command line. The value of `GO_CORE_URL` is -# subsequently displayed in the name of the test case to validate which repo is -# being used during the test run. -TEST_USE_REAL_URL="${TEST_USE_REAL_URL:-$TRAVIS}" -GO_CORE_URL="${TEST_USE_REAL_URL:+$_GO_CORE_URL}" -GO_CORE_URL="${GO_CORE_URL:-$_GO_CORE_DIR}" setup() { test_filter export GO_SCRIPT_BASH_VERSION="$_GO_CORE_VERSION" export GO_SCRIPTS_DIR="$_GO_TEST_DIR/tmp/go-template-test-scripts" - export GO_SCRIPT_BASH_REPO_URL="$GO_CORE_URL" + export GO_SCRIPT_BASH_REPO_URL="https://github.com/mbland/go-script-bash.git" + export GO_SCRIPT_BASH_DOWNLOAD_URL="${GO_SCRIPT_BASH_REPO_URL%.git}/archive" } teardown() { @@ -40,21 +26,65 @@ teardown() { assert_output_matches "Usage: $_GO_CORE_DIR/go-template " } -@test "$SUITE: clone the go-script-bash repository from $GO_CORE_URL" { - if [[ -e "$GO_CORE_URL/.git/shallow" ]]; then - skip "Can't clone shallow repositories" - fi +@test "$SUITE: download the go-script-bash release from $GO_SCRIPT_BASH_REPO_URL" { run "$_GO_CORE_DIR/go-template" # Without a command argument, the script will print the top-level help and # return an error, but the core repo should exist as expected. assert_failure - assert_output_matches "Cloning framework from '$GO_CORE_URL'\.\.\." + assert_output_matches "Downloading framework from '${GO_SCRIPT_BASH_REPO_URL%.git}.*.tar.gz'\.\.\." + + # Use `.*/scripts/go-script-bash` to account for the fact that `git clone` on + # MSYS2 will output `C:/Users//AppData/Local/Temp/` in place of `/tmp`. + assert_output_matches "Download of '${GO_SCRIPT_BASH_REPO_URL%.git}.*.tar.gz' successful." + assert_output_matches "Usage: $_GO_CORE_DIR/go-template " + [[ -f "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash/go-core.bash" ]] + +} + +@test "$SUITE: fail to download a nonexistent repo" { + GO_SCRIPT_BASH_REPO_URL='bogus-repo-that-does-not-exist' \ + GO_SCRIPT_BASH_DOWNLOAD_URL='bogus-url-that-does-not-exist' \ + run "$_GO_CORE_DIR/go-template" + assert_failure "Downloading framework from 'bogus-url-that-does-not-exist/$GO_SCRIPT_BASH_VERSION.tar.gz'..." \ + "curl: (6) Could not resolve host: bogus-url-that-does-not-exist" \ + "Failed to download from 'bogus-url-that-does-not-exist/$GO_SCRIPT_BASH_VERSION.tar.gz'." \ + "Using git clone as fallback" \ + "Cloning framework from 'bogus-repo-that-does-not-exist'..." \ + "fatal: repository 'bogus-repo-that-does-not-exist' does not exist" \ + "Failed to clone 'bogus-repo-that-does-not-exist'; aborting." +} + +@test "$SUITE: fail to download a nonexistent version" { + GO_SCRIPT_BASH_VERSION='vnonexistent' \ + run "$_GO_CORE_DIR/go-template" + assert_failure "Downloading framework from 'https://github.com/mbland/go-script-bash/archive/vnonexistent.tar.gz'..." \ + "curl: (22) The requested URL returned error: 404 Not Found" \ + "Failed to download from 'https://github.com/mbland/go-script-bash/archive/vnonexistent.tar.gz'." \ + "Using git clone as fallback" \ + "Cloning framework from 'https://github.com/mbland/go-script-bash.git'..." \ + "Cloning into '$PWD/$GO_SCRIPTS_DIR/go-script-bash'..." \ + "warning: Could not find remote branch vnonexistent to clone." \ + "fatal: Remote branch vnonexistent not found in upstream origin" \ + "Failed to clone 'https://github.com/mbland/go-script-bash.git'; aborting." +} + +@test "$SUITE: fail to find curl uses git clone" { + PATH="$BATS_TEST_BINDIR:$PATH" + stub_program_in_path curl "exit 1" + run "$_GO_CORE_DIR/go-template" + restore_program_in_path curl + + # Without a command argument, the script will print the top-level help and + # return an error, but the core repo should exist as expected. + assert_output_matches "Failed to find cURL or tar" + assert_output_matches "Using git clone as fallback" + assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'\.\.\." # Use `.*/scripts/go-script-bash` to account for the fact that `git clone` on # MSYS2 will output `C:/Users//AppData/Local/Temp/` in place of `/tmp`. assert_output_matches "Cloning into '.*/$GO_SCRIPTS_DIR/go-script-bash'\.\.\." - assert_output_matches "Clone of '$GO_CORE_URL' successful\."$'\n\n' + assert_output_matches "Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n' assert_output_matches "Usage: $_GO_CORE_DIR/go-template " [[ -f "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash/go-core.bash" ]] @@ -64,10 +94,79 @@ teardown() { assert_output_matches "go-script-bash $_GO_CORE_VERSION" } -@test "$SUITE: fail to clone a nonexistent repo" { - GO_SCRIPT_BASH_REPO_URL='bogus-repo-that-does-not-exist' \ - run "$_GO_CORE_DIR/go-template" - assert_failure "Cloning framework from 'bogus-repo-that-does-not-exist'..." \ - "fatal: repository 'bogus-repo-that-does-not-exist' does not exist" \ - "Failed to clone 'bogus-repo-that-does-not-exist'; aborting." +@test "$SUITE: fail to find tar uses git clone" { + PATH="$BATS_TEST_BINDIR:$PATH" + stub_program_in_path tar "exit 1" + run "$_GO_CORE_DIR/go-template" + restore_program_in_path tar + + # Without a command argument, the script will print the top-level help and + # return an error, but the core repo should exist as expected. + assert_output_matches "Failed to find cURL or tar" + assert_output_matches "Using git clone as fallback" + assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'\.\.\." + + # Use `.*/scripts/go-script-bash` to account for the fact that `git clone` on + # MSYS2 will output `C:/Users//AppData/Local/Temp/` in place of `/tmp`. + assert_output_matches "Cloning into '.*/$GO_SCRIPTS_DIR/go-script-bash'\.\.\." + assert_output_matches "Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n' + assert_output_matches "Usage: $_GO_CORE_DIR/go-template " + [[ -f "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash/go-core.bash" ]] + + cd "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash" + run git log --oneline -n 1 + assert_success + assert_output_matches "go-script-bash $_GO_CORE_VERSION" +} + +@test "$SUITE: fail to create directory uses git clone" { + PATH="$BATS_TEST_BINDIR:$PATH" + stub_program_in_path mkdir "exit 1" + run "$_GO_CORE_DIR/go-template" + restore_program_in_path mkdir + + # Without a command argument, the script will print the top-level help and + # return an error, but the core repo should exist as expected. + assert_output_matches "Downloading framework from '${GO_SCRIPT_BASH_REPO_URL%.git}.*.tar.gz'\.\.\." + assert_output_matches "Failed to create scripts dir '$GO_SCRIPTS_DIR'" + assert_output_matches "Using git clone as fallback" + assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'\.\.\." + + # Use `.*/scripts/go-script-bash` to account for the fact that `git clone` on + # MSYS2 will output `C:/Users//AppData/Local/Temp/` in place of `/tmp`. + assert_output_matches "Cloning into '.*/$GO_SCRIPTS_DIR/go-script-bash'\.\.\." + assert_output_matches "Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n' + assert_output_matches "Usage: $_GO_CORE_DIR/go-template " + [[ -f "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash/go-core.bash" ]] + + cd "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash" + run git log --oneline -n 1 + assert_success + assert_output_matches "go-script-bash $_GO_CORE_VERSION" } + +@test "$SUITE: fail to move extracted directory uses git clone" { + PATH="$BATS_TEST_BINDIR:$PATH" + stub_program_in_path mv "exit 1" + run "$_GO_CORE_DIR/go-template" + restore_program_in_path mv + + # Without a command argument, the script will print the top-level help and + # return an error, but the core repo should exist as expected. + assert_output_matches "Downloading framework from '${GO_SCRIPT_BASH_REPO_URL%.git}.*.tar.gz'\.\.\." + assert_output_matches "Failed to install downloaded directory in '.*/$GO_SCRIPTS_DIR/go-script-bash'" + assert_output_matches "Using git clone as fallback" + assert_output_matches "Cloning framework from '$GO_SCRIPT_BASH_REPO_URL'\.\.\." + + # Use `.*/scripts/go-script-bash` to account for the fact that `git clone` on + # MSYS2 will output `C:/Users//AppData/Local/Temp/` in place of `/tmp`. + assert_output_matches "Cloning into '.*/$GO_SCRIPTS_DIR/go-script-bash'\.\.\." + assert_output_matches "Clone of '$GO_SCRIPT_BASH_REPO_URL' successful\."$'\n\n' + assert_output_matches "Usage: $_GO_CORE_DIR/go-template " + [[ -f "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash/go-core.bash" ]] + + cd "$_GO_ROOTDIR/$GO_SCRIPTS_DIR/go-script-bash" + run git log --oneline -n 1 + assert_success + assert_output_matches "go-script-bash $_GO_CORE_VERSION" +} \ No newline at end of file