mirror of
https://github.com/actions/setup-dotnet.git
synced 2024-11-26 05:13:05 +07:00
Merge pull request #270 from snickler/main
Updated dotnet-install scripts to latest version
This commit is contained in:
commit
608ee757cf
806
externals/install-dotnet.ps1
vendored
806
externals/install-dotnet.ps1
vendored
File diff suppressed because it is too large
Load Diff
543
externals/install-dotnet.sh
vendored
543
externals/install-dotnet.sh
vendored
@ -135,6 +135,31 @@ get_legacy_os_name_from_platform() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_legacy_os_name() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local uname=$(uname)
|
||||||
|
if [ "$uname" = "Darwin" ]; then
|
||||||
|
echo "osx"
|
||||||
|
return 0
|
||||||
|
elif [ -n "$runtime_id" ]; then
|
||||||
|
echo $(get_legacy_os_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}")
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if [ -e /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "")
|
||||||
|
if [ -n "$os" ]; then
|
||||||
|
echo "$os"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_verbose "Distribution specific OS name and version could not be detected: UName = $uname"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
get_linux_platform_name() {
|
get_linux_platform_name() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
@ -174,8 +199,8 @@ get_current_os_name() {
|
|||||||
echo "freebsd"
|
echo "freebsd"
|
||||||
return 0
|
return 0
|
||||||
elif [ "$uname" = "Linux" ]; then
|
elif [ "$uname" = "Linux" ]; then
|
||||||
local linux_platform_name
|
local linux_platform_name=""
|
||||||
linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; }
|
linux_platform_name="$(get_linux_platform_name)" || true
|
||||||
|
|
||||||
if [ "$linux_platform_name" = "rhel.6" ]; then
|
if [ "$linux_platform_name" = "rhel.6" ]; then
|
||||||
echo $linux_platform_name
|
echo $linux_platform_name
|
||||||
@ -196,31 +221,6 @@ get_current_os_name() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
get_legacy_os_name() {
|
|
||||||
eval $invocation
|
|
||||||
|
|
||||||
local uname=$(uname)
|
|
||||||
if [ "$uname" = "Darwin" ]; then
|
|
||||||
echo "osx"
|
|
||||||
return 0
|
|
||||||
elif [ -n "$runtime_id" ]; then
|
|
||||||
echo $(get_legacy_os_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}")
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
if [ -e /etc/os-release ]; then
|
|
||||||
. /etc/os-release
|
|
||||||
os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "")
|
|
||||||
if [ -n "$os" ]; then
|
|
||||||
echo "$os"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
say_verbose "Distribution specific OS name and version could not be detected: UName = $uname"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
machine_has() {
|
machine_has() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
@ -228,7 +228,6 @@ machine_has() {
|
|||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
check_min_reqs() {
|
check_min_reqs() {
|
||||||
local hasMinimum=false
|
local hasMinimum=false
|
||||||
if machine_has "curl"; then
|
if machine_has "curl"; then
|
||||||
@ -321,11 +320,13 @@ get_normalized_architecture_from_architecture() {
|
|||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
local architecture="$(to_lowercase "$1")"
|
local architecture="$(to_lowercase "$1")"
|
||||||
case "$architecture" in
|
|
||||||
\<auto\>)
|
if [[ $architecture == \<auto\> ]]; then
|
||||||
echo "$(get_normalized_architecture_from_architecture "$(get_machine_architecture)")"
|
echo "$(get_machine_architecture)"
|
||||||
return 0
|
return 0
|
||||||
;;
|
fi
|
||||||
|
|
||||||
|
case "$architecture" in
|
||||||
amd64|x64)
|
amd64|x64)
|
||||||
echo "x64"
|
echo "x64"
|
||||||
return 0
|
return 0
|
||||||
@ -425,6 +426,7 @@ get_normalized_channel() {
|
|||||||
get_normalized_product() {
|
get_normalized_product() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
|
local product=""
|
||||||
local runtime="$(to_lowercase "$1")"
|
local runtime="$(to_lowercase "$1")"
|
||||||
if [[ "$runtime" == "dotnet" ]]; then
|
if [[ "$runtime" == "dotnet" ]]; then
|
||||||
product="dotnet-runtime"
|
product="dotnet-runtime"
|
||||||
@ -446,7 +448,7 @@ get_normalized_product() {
|
|||||||
|
|
||||||
# args:
|
# args:
|
||||||
# version_text - stdin
|
# version_text - stdin
|
||||||
get_version_from_version_info() {
|
get_version_from_latestversion_file_content() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
cat | tail -n 1 | sed 's/\r$//'
|
cat | tail -n 1 | sed 's/\r$//'
|
||||||
@ -478,7 +480,7 @@ is_dotnet_package_installed() {
|
|||||||
# azure_feed - $1
|
# azure_feed - $1
|
||||||
# channel - $2
|
# channel - $2
|
||||||
# normalized_architecture - $3
|
# normalized_architecture - $3
|
||||||
get_latest_version_info() {
|
get_version_from_latestversion_file() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
local azure_feed="$1"
|
local azure_feed="$1"
|
||||||
@ -487,24 +489,24 @@ get_latest_version_info() {
|
|||||||
|
|
||||||
local version_file_url=null
|
local version_file_url=null
|
||||||
if [[ "$runtime" == "dotnet" ]]; then
|
if [[ "$runtime" == "dotnet" ]]; then
|
||||||
version_file_url="$uncached_feed/Runtime/$channel/latest.version"
|
version_file_url="$azure_feed/Runtime/$channel/latest.version"
|
||||||
elif [[ "$runtime" == "aspnetcore" ]]; then
|
elif [[ "$runtime" == "aspnetcore" ]]; then
|
||||||
version_file_url="$uncached_feed/aspnetcore/Runtime/$channel/latest.version"
|
version_file_url="$azure_feed/aspnetcore/Runtime/$channel/latest.version"
|
||||||
elif [ -z "$runtime" ]; then
|
elif [ -z "$runtime" ]; then
|
||||||
version_file_url="$uncached_feed/Sdk/$channel/latest.version"
|
version_file_url="$azure_feed/Sdk/$channel/latest.version"
|
||||||
else
|
else
|
||||||
say_err "Invalid value for \$runtime"
|
say_err "Invalid value for \$runtime"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
say_verbose "get_latest_version_info: latest url: $version_file_url"
|
say_verbose "get_version_from_latestversion_file: latest url: $version_file_url"
|
||||||
|
|
||||||
download "$version_file_url"
|
download "$version_file_url" || return $?
|
||||||
return $?
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# args:
|
# args:
|
||||||
# json_file - $1
|
# json_file - $1
|
||||||
parse_jsonfile_for_version() {
|
parse_globaljson_file_for_version() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
|
|
||||||
local json_file="$1"
|
local json_file="$1"
|
||||||
@ -560,9 +562,9 @@ get_specific_version_from_version() {
|
|||||||
if [ -z "$json_file" ]; then
|
if [ -z "$json_file" ]; then
|
||||||
if [[ "$version" == "latest" ]]; then
|
if [[ "$version" == "latest" ]]; then
|
||||||
local version_info
|
local version_info
|
||||||
version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
|
version_info="$(get_version_from_latestversion_file "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
|
||||||
say_verbose "get_specific_version_from_version: version_info=$version_info"
|
say_verbose "get_specific_version_from_version: version_info=$version_info"
|
||||||
echo "$version_info" | get_version_from_version_info
|
echo "$version_info" | get_version_from_latestversion_file_content
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo "$version"
|
echo "$version"
|
||||||
@ -570,7 +572,7 @@ get_specific_version_from_version() {
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
local version_info
|
local version_info
|
||||||
version_info="$(parse_jsonfile_for_version "$json_file")" || return 1
|
version_info="$(parse_globaljson_file_for_version "$json_file")" || return 1
|
||||||
echo "$version_info"
|
echo "$version_info"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@ -635,14 +637,14 @@ get_specific_product_version() {
|
|||||||
|
|
||||||
if machine_has "curl"
|
if machine_has "curl"
|
||||||
then
|
then
|
||||||
specific_product_version=$(curl -s --fail "${download_link}${feed_credential}")
|
specific_product_version=$(curl -s --fail "${download_link}${feed_credential}" 2>&1)
|
||||||
if [ $? = 0 ]; then
|
if [ $? = 0 ]; then
|
||||||
echo "${specific_product_version//[$'\t\r\n']}"
|
echo "${specific_product_version//[$'\t\r\n']}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
elif machine_has "wget"
|
elif machine_has "wget"
|
||||||
then
|
then
|
||||||
specific_product_version=$(wget -qO- "${download_link}${feed_credential}")
|
specific_product_version=$(wget -qO- "${download_link}${feed_credential}" 2>&1)
|
||||||
if [ $? = 0 ]; then
|
if [ $? = 0 ]; then
|
||||||
echo "${specific_product_version//[$'\t\r\n']}"
|
echo "${specific_product_version//[$'\t\r\n']}"
|
||||||
return 0
|
return 0
|
||||||
@ -907,7 +909,7 @@ get_http_header_curl() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
curl_options="-I -sSL --retry 5 --retry-delay 2 --connect-timeout 15 "
|
curl_options="-I -sSL --retry 5 --retry-delay 2 --connect-timeout 15 "
|
||||||
curl $curl_options "$remote_path_with_credential" || return 1
|
curl $curl_options "$remote_path_with_credential" 2>&1 || return 1
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,15 +920,26 @@ get_http_header_wget() {
|
|||||||
eval $invocation
|
eval $invocation
|
||||||
local remote_path="$1"
|
local remote_path="$1"
|
||||||
local disable_feed_credential="$2"
|
local disable_feed_credential="$2"
|
||||||
|
local wget_options="-q -S --spider --tries 5 "
|
||||||
|
# Store options that aren't supported on all wget implementations separately.
|
||||||
|
local wget_options_extra="--waitretry 2 --connect-timeout 15 "
|
||||||
|
local wget_result=''
|
||||||
|
|
||||||
remote_path_with_credential="$remote_path"
|
remote_path_with_credential="$remote_path"
|
||||||
if [ "$disable_feed_credential" = false ]; then
|
if [ "$disable_feed_credential" = false ]; then
|
||||||
remote_path_with_credential+="$feed_credential"
|
remote_path_with_credential+="$feed_credential"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wget_options="-q -S --spider --tries 5 --waitretry 2 --connect-timeout 15 "
|
wget $wget_options $wget_options_extra "$remote_path_with_credential" 2>&1
|
||||||
wget $wget_options "$remote_path_with_credential" 2>&1 || return 1
|
wget_result=$?
|
||||||
return 0
|
|
||||||
|
if [[ $wget_result == 2 ]]; then
|
||||||
|
# Parsing of the command has failed. Exclude potentially unrecognized options and retry.
|
||||||
|
wget $wget_options "$remote_path_with_credential" 2>&1
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $wget_result
|
||||||
}
|
}
|
||||||
|
|
||||||
# args:
|
# args:
|
||||||
@ -988,9 +1001,9 @@ downloadcurl() {
|
|||||||
local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs "
|
local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs "
|
||||||
local failed=false
|
local failed=false
|
||||||
if [ -z "$out_path" ]; then
|
if [ -z "$out_path" ]; then
|
||||||
curl $curl_options "$remote_path_with_credential" || failed=true
|
curl $curl_options "$remote_path_with_credential" 2>&1 || failed=true
|
||||||
else
|
else
|
||||||
curl $curl_options -o "$out_path" "$remote_path_with_credential" || failed=true
|
curl $curl_options -o "$out_path" "$remote_path_with_credential" 2>&1 || failed=true
|
||||||
fi
|
fi
|
||||||
if [ "$failed" = true ]; then
|
if [ "$failed" = true ]; then
|
||||||
local disable_feed_credential=false
|
local disable_feed_credential=false
|
||||||
@ -1016,14 +1029,31 @@ downloadwget() {
|
|||||||
local out_path="${2:-}"
|
local out_path="${2:-}"
|
||||||
# Append feed_credential as late as possible before calling wget to avoid logging feed_credential
|
# Append feed_credential as late as possible before calling wget to avoid logging feed_credential
|
||||||
local remote_path_with_credential="${remote_path}${feed_credential}"
|
local remote_path_with_credential="${remote_path}${feed_credential}"
|
||||||
local wget_options="--tries 20 --waitretry 2 --connect-timeout 15 "
|
local wget_options="--tries 20 "
|
||||||
local failed=false
|
# Store options that aren't supported on all wget implementations separately.
|
||||||
|
local wget_options_extra="--waitretry 2 --connect-timeout 15 "
|
||||||
|
local wget_result=''
|
||||||
|
|
||||||
if [ -z "$out_path" ]; then
|
if [ -z "$out_path" ]; then
|
||||||
wget -q $wget_options -O - "$remote_path_with_credential" || failed=true
|
wget -q $wget_options $wget_options_extra -O - "$remote_path_with_credential" 2>&1
|
||||||
|
wget_result=$?
|
||||||
else
|
else
|
||||||
wget $wget_options -O "$out_path" "$remote_path_with_credential" || failed=true
|
wget $wget_options $wget_options_extra -O "$out_path" "$remote_path_with_credential" 2>&1
|
||||||
|
wget_result=$?
|
||||||
fi
|
fi
|
||||||
if [ "$failed" = true ]; then
|
|
||||||
|
if [[ $wget_result == 2 ]]; then
|
||||||
|
# Parsing of the command has failed. Exclude potentially unrecognized options and retry.
|
||||||
|
if [ -z "$out_path" ]; then
|
||||||
|
wget -q $wget_options -O - "$remote_path_with_credential" 2>&1
|
||||||
|
wget_result=$?
|
||||||
|
else
|
||||||
|
wget $wget_options -O "$out_path" "$remote_path_with_credential" 2>&1
|
||||||
|
wget_result=$?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $wget_result != 0 ]]; then
|
||||||
local disable_feed_credential=false
|
local disable_feed_credential=false
|
||||||
local response=$(get_http_header_wget $remote_path $disable_feed_credential)
|
local response=$(get_http_header_wget $remote_path $disable_feed_credential)
|
||||||
http_code=$( echo "$response" | awk '/^ HTTP/{print $2}' | tail -1 )
|
http_code=$( echo "$response" | awk '/^ HTTP/{print $2}' | tail -1 )
|
||||||
@ -1034,6 +1064,7 @@ downloadwget() {
|
|||||||
say_verbose "$download_error_msg"
|
say_verbose "$download_error_msg"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1090,11 +1121,197 @@ get_download_link_from_aka_ms() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_feeds_to_use()
|
||||||
|
{
|
||||||
|
feeds=(
|
||||||
|
"https://dotnetcli.azureedge.net/dotnet"
|
||||||
|
"https://dotnetbuilds.azureedge.net/public"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ -n "$azure_feed" ]]; then
|
||||||
|
feeds=("$azure_feed")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$no_cdn" == "true" ]]; then
|
||||||
|
feeds=(
|
||||||
|
"https://dotnetcli.blob.core.windows.net/dotnet"
|
||||||
|
"https://dotnetbuilds.blob.core.windows.net/public"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ -n "$uncached_feed" ]]; then
|
||||||
|
feeds=("$uncached_feed")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# THIS FUNCTION MAY EXIT (if the determined version is already installed).
|
||||||
|
generate_download_links() {
|
||||||
|
|
||||||
|
download_links=()
|
||||||
|
specific_versions=()
|
||||||
|
effective_versions=()
|
||||||
|
link_types=()
|
||||||
|
|
||||||
|
# If generate_akams_links returns false, no fallback to old links. Just terminate.
|
||||||
|
# This function may also 'exit' (if the determined version is already installed).
|
||||||
|
generate_akams_links || return
|
||||||
|
|
||||||
|
# Check other feeds only if we haven't been able to find an aka.ms link.
|
||||||
|
if [[ "${#download_links[@]}" -lt 1 ]]; then
|
||||||
|
for feed in ${feeds[@]}
|
||||||
|
do
|
||||||
|
# generate_regular_links may also 'exit' (if the determined version is already installed).
|
||||||
|
generate_regular_links $feed || return
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${#download_links[@]}" -eq 0 ]]; then
|
||||||
|
say_err "Failed to resolve the exact version number."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_verbose "Generated ${#download_links[@]} links."
|
||||||
|
for link_index in ${!download_links[@]}
|
||||||
|
do
|
||||||
|
say_verbose "Link $link_index: ${link_types[$link_index]}, ${effective_versions[$link_index]}, ${download_links[$link_index]}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# THIS FUNCTION MAY EXIT (if the determined version is already installed).
|
||||||
|
generate_akams_links() {
|
||||||
|
local valid_aka_ms_link=true;
|
||||||
|
|
||||||
|
normalized_version="$(to_lowercase "$version")"
|
||||||
|
if [[ -n "$json_file" || "$normalized_version" != "latest" ]]; then
|
||||||
|
# aka.ms links are not needed when exact version is specified via command or json file
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
get_download_link_from_aka_ms || valid_aka_ms_link=false
|
||||||
|
|
||||||
|
if [[ "$valid_aka_ms_link" == true ]]; then
|
||||||
|
say_verbose "Retrieved primary payload URL from aka.ms link: '$aka_ms_download_link'."
|
||||||
|
say_verbose "Downloading using legacy url will not be attempted."
|
||||||
|
|
||||||
|
download_link=$aka_ms_download_link
|
||||||
|
|
||||||
|
#get version from the path
|
||||||
|
IFS='/'
|
||||||
|
read -ra pathElems <<< "$download_link"
|
||||||
|
count=${#pathElems[@]}
|
||||||
|
specific_version="${pathElems[count-2]}"
|
||||||
|
unset IFS;
|
||||||
|
say_verbose "Version: '$specific_version'."
|
||||||
|
|
||||||
|
#Retrieve effective version
|
||||||
|
effective_version="$(get_specific_product_version "$azure_feed" "$specific_version" "$download_link")"
|
||||||
|
|
||||||
|
# Add link info to arrays
|
||||||
|
download_links+=($download_link)
|
||||||
|
specific_versions+=($specific_version)
|
||||||
|
effective_versions+=($effective_version)
|
||||||
|
link_types+=("aka.ms")
|
||||||
|
|
||||||
|
# Check if the SDK version is already installed.
|
||||||
|
if [[ "$dry_run" != true ]] && is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then
|
||||||
|
say "$asset_name with version '$effective_version' is already installed."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if quality is specified - exit with error - there is no fallback approach
|
||||||
|
if [ ! -z "$normalized_quality" ]; then
|
||||||
|
say_err "Failed to locate the latest version in the channel '$normalized_channel' with '$normalized_quality' quality for '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'."
|
||||||
|
say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
say_verbose "Falling back to latest.version file approach."
|
||||||
|
}
|
||||||
|
|
||||||
|
# THIS FUNCTION MAY EXIT (if the determined version is already installed)
|
||||||
|
# args:
|
||||||
|
# feed - $1
|
||||||
|
generate_regular_links() {
|
||||||
|
local feed="$1"
|
||||||
|
local valid_legacy_download_link=true
|
||||||
|
|
||||||
|
specific_version=$(get_specific_version_from_version "$feed" "$channel" "$normalized_architecture" "$version" "$json_file") || specific_version='0'
|
||||||
|
|
||||||
|
if [[ "$specific_version" == '0' ]]; then
|
||||||
|
say_verbose "Failed to resolve the specific version number using feed '$feed'"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
effective_version="$(get_specific_product_version "$feed" "$specific_version")"
|
||||||
|
say_verbose "specific_version=$specific_version"
|
||||||
|
|
||||||
|
download_link="$(construct_download_link "$feed" "$channel" "$normalized_architecture" "$specific_version" "$normalized_os")"
|
||||||
|
say_verbose "Constructed primary named payload URL: $download_link"
|
||||||
|
|
||||||
|
# Add link info to arrays
|
||||||
|
download_links+=($download_link)
|
||||||
|
specific_versions+=($specific_version)
|
||||||
|
effective_versions+=($effective_version)
|
||||||
|
link_types+=("primary")
|
||||||
|
|
||||||
|
legacy_download_link="$(construct_legacy_download_link "$feed" "$channel" "$normalized_architecture" "$specific_version")" || valid_legacy_download_link=false
|
||||||
|
|
||||||
|
if [ "$valid_legacy_download_link" = true ]; then
|
||||||
|
say_verbose "Constructed legacy named payload URL: $legacy_download_link"
|
||||||
|
|
||||||
|
download_links+=($legacy_download_link)
|
||||||
|
specific_versions+=($specific_version)
|
||||||
|
effective_versions+=($effective_version)
|
||||||
|
link_types+=("legacy")
|
||||||
|
else
|
||||||
|
legacy_download_link=""
|
||||||
|
say_verbose "Cound not construct a legacy_download_link; omitting..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the SDK version is already installed.
|
||||||
|
if [[ "$dry_run" != true ]] && is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then
|
||||||
|
say "$asset_name with version '$effective_version' is already installed."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_dry_run() {
|
||||||
|
|
||||||
|
say "Payload URLs:"
|
||||||
|
|
||||||
|
for link_index in "${!download_links[@]}"
|
||||||
|
do
|
||||||
|
say "URL #$link_index - ${link_types[$link_index]}: ${download_links[$link_index]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
resolved_version=${specific_versions[0]}
|
||||||
|
repeatable_command="./$script_name --version "\""$resolved_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
|
||||||
|
|
||||||
|
if [ ! -z "$normalized_quality" ]; then
|
||||||
|
repeatable_command+=" --quality "\""$normalized_quality"\"""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$runtime" == "dotnet" ]]; then
|
||||||
|
repeatable_command+=" --runtime "\""dotnet"\"""
|
||||||
|
elif [[ "$runtime" == "aspnetcore" ]]; then
|
||||||
|
repeatable_command+=" --runtime "\""aspnetcore"\"""
|
||||||
|
fi
|
||||||
|
|
||||||
|
repeatable_command+="$non_dynamic_parameters"
|
||||||
|
|
||||||
|
if [ -n "$feed_credential" ]; then
|
||||||
|
repeatable_command+=" --feed-credential "\""<feed_credential>"\"""
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Repeatable invocation: $repeatable_command"
|
||||||
|
}
|
||||||
|
|
||||||
calculate_vars() {
|
calculate_vars() {
|
||||||
eval $invocation
|
eval $invocation
|
||||||
valid_legacy_download_link=true
|
|
||||||
|
|
||||||
#normalize input variables
|
script_name=$(basename "$0")
|
||||||
normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")"
|
normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")"
|
||||||
say_verbose "Normalized architecture: '$normalized_architecture'."
|
say_verbose "Normalized architecture: '$normalized_architecture'."
|
||||||
normalized_os="$(get_normalized_os "$user_defined_os")"
|
normalized_os="$(get_normalized_os "$user_defined_os")"
|
||||||
@ -1105,76 +1322,8 @@ calculate_vars() {
|
|||||||
say_verbose "Normalized channel: '$normalized_channel'."
|
say_verbose "Normalized channel: '$normalized_channel'."
|
||||||
normalized_product="$(get_normalized_product "$runtime")"
|
normalized_product="$(get_normalized_product "$runtime")"
|
||||||
say_verbose "Normalized product: '$normalized_product'."
|
say_verbose "Normalized product: '$normalized_product'."
|
||||||
|
|
||||||
#try to get download location from aka.ms link
|
|
||||||
#not applicable when exact version is specified via command or json file
|
|
||||||
normalized_version="$(to_lowercase "$version")"
|
|
||||||
if [[ -z "$json_file" && "$normalized_version" == "latest" ]]; then
|
|
||||||
|
|
||||||
valid_aka_ms_link=true;
|
|
||||||
get_download_link_from_aka_ms || valid_aka_ms_link=false
|
|
||||||
|
|
||||||
if [ "$valid_aka_ms_link" == false ]; then
|
|
||||||
# if quality is specified - exit with error - there is no fallback approach
|
|
||||||
if [ ! -z "$normalized_quality" ]; then
|
|
||||||
say_err "Failed to locate the latest version in the channel '$normalized_channel' with '$normalized_quality' quality for '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'."
|
|
||||||
say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
say_verbose "Falling back to latest.version file approach."
|
|
||||||
else
|
|
||||||
say_verbose "Retrieved primary payload URL from aka.ms link: '$aka_ms_download_link'."
|
|
||||||
download_link=$aka_ms_download_link
|
|
||||||
|
|
||||||
say_verbose "Downloading using legacy url will not be attempted."
|
|
||||||
valid_legacy_download_link=false
|
|
||||||
|
|
||||||
#get version from the path
|
|
||||||
IFS='/'
|
|
||||||
read -ra pathElems <<< "$download_link"
|
|
||||||
count=${#pathElems[@]}
|
|
||||||
specific_version="${pathElems[count-2]}"
|
|
||||||
unset IFS;
|
|
||||||
say_verbose "Version: '$specific_version'."
|
|
||||||
|
|
||||||
#Retrieve product specific version
|
|
||||||
specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version" "$download_link")"
|
|
||||||
say_verbose "Product specific version: '$specific_product_version'."
|
|
||||||
|
|
||||||
install_root="$(resolve_installation_path "$install_dir")"
|
install_root="$(resolve_installation_path "$install_dir")"
|
||||||
say_verbose "InstallRoot: '$install_root'."
|
say_verbose "InstallRoot: '$install_root'."
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")"
|
|
||||||
specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version")"
|
|
||||||
say_verbose "specific_version=$specific_version"
|
|
||||||
if [ -z "$specific_version" ]; then
|
|
||||||
say_err "Could not resolve version information."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
download_link="$(construct_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version" "$normalized_os")"
|
|
||||||
say_verbose "Constructed primary named payload URL: $download_link"
|
|
||||||
|
|
||||||
legacy_download_link="$(construct_legacy_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" || valid_legacy_download_link=false
|
|
||||||
|
|
||||||
if [ "$valid_legacy_download_link" = true ]; then
|
|
||||||
say_verbose "Constructed legacy named payload URL: $legacy_download_link"
|
|
||||||
else
|
|
||||||
say_verbose "Cound not construct a legacy_download_link; omitting..."
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_root="$(resolve_installation_path "$install_dir")"
|
|
||||||
say_verbose "InstallRoot: $install_root"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_dotnet() {
|
|
||||||
eval $invocation
|
|
||||||
local download_failed=false
|
|
||||||
local asset_name=''
|
|
||||||
local asset_relative_path=''
|
|
||||||
|
|
||||||
if [[ "$runtime" == "dotnet" ]]; then
|
if [[ "$runtime" == "dotnet" ]]; then
|
||||||
asset_relative_path="shared/Microsoft.NETCore.App"
|
asset_relative_path="shared/Microsoft.NETCore.App"
|
||||||
@ -1185,84 +1334,52 @@ install_dotnet() {
|
|||||||
elif [ -z "$runtime" ]; then
|
elif [ -z "$runtime" ]; then
|
||||||
asset_relative_path="sdk"
|
asset_relative_path="sdk"
|
||||||
asset_name=".NET Core SDK"
|
asset_name=".NET Core SDK"
|
||||||
else
|
|
||||||
say_err "Invalid value for \$runtime"
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the SDK version is already installed.
|
get_feeds_to_use
|
||||||
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then
|
}
|
||||||
say "$asset_name version $specific_version is already installed."
|
|
||||||
return 0
|
install_dotnet() {
|
||||||
fi
|
eval $invocation
|
||||||
|
local download_failed=false
|
||||||
|
local download_completed=false
|
||||||
|
|
||||||
mkdir -p "$install_root"
|
mkdir -p "$install_root"
|
||||||
zip_path="$(mktemp "$temporary_file_template")"
|
zip_path="$(mktemp "$temporary_file_template")"
|
||||||
say_verbose "Zip path: $zip_path"
|
say_verbose "Zip path: $zip_path"
|
||||||
|
|
||||||
|
for link_index in "${!download_links[@]}"
|
||||||
|
do
|
||||||
|
download_link="${download_links[$link_index]}"
|
||||||
|
specific_version="${specific_versions[$link_index]}"
|
||||||
|
effective_version="${effective_versions[$link_index]}"
|
||||||
|
link_type="${link_types[$link_index]}"
|
||||||
|
|
||||||
# Failures are normal in the non-legacy case for ultimately legacy downloads.
|
say "Attempting to download using $link_type link $download_link"
|
||||||
# Do not output to stderr, since output to stderr is considered an error.
|
|
||||||
say "Downloading primary link $download_link"
|
|
||||||
|
|
||||||
# The download function will set variables $http_code and $download_error_msg in case of failure.
|
# The download function will set variables $http_code and $download_error_msg in case of failure.
|
||||||
download "$download_link" "$zip_path" 2>&1 || download_failed=true
|
|
||||||
|
|
||||||
# if the download fails, download the legacy_download_link
|
|
||||||
if [ "$download_failed" = true ]; then
|
|
||||||
primary_path_http_code="$http_code"; primary_path_download_error_msg="$download_error_msg"
|
|
||||||
case $primary_path_http_code in
|
|
||||||
404)
|
|
||||||
say "The resource at $download_link is not available."
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
say "$primary_path_download_error_msg"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed"
|
|
||||||
if [ "$valid_legacy_download_link" = true ]; then
|
|
||||||
download_failed=false
|
download_failed=false
|
||||||
download_link="$legacy_download_link"
|
|
||||||
zip_path="$(mktemp "$temporary_file_template")"
|
|
||||||
say_verbose "Legacy zip path: $zip_path"
|
|
||||||
|
|
||||||
say "Downloading legacy link $download_link"
|
|
||||||
|
|
||||||
# The download function will set variables $http_code and $download_error_msg in case of failure.
|
|
||||||
download "$download_link" "$zip_path" 2>&1 || download_failed=true
|
download "$download_link" "$zip_path" 2>&1 || download_failed=true
|
||||||
|
|
||||||
if [ "$download_failed" = true ]; then
|
if [ "$download_failed" = true ]; then
|
||||||
legacy_path_http_code="$http_code"; legacy_path_download_error_msg="$download_error_msg"
|
case $http_code in
|
||||||
case $legacy_path_http_code in
|
|
||||||
404)
|
404)
|
||||||
say "The resource at $download_link is not available."
|
say "The resource at $link_type link '$download_link' is not available."
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
say "$legacy_path_download_error_msg"
|
say "Failed to download $link_type link '$download_link': $download_error_msg"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed"
|
rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed"
|
||||||
|
else
|
||||||
|
download_completed=true
|
||||||
|
break
|
||||||
fi
|
fi
|
||||||
fi
|
done
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$download_failed" = true ]; then
|
if [[ "$download_completed" == false ]]; then
|
||||||
if [[ "$primary_path_http_code" = "404" && ( "$valid_legacy_download_link" = false || "$legacy_path_http_code" = "404") ]]; then
|
|
||||||
say_err "Could not find \`$asset_name\` with version = $specific_version"
|
say_err "Could not find \`$asset_name\` with version = $specific_version"
|
||||||
say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support"
|
say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support"
|
||||||
else
|
|
||||||
say_err "Could not download: \`$asset_name\` with version = $specific_version"
|
|
||||||
# 404-NotFound is an expected response if it goes from only one of the links, do not show that error.
|
|
||||||
# If primary path is available (not 404-NotFound) then show the primary error else show the legacy error.
|
|
||||||
if [ "$primary_path_http_code" != "404" ]; then
|
|
||||||
say_err "$primary_path_download_error_msg"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
if [[ "$valid_legacy_download_link" = true && "$legacy_path_http_code" != "404" ]]; then
|
|
||||||
say_err "$legacy_path_download_error_msg"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -1283,14 +1400,14 @@ install_dotnet() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the standard SDK version is installed.
|
# Check if the standard SDK version is installed.
|
||||||
say_verbose "Checking installation: version = $specific_product_version"
|
say_verbose "Checking installation: version = $effective_version"
|
||||||
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_product_version"; then
|
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Version verification failed. More likely something is wrong either with the downloaded content or with the verification algorithm.
|
# Version verification failed. More likely something is wrong either with the downloaded content or with the verification algorithm.
|
||||||
say_err "Failed to verify the version of installed \`$asset_name\`.\nInstallation source: $download_link.\nInstallation location: $install_root.\nReport the bug at https://github.com/dotnet/install-scripts/issues."
|
say_err "Failed to verify the version of installed \`$asset_name\`.\nInstallation source: $download_link.\nInstallation location: $install_root.\nReport the bug at https://github.com/dotnet/install-scripts/issues."
|
||||||
say_err "\`$asset_name\` with version = $specific_product_version failed to install with an unknown error."
|
say_err "\`$asset_name\` with version = $effective_version failed to install with an error."
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1308,8 +1425,8 @@ architecture="<auto>"
|
|||||||
dry_run=false
|
dry_run=false
|
||||||
no_path=false
|
no_path=false
|
||||||
no_cdn=false
|
no_cdn=false
|
||||||
azure_feed="https://dotnetcli.azureedge.net/dotnet"
|
azure_feed=""
|
||||||
uncached_feed="https://dotnetcli.blob.core.windows.net/dotnet"
|
uncached_feed=""
|
||||||
feed_credential=""
|
feed_credential=""
|
||||||
verbose=false
|
verbose=false
|
||||||
runtime=""
|
runtime=""
|
||||||
@ -1434,7 +1551,7 @@ do
|
|||||||
echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release"
|
echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release"
|
||||||
echo " examples: 5.0.1xx, 5.0.2xx."
|
echo " examples: 5.0.1xx, 5.0.2xx."
|
||||||
echo " Supported since 5.0 release"
|
echo " Supported since 5.0 release"
|
||||||
echo " Note: The version parameter overrides the channel parameter when any version other than `latest` is used."
|
echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used."
|
||||||
echo " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`."
|
echo " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`."
|
||||||
echo " -Version"
|
echo " -Version"
|
||||||
echo " Possible values:"
|
echo " Possible values:"
|
||||||
@ -1447,7 +1564,7 @@ do
|
|||||||
echo " Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used."
|
echo " Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used."
|
||||||
echo " For SDK use channel in A.B.Cxx format. Using quality for SDK together with channel in A.B format is not supported."
|
echo " For SDK use channel in A.B.Cxx format. Using quality for SDK together with channel in A.B format is not supported."
|
||||||
echo " Supported since 5.0 release."
|
echo " Supported since 5.0 release."
|
||||||
echo " Note: The version parameter overrides the channel parameter when any version other than `latest` is used, and therefore overrides the quality."
|
echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality."
|
||||||
echo " --internal,-Internal Download internal builds. Requires providing credentials via --feed-credential parameter."
|
echo " --internal,-Internal Download internal builds. Requires providing credentials via --feed-credential parameter."
|
||||||
echo " --feed-credential <FEEDCREDENTIAL> Token to access Azure feed. Used as a query string to append to the Azure feed."
|
echo " --feed-credential <FEEDCREDENTIAL> Token to access Azure feed. Used as a query string to append to the Azure feed."
|
||||||
echo " -FeedCredential This parameter typically is not specified."
|
echo " -FeedCredential This parameter typically is not specified."
|
||||||
@ -1469,8 +1586,12 @@ do
|
|||||||
echo " --dry-run,-DryRun Do not perform installation. Display download link."
|
echo " --dry-run,-DryRun Do not perform installation. Display download link."
|
||||||
echo " --no-path, -NoPath Do not set PATH for the current process."
|
echo " --no-path, -NoPath Do not set PATH for the current process."
|
||||||
echo " --verbose,-Verbose Display diagnostics information."
|
echo " --verbose,-Verbose Display diagnostics information."
|
||||||
echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user."
|
echo " --azure-feed,-AzureFeed For internal use only."
|
||||||
echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user."
|
echo " Allows using a different storage to download SDK archives from."
|
||||||
|
echo " This parameter is only used if --no-cdn is false."
|
||||||
|
echo " --uncached-feed,-UncachedFeed For internal use only."
|
||||||
|
echo " Allows using a different storage to download SDK archives from."
|
||||||
|
echo " This parameter is only used if --no-cdn is true."
|
||||||
echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable."
|
echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable."
|
||||||
echo " -SkipNonVersionedFiles"
|
echo " -SkipNonVersionedFiles"
|
||||||
echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
|
echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
|
||||||
@ -1478,14 +1599,6 @@ do
|
|||||||
echo " Note: global.json must have a value for 'SDK:Version'"
|
echo " Note: global.json must have a value for 'SDK:Version'"
|
||||||
echo " -?,--?,-h,--help,-Help Shows this help message"
|
echo " -?,--?,-h,--help,-Help Shows this help message"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Obsolete parameters:"
|
|
||||||
echo " --shared-runtime The recommended alternative is '--runtime dotnet'."
|
|
||||||
echo " This parameter is obsolete and may be removed in a future version of this script."
|
|
||||||
echo " Installs just the shared runtime bits, not the entire SDK."
|
|
||||||
echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
|
|
||||||
echo " -RuntimeId" The parameter is obsolete and may be removed in a future version of this script. Should be used only for versions below 2.1.
|
|
||||||
echo " For primary links to override OS or/and architecture, use --os and --architecture option instead."
|
|
||||||
echo ""
|
|
||||||
echo "Install Location:"
|
echo "Install Location:"
|
||||||
echo " Location is chosen in following order:"
|
echo " Location is chosen in following order:"
|
||||||
echo " - --install-dir option"
|
echo " - --install-dir option"
|
||||||
@ -1502,10 +1615,6 @@ do
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$no_cdn" = true ]; then
|
|
||||||
azure_feed="$uncached_feed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
|
say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
|
||||||
say "- The SDK needs to be installed without user interaction and without admin rights."
|
say "- The SDK needs to be installed without user interaction and without admin rights."
|
||||||
say "- The SDK installation doesn't need to persist across multiple CI runs."
|
say "- The SDK installation doesn't need to persist across multiple CI runs."
|
||||||
@ -1523,33 +1632,11 @@ fi
|
|||||||
|
|
||||||
check_min_reqs
|
check_min_reqs
|
||||||
calculate_vars
|
calculate_vars
|
||||||
script_name=$(basename "$0")
|
# generate_regular_links call below will 'exit' if the determined version is already installed.
|
||||||
|
generate_download_links
|
||||||
|
|
||||||
if [ "$dry_run" = true ]; then
|
if [[ "$dry_run" = true ]]; then
|
||||||
say "Payload URLs:"
|
print_dry_run
|
||||||
say "Primary named payload URL: ${download_link}"
|
|
||||||
if [ "$valid_legacy_download_link" = true ]; then
|
|
||||||
say "Legacy named payload URL: ${legacy_download_link}"
|
|
||||||
fi
|
|
||||||
repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
|
|
||||||
|
|
||||||
if [ ! -z "$normalized_quality" ]; then
|
|
||||||
repeatable_command+=" --quality "\""$normalized_quality"\"""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$runtime" == "dotnet" ]]; then
|
|
||||||
repeatable_command+=" --runtime "\""dotnet"\"""
|
|
||||||
elif [[ "$runtime" == "aspnetcore" ]]; then
|
|
||||||
repeatable_command+=" --runtime "\""aspnetcore"\"""
|
|
||||||
fi
|
|
||||||
|
|
||||||
repeatable_command+="$non_dynamic_parameters"
|
|
||||||
|
|
||||||
if [ -n "$feed_credential" ]; then
|
|
||||||
repeatable_command+=" --feed-credential "\""<feed_credential>"\"""
|
|
||||||
fi
|
|
||||||
|
|
||||||
say "Repeatable invocation: $repeatable_command"
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user