diff --git a/src/main/bash/sdkman-install.sh b/src/main/bash/sdkman-install.sh index 8c95fee96..3e37a415b 100644 --- a/src/main/bash/sdkman-install.sh +++ b/src/main/bash/sdkman-install.sh @@ -70,12 +70,42 @@ function __sdkman_install_candidate_version() { mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}" rm -rf "${SDKMAN_DIR}/tmp/out" - unzip -oq "${SDKMAN_DIR}/tmp/${candidate}-${version}.zip" -d "${SDKMAN_DIR}/tmp/out" + __sdkman_extract_install_archive "${candidate}" "${version}" mv -f "$SDKMAN_DIR"/tmp/out/* "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" __sdkman_echo_green "Done installing!" echo "" } +function __sdkman_extract_install_archive() { + # function parameters + local -r candidate="$1" + local -r version="$2" + + # copy relevant parts of __sdkman_download + local -r metadata_folder="${SDKMAN_DIR}/var/metadata" + local -r base_name="${candidate}-${version}" + local -r headers_file="${metadata_folder}/${base_name}.headers" + + # understand the archive type + local -r archive_type=$(__sdkman_archive_type "${headers_file}") + local -r archive_file="${SDKMAN_DIR}/tmp/${candidate}-${version}.${archive_type}" + local -r out_dir="${SDKMAN_DIR}/tmp/out" + + # perform extraction + case $archive_type in + zip*) + unzip -oq "${archive_file}" -d "${out_dir}" + ;; + tar*) + (mkdir -p "${out_dir}" && cd "${out_dir}" && tar xvaf "${archive_file}" > /dev/null) + ;; + *) + __sdkman_echo_red "Stop! The archive is of an unknown type: '$archive_type'! Please file an issue on https://github.com/sdkman/sdkman-cli/issues ." + return 1 + ;; + esac +} + function __sdkman_install_local_version() { local candidate version folder version_length version_length_max @@ -122,6 +152,7 @@ function __sdkman_download() { metadata_folder="${SDKMAN_DIR}/var/metadata" mkdir -p "${metadata_folder}" + # beware that the final location of the downloaded file is duplicated in __sdkman_extract_install_archive local platform_parameter="$SDKMAN_PLATFORM" local download_url="${SDKMAN_CANDIDATES_API}/broker/download/${candidate}/${version}/${platform_parameter}" local base_name="${candidate}-${version}" @@ -142,22 +173,62 @@ function __sdkman_download() { grep '^X-Sdkman' "${tmp_headers_file}" > "${headers_file}" __sdkman_echo_debug "Downloaded binary to: ${binary_input} (HTTP headers written to: ${headers_file})" - # post-installation hook: implements function __sdkman_post_installation_hook - # responsible for taking `binary_input` and producing `zip_output` - local post_installation_hook="${SDKMAN_DIR}/tmp/hook_post_${candidate}_${version}.sh" - __sdkman_echo_debug "Get post-installation hook: ${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}" - __sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}" >| "$post_installation_hook" - __sdkman_echo_debug "Copy remote post-installation hook: ${post_installation_hook}" - source "$post_installation_hook" - __sdkman_post_installation_hook || return 1 - __sdkman_echo_debug "Processed binary as: $zip_output" - __sdkman_echo_debug "Completed post-installation hook..." - - __sdkman_validate_zip "${zip_output}" || return 1 - __sdkman_checksum_zip "${zip_output}" "${headers_file}" || return 1 + local archive_type="$(__sdkman_archive_type "${headers_file}")" + local archive_input="${SDKMAN_DIR}/tmp/${base_name}.${archive_type}" + __sdkman_echo_debug "Binary archive type determined to be $archive_type" + # instead of post_installation_hook - simply mv the file + mv "${binary_input}" "${archive_input}" + + __sdkman_validate_archive "${archive_input}" "${archive_type}" || return 1 + __sdkman_checksum_archive "${archive_input}" "${headers_file}" || return 1 echo "" } +function __sdkman_archive_type() { + local -r HEADER='X-Sdkman-ArchiveType' + local -r headers_file="$1" + + # search for the header with a case insensitive match + # then use same sed technique as __sdkman_checksum_archive + grep -i "$HEADER" "$headers_file" | sed -n "s/^$HEADER:\s*\(.*\)$/\1/p" +} + +function __sdkman_validate_archive() { + local -r archive_file="$1" + local -r archive_type="$2" + local test_output + local test_return + local error_text + + case $archive_type in + zip*) + __sdkman_validate_zip "$archive_file" + return $? + ;; + tar*) + # look for the line 'tar: Error is not recoverable: exiting now' + error_text='Error is not recoverable' + test_output=$(tar tf "$archive_file" 2>&1 > /dev/null) + test_return=$? + ;; + *) + __sdkman_echo_red "Stop! The archive is of an unknown type: '$archive_type'! Please file an issue on https://github.com/sdkman/sdkman-cli/issues ." + return 1 + ;; + esac + + # return a failure if the test command failed + if [ 0 != $test_return ]; then + __sdkman_echo_red "Stop! The archive was corrupt (the test for $archive_type archives failed)" + return 1 + fi + + # if there's way to determine if output is a reported failure, + # return a failure if the test command ran successfully, but reported a failure + [ ! -z "${error_text}" ] && echo "${test_output}" | grep -q "${error_text}" && return 1 || return 0 +} + + function __sdkman_validate_zip() { local zip_archive zip_ok @@ -171,7 +242,7 @@ function __sdkman_validate_zip() { fi } -function __sdkman_checksum_zip() { +function __sdkman_checksum_archive() { local -r zip_archive="$1" local -r headers_file="$2" local algorithm checksum cmd @@ -229,4 +300,4 @@ function __sdkman_checksum_zip() { fi fi done < "${headers_file}" -} +} \ No newline at end of file diff --git a/src/test/groovy/sdkman/env/BashEnv.groovy b/src/test/groovy/sdkman/env/BashEnv.groovy index fe66d12c6..f6347e549 100644 --- a/src/test/groovy/sdkman/env/BashEnv.groovy +++ b/src/test/groovy/sdkman/env/BashEnv.groovy @@ -26,7 +26,7 @@ class BashEnv { def commandOutput // Command timeout in milliseconds - def timeout = 5000 + def timeout = 11_000 // must be longer than 10 seconds, default sdkman timeout def workDir def env diff --git a/src/test/groovy/sdkman/stubs/WebServiceStub.groovy b/src/test/groovy/sdkman/stubs/WebServiceStub.groovy index 9a7656009..471903a74 100644 --- a/src/test/groovy/sdkman/stubs/WebServiceStub.groovy +++ b/src/test/groovy/sdkman/stubs/WebServiceStub.groovy @@ -34,9 +34,11 @@ class WebServiceStub { } static primeDownloadFor(String host, String candidate, String version, String platform, Map headers) { - def binary = (candidate == "java") ? "jdk-${version}-${platform}.tar.gz" : "${candidate}-${version}.zip" + def isTar = candidate == "java" + def binary = isTar ? "jdk-${version}-${platform}.tar.gz" : "${candidate}-${version}.zip" def responseBuilder = aResponse() .withHeader("Location", "${host}/${binary}") + .withHeader("X-Sdkman-ArchiveType", isTar ? 'tar' : 'zip') .withStatus(302) headers.each { responseBuilder.withHeader(it.key, it.value) } @@ -57,4 +59,4 @@ class WebServiceStub { .withHeader("Content-Type", "text/plain") .withBodyFile("selfupdate.sh"))) } -} +} \ No newline at end of file diff --git a/src/test/resources/features/hooks.feature b/src/test/resources/features/hooks.feature deleted file mode 100644 index fd47f9bd4..000000000 --- a/src/test/resources/features/hooks.feature +++ /dev/null @@ -1,25 +0,0 @@ -Feature: Hooks - - We can safely remove this feature when `.tar.gz` and `.zip` are supported directly by the backend. - - Background: - Given the internet is reachable - And an initialised environment - - Scenario: Post-installation Hook returns successfully - And an "x86_64" machine with "Linux" installed - And the system is bootstrapped - And the candidate "grails" version "2.1.0" is available for download on "Linux" with architecture "x86_64" - And a post-installation hook is served for "grails" "2.1.0" on "Linux" with architecture "x86_64" that returns successfully - When I enter "sdk install grails 2.1.0" - And I see "Post-installation hook success" - And the exit code is 0 - - Scenario: Post-install Hook returns a non-zero code - And an "x86_64" machine with "Linux" installed - And the system is bootstrapped - And the candidate "grails" version "2.1.0" is available for download on "Linux" with architecture "x86_64" - And a post-installation hook is served for "grails" "2.1.0" on "Linux" with architecture "x86_64" that returns a failure - When I enter "sdk install grails 2.1.0" - Then I see "Post-installation hook failure" - And the exit code is 1 diff --git a/src/test/resources/features/java_installation.feature b/src/test/resources/features/java_installation.feature index dd7bdc4fe..411a748ec 100644 --- a/src/test/resources/features/java_installation.feature +++ b/src/test/resources/features/java_installation.feature @@ -30,16 +30,6 @@ Feature: Java Multi Platform Binary Distribution And I see "Done installing!" And the candidate "java" version "8.0.111" is installed - Scenario: Platform is supported but download fails - Given an "x86_64" machine with "Linux" installed - And the system is bootstrapped - And the candidate "java" version "8.0.101" is available for download on "Linux" with architecture "x86_64" - And the appropriate multi-platform hook is available for "java" version "8.0.101" on "Linux" with architecture "x86_64" - When I enter "sdk install java 8.0.101" - And I see "Download has failed, aborting!" - And the candidate "java" version "8.0.101" is not installed - And I see "Cannot install java 8.0.101 at this time..." - Scenario: Platform is not supported for specific version and user is notified And an "x86_64" machine with "Linux" installed And the system is bootstrapped