Skip to content

Commit

Permalink
[PowerShell]- Fallback method with find_prev_vers_frm_git_tags() impl…
Browse files Browse the repository at this point in the history
…emented (#926)

* [PowerShell]- Fallback method with find_prev_vers_frm_git_tags() done

* bumped the feature version
  • Loading branch information
gauravsaini04 authored Apr 2, 2024
1 parent 32d2b5b commit 55dbe13
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/powershell/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "powershell",
"version": "1.3.4",
"version": "1.3.5",
"name": "PowerShell",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell",
"description": "Installs PowerShell along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.",
Expand Down
81 changes: 74 additions & 7 deletions src/powershell/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,83 @@ install_using_apt() {
apt-get install -yq powershell${version_suffix} || return 1
}

# Use semver logic to decrement a version number then look for the closest match
find_prev_version_from_git_tags() {
local variable_name=$1
local current_version=${!variable_name}
local repository=$2
# Normally a "v" is used before the version number, but support alternate cases
local prefix=${3:-"tags/v"}
# Some repositories use "_" instead of "." for version number part separation, support that
local separator=${4:-"."}
# Some tools release versions that omit the last digit (e.g. go)
local last_part_optional=${5:-"false"}
# Some repositories may have tags that include a suffix (e.g. actions/node-versions)
local version_suffix_regex=$6
# Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios.
set +e
major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')"
minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')"
breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')"

if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then
((major=major-1))
declare -g ${variable_name}="${major}"
# Look for latest version from previous major release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
# Handle situations like Go's odd version pattern where "0" releases omit the last part
elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then
((minor=minor-1))
declare -g ${variable_name}="${major}.${minor}"
# Look for latest version from previous minor release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
else
((breakfix=breakfix-1))
if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then
declare -g ${variable_name}="${major}.${minor}"
else
declare -g ${variable_name}="${major}.${minor}.${breakfix}"
fi
fi
set -e
}

# Function to fetch the version released prior to the latest version
get_previous_version() {
repo_url=$1
curl -s "$repo_url" | jq -r 'del(.[].assets) | .[0].tag_name'
local url=$1
local repo_url=$2
local variable_name=$3
prev_version=${!variable_name}

output=$(curl -s "$repo_url");
check_packages jq
message=$(echo "$output" | jq -r '.message')

if [[ $message == "API rate limit exceeded"* ]]; then
echo -e "\nAn attempt to find latest version using GitHub Api Failed... \nReason: ${message}"
echo -e "\nAttempting to find latest version using GitHub tags."
find_prev_version_from_git_tags prev_version "$url" "tags/v"
declare -g ${variable_name}="${prev_version}"
else
echo -e "\nAttempting to find latest version using GitHub Api."
version=$(echo "$output" | jq -r '.tag_name')
declare -g ${variable_name}="${version#v}"
fi
echo "${variable_name}=${!variable_name}"
}

get_github_api_repo_url() {
local url=$1
echo "${url/https:\/\/github.com/https:\/\/api.github.com\/repos}/releases/latest"
}


install_prev_pwsh() {
pwsh_url=$1
repo_url=$(get_github_api_repo_url $pwsh_url)
echo -e "\n(!) Failed to fetch the latest artifacts for powershell v${POWERSHELL_VERSION}..."
previous_version=$(get_previous_version "https://api.github.com/repos/PowerShell/PowerShell/releases")
echo -e "\nAttempting to install ${previous_version}"
POWERSHELL_VERSION="${previous_version#v}"
get_previous_version $pwsh_url $repo_url POWERSHELL_VERSION
echo -e "\nAttempting to install v${POWERSHELL_VERSION}"
install_pwsh "${POWERSHELL_VERSION}"
}

Expand All @@ -138,10 +204,11 @@ install_using_github() {
if [ "${architecture}" = "amd64" ]; then
architecture="x64"
fi
find_version_from_git_tags POWERSHELL_VERSION https://github.com/PowerShell/PowerShell
pwsh_url="https://github.com/PowerShell/PowerShell"
find_version_from_git_tags POWERSHELL_VERSION $pwsh_url
install_pwsh "${POWERSHELL_VERSION}"
if grep -q "Not Found" "${powershell_filename}"; then
install_prev_pwsh
install_prev_pwsh $pwsh_url
fi

# Ugly - but only way to get sha256 is to parse release HTML. Remove newlines and tags, then look for filename followed by 64 hex characters.
Expand Down
164 changes: 126 additions & 38 deletions test/powershell/install_powershell_fallback_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,125 @@ check "Powershell version as installed by feature" bash -c "pwsh --version"
. /etc/os-release
architecture="$(dpkg --print-architecture)"

sudo mkdir -p /var/lib/apt/lists/

# Figure out correct version of a three part version number is not passed
find_version_from_git_tags() {
local variable_name=$1
local requested_version=${!variable_name}
if [ "${requested_version}" = "none" ]; then return; fi
local repository=$2
local prefix=${3:-"tags/v"}
local separator=${4:-"."}
local last_part_optional=${5:-"false"}
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
local escaped_separator=${separator//./\\.}
local last_part
if [ "${last_part_optional}" = "true" ]; then
last_part="(${escaped_separator}[0-9]+)?"
else
last_part="${escaped_separator}[0-9]+"
fi
local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$"
local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)"
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)"
else
set +e
declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
set -e
fi
fi
if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then
echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2
exit 1
fi
echo "${variable_name}=${!variable_name}"
}

# Use semver logic to decrement a version number then look for the closest match
find_prev_version_from_git_tags() {
local variable_name=$1
local current_version=${!variable_name}
local repository=$2
# Normally a "v" is used before the version number, but support alternate cases
local prefix=${3:-"tags/v"}
# Some repositories use "_" instead of "." for version number part separation, support that
local separator=${4:-"."}
# Some tools release versions that omit the last digit (e.g. go)
local last_part_optional=${5:-"false"}
# Some repositories may have tags that include a suffix (e.g. actions/node-versions)
local version_suffix_regex=$6
# Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios.
set +e
major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')"
minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')"
breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')"

if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then
((major=major-1))
declare -g ${variable_name}="${major}"
# Look for latest version from previous major release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
# Handle situations like Go's odd version pattern where "0" releases omit the last part
elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then
((minor=minor-1))
declare -g ${variable_name}="${major}.${minor}"
# Look for latest version from previous minor release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
else
((breakfix=breakfix-1))
if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then
declare -g ${variable_name}="${major}.${minor}"
else
declare -g ${variable_name}="${major}.${minor}.${breakfix}"
fi
fi
set -e
}

# Function to fetch the version released prior to the latest version
get_previous_version() {
repo_url=$1
curl -s "$repo_url" | jq -r 'del(.[].assets) | .[0].tag_name'
local url=$1
local repo_url=$2
local variable_name=$3
local mode=$4
prev_version=${!variable_name}

output=$(curl -s "$repo_url");
message=$(echo "$output" | jq -r '.message')

if [ $mode == "mode1" ]; then
message="API rate limit exceeded"
else
message=""
fi

if [[ $message == "API rate limit exceeded"* ]]; then
echo -e "\nAn attempt to find latest version using GitHub Api Failed... \nReason: ${message}"
echo -e "\nAttempting to find latest version using GitHub tags."
find_prev_version_from_git_tags prev_version "$url" "tags/v"
declare -g ${variable_name}="${prev_version}"
else
echo -e "\nAttempting to find latest version using GitHub Api."
version=$(echo "$output" | jq -r '.tag_name')
declare -g ${variable_name}="${version#v}"
fi
echo "${variable_name}=${!variable_name}"
}

get_github_api_repo_url() {
local url=$1
echo "${url/https:\/\/github.com/https:\/\/api.github.com\/repos}/releases/latest"
}

install_prev_pwsh() {
local pwsh_url=$1
local mode=$2
local repo_url=$(get_github_api_repo_url $pwsh_url)
echo -e "\n(!) Failed to fetch the latest artifacts for powershell v${POWERSHELL_VERSION}..."
previous_version=$(get_previous_version "https://api.github.com/repos/PowerShell/PowerShell/releases")
echo -e "\nAttempting to install ${previous_version}"
POWERSHELL_VERSION="${previous_version#v}"
get_previous_version $pwsh_url $repo_url POWERSHELL_VERSION $mode
echo -e "\nAttempting to install v${POWERSHELL_VERSION}"
install_pwsh "${POWERSHELL_VERSION}"
}

Expand All @@ -37,44 +146,18 @@ install_pwsh() {
sudo curl -sSL -o "${powershell_filename}" "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/${powershell_filename}"
}

apt_get_update()
{
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
sudo apt-get update -y
fi
}

check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
sudo chmod +x /var/lib/apt/lists/
sudo mkdir -p /var/lib/apt/lists/partial
sudo chmod +rx /var/lib/dpkg/lock-frontend
apt_get_update
sudo apt-get -y install --no-install-recommends "$@"
fi
}

install_using_github() {
# Fall back on direct download if no apt package exists in microsoft pool
check_packages curl ca-certificates gnupg2 dirmngr libc6 libgcc1 libgssapi-krb5-2 libstdc++6 libunwind8 libuuid1 zlib1g libicu[0-9][0-9]
if ! type git > /dev/null 2>&1; then
check_packages git
fi
mode=$1
if [ "${architecture}" = "amd64" ]; then
architecture="x64"
fi

echo -e "\nTrying to install a non-existing version for Powershell..."

POWERSHELL_VERSION="1.2.XYZ"
pwsh_url="https://github.com/PowerShell/PowerShell"
POWERSHELL_VERSION="7.4.xyz"
install_pwsh "${POWERSHELL_VERSION}"

if grep -q "Not Found" "${powershell_filename}"; then
install_prev_pwsh
install_prev_pwsh $pwsh_url $mode
fi

echo -e "\n" $POWERSHELL_VERSION "=powershell_version\n";
# Ugly - but only way to get sha256 is to parse release HTML. Remove newlines and tags, then look for filename followed by 64 hex characters.
sudo curl -sSL -o "release.html" "https://github.com/PowerShell/PowerShell/releases/tag/v${POWERSHELL_VERSION}"
powershell_archive_sha256="$(cat release.html | tr '\n' ' ' | sed 's|<[^>]*>||g' | grep -oP "${powershell_filename}\s+\K[0-9a-fA-F]{64}" || echo '')"
Expand All @@ -86,12 +169,17 @@ install_using_github() {
fi
sudo tar xf "${powershell_filename}" -C "${powershell_target_path}"
sudo ln -s "${powershell_target_path}/pwsh" /usr/local/bin/pwsh
sudo rm -rf /tmp/pwsh
sudo rm -rf /tmp/pwsh /usr/local/bin/pwsh

}

install_using_github
echo -e "\nInstalling Powershell with find_prev_version_from_git_tags() fn 👈🏻"
install_using_github "mode1"
check "Powershell version as installed by test (find_prev_version_from_git_tags() fn)" bash -c "pwsh --version"

check "Powershell version as installed by test" bash -c "pwsh --version"
echo -e "\nInstalling Powershell with GitHub Api 👈🏻"
install_using_github "mode2"
check "Powershell version as installed by test (GitHub Api)" bash -c "pwsh --version"

# Report result
reportResults

0 comments on commit 55dbe13

Please sign in to comment.