Skip to content

Commit

Permalink
Add semver comparison branch
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenbaker committed Apr 1, 2024
1 parent b2ce158 commit b92ca6a
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 14 deletions.
142 changes: 142 additions & 0 deletions lib/compare_semver
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/bin/bash

set -euo pipefail

# Validate a semver string, exit if invalid.
# Arguments:
# $1: semver string to validate
validate_semver() {
# Regex pattern to match a semver string with numeric version identifiers and optional pre-release identifier

# Regex for a semver digit
D='0|[1-9][0-9]*'
# Regex for a semver pre-release word
PW='[0-9]*[a-zA-Z-][0-9a-zA-Z-]*'
# Regex for a semver build-metadata word
MW='[0-9a-zA-Z-]+'

semver_pattern="^($D)\.($D)\.($D)(-(($D|$PW)(\.($D|$PW))*))?(\+($MW(\.$MW)*))?$"

if ! [[ $1 =~ $semver_pattern ]]; then
echo "$0: Invalid semver string: $1" >&2
echo "$0: Expected format 'x.y.z' or 'x.y.z-pre-release'."
exit 1
fi
}

# Function to compare two component strings.
# Arguments:
# $1: First component string
# $2: Second component string
# Output:
# -1 if the first component has lower precedence
# 1 if the first component has higher precedence
compare_components() {
if (($1 > $2)); then
echo 1 && exit 0
elif (($1 < $2)); then
echo -1 && exit 0
fi
}

# Function to compare pre-release identifiers.
# Arguments:
# $1: First pre-release identifier
# $2: Second pre-release identifier
# Output:
# -1 if the first pre-release identifier has lower precedence
# 0 if both identifiers are equal
# 1 if the first pre-release identifier has higher precedence
compare_prerelease() {
# If both pre-release identifiers are empty, they are considered equal
if [ -z "$1" ] && [ -z "$2" ]; then
echo 0 && exit 0
fi

# If one of the pre-release identifiers is empty, the one with the non-empty identifier has lower precedence
if [ -z "$1" ]; then
echo 1 && exit 0
elif [ -z "$2" ]; then
echo -1 && exit 0
fi

# Split pre-release identifiers into individual parts
IFS='.' read -r -a parts1 <<<"$1"
IFS='.' read -r -a parts2 <<<"$2"

# Compare each part of the pre-release identifiers
for ((i = 0; i < ${#parts1[@]} || i < ${#parts2[@]}; i++)); do
part1="${parts1[i]:=""}"
part2="${parts2[i]:=""}"

# If one part is missing, it has lower precedence
if [ -z "$part1" ]; then
echo -1 && exit 0
elif [ -z "$part2" ]; then
echo 1 && exit 0
fi

# If parts are numeric, compare numerically
if [[ "$part1" =~ ^[0-9]+$ ]] && [[ "$part2" =~ ^[0-9]+$ ]]; then
compare_components "$part1" "$part2"
fi

# Lexicographically compare non-numeric parts
[[ "$part1" < "$part2" ]] && echo -1 && exit 0
[[ "$part1" > "$part2" ]] && echo 1 && exit 0
done

# Versions are equal
echo 0
}

# Function to compare two semver strings.
# Arguments:
# $1: First semver string
# $2: Second semver string
# Output:
# -1 if the first version is less than the second version
# 0 if the versions are equal
# 1 if the first version is greater than the second version
compare_semver() {
# Validate the semver strings
validate_semver "$1"
validate_semver "$2"

# Remove build metadata if present
semver1=$(cut -d+ -f1 <<<"$1")
semver2=$(cut -d+ -f1 <<<"$2")

# Extract major, minor, patch, and pre-release versions from the input strings
major1=$(cut -d. -f1 <<<"$semver1")
minor1=$(cut -d. -f2 <<<"$semver1")
patch1=$(cut -d. -f3 -s <<<"$semver1" | cut -d- -f1)
prerelease1=$(cut -d- -f2- -s <<<"$semver1")

major2=$(cut -d. -f1 <<<"$semver2")
minor2=$(cut -d. -f2 <<<"$semver2")
patch2=$(cut -d. -f3 -s <<<"$semver2" | cut -d- -f1)
prerelease2=$(cut -d- -f2- -s <<<"$semver2")

# Compare each component of the versions
compare_components "$major1" "$major2"
compare_components "$minor1" "$minor2"
compare_components "$patch1" "$patch2"

# Check if one version has a pre-release identifier while the other does not
if [ -n "$prerelease1" ] && [ -z "$prerelease2" ]; then
echo -1 && exit 0
elif [ -z "$prerelease1" ] && [ -n "$prerelease2" ]; then
echo 1 && exit 0
fi

# Compare pre-release identifiers if they exist
if [ -n "$prerelease1" ] && [ -n "$prerelease2" ]; then
compare_prerelease "$prerelease1" "$prerelease2" && exit 0
fi

# Versions are equal
echo 0
}

compare_semver "$1" "$2"
67 changes: 67 additions & 0 deletions lib/test_compare_semver
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash

test_cases=(
# Versions are equal
"1.0.0 1.0.0 0"
"1.1.0 1.1.0 0"
"1.0.1 1.0.1 0"
"1.0.0-alpha 1.0.0-alpha 0"
"1.0.0-alpha.beta 1.0.0-alpha.beta 0"
"1.0.0-abc123.1.1.1 1.0.0-abc123.1.1.1 0"

# First version is less than second version
"1.0.0 2.0.0 -1"
"1.0.0 1.1.0 -1"
"1.0.0 1.0.1 -1"
"1.0.0-alpha 1.0.0 -1"

# First version is greater than second version
"2.0.0 1.0.0 1"
"1.1.0 1.0.0 1"
"1.0.1 1.0.0 1"
"1.0.0 1.0.0-alpha 1"
"1.0.0-alpha.1 1.0.0-alpha 1"

# Pre-release versions comparison
"1.0.0-alpha 1.0.0-alpha.beta -1"
"1.0.0-alpha.beta 1.0.0-beta -1"
"1.0.0-alpha.beta 1.0.0-beta.2 -1"
"1.0.0-alpha.beta 1.0.0-beta.11 -1"
"1.0.0-alpha.beta 1.0.0-rc.1 -1"
"1.0.0-alpha.beta 1.0.0 -1"
"1.0.0-beta 1.0.0-beta.2 -1"
"1.0.0-beta 1.0.0-beta.11 -1"
"1.0.0-beta 1.0.0-rc.1 -1"
"1.0.0-beta 1.0.0 -1"
"1.0.0-beta.2 1.0.0-beta.11 -1"
"1.0.0-beta.2 1.0.0-rc.1 -1"
"1.0.0-beta.2 1.0.0 -1"
"1.0.0-beta.11 1.0.0-rc.1 -1"
"1.0.0-rc.1 1.0.0-rc.2 -1"
"1.0.0-rc.1 1.0.0 -1"

# Versions with build metadata
"1.0.0 1.0.0+build.1 0"
"1.0.0 1.0.0+build.2 0"
"1.0.0-alpha 1.0.0-alpha+build.1 0"
"1.0.0-alpha+build.1 1.0.0-alpha+build.2 0"
"1.0.0-alpha+build.1 1.0.0+build.1 -1"
"1.0.0+early 1.0.1+late -1"
"1.0.0+early 1.1.0+late -1"
"1.0.0+early 2.0.0+late -1"
)

LIB="$(dirname "$(readlink -f "$0")")"

# Run test cases
for test_case in "${test_cases[@]}"; do
versions=()
read -ra versions <<<"$test_case"
result=$("$LIB/compare_semver" "${versions[0]}" "${versions[1]}")
expected="${versions[2]}"
if [ "$result" -eq "$expected" ]; then
echo "OK: ${versions[0]} compared to ${versions[1]} :: Result - $result"
else
echo "FAIL: ${versions[0]} compared to ${versions[1]} :: Expected - $expected, Actual - $result"
fi
done
45 changes: 31 additions & 14 deletions lib/utils.bash
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ REPO="smithy-lang/smithy"
GH_REPO="https://github.com/$REPO"
GH_REPO_API="https://api.github.com/repos/$REPO"
TOOL_NAME="smithy"
LIB="$(dirname "$(dirname "$(readlink -f "$0")")")/lib"

fail() {
printf '%s\n' "asdf-$TOOL_NAME: $*" >&2
Expand Down Expand Up @@ -64,7 +65,7 @@ check_arch() {
}

# get the url for the artifact to download, based on
# the system (1st arg) and the version (2nd arg)
# the system (1st arg), the version (2nd arg), and the file extensions (3rd arg)
get_artifact_url() {
check jq
if [ "$#" -gt 1 ]; then
Expand All @@ -75,7 +76,7 @@ get_artifact_url() {
else
tag=$2
fi
echo "$GH_REPO/releases/download/$tag/smithy-cli-$1.zip"
echo "$GH_REPO/releases/download/$tag/smithy-cli-$1.$3"
else
fail "platform or version were not specified."
fi
Expand All @@ -93,19 +94,35 @@ download_release() {
echo "* Downloading $TOOL_NAME release ($version)..."
platform=$(get_platform)
arch=$(get_arch)
url=$(get_artifact_url "$platform-$arch" "$version")
if [ "$url" ]; then
mkdir -p "$path"
echo "Retrieving release at $url..."
tmp_download_dir=$(mktemp -d -t asdf_extract_XXXXXXX)
cd "$tmp_download_dir"
curl "${curl_opts[@]}" -o smithy.zip "$url" || fail "Request to '$url' returned bad response ($?)."
unzip -oq smithy.zip
mv smithy-cli-"$platform"-"$arch"/* "$path"
rm -rf smithy-cli-"$platform"-"$arch" smithy.zip
cd -
# Based on the version, install from tar or zip
# versions earlier than 1.47.0 must install from tarball
vercmp="$(. "$LIB"/compare_semver "$version" "1.47.0")"
if [ "$vercmp" = "-1" ]; then
# version is less than 1.47.0, use tar
url=$(get_artifact_url "$(get_platform)-$(get_arch)" "$version" "tar.gz")
if [ "$url" ]; then
echo "Retrieving release at $url..."
curl "${curl_opts[@]}" "$url" | tar xzf - -C "$path" ||
fail "Request to '$url' returned bad response ($?)."
else
fail "Could not form url."
fi
else
fail "Could not form url."
# version is greater than or equal to 1.47.0, use zip
url=$(get_artifact_url "$platform-$arch" "$version" "zip")
if [ "$url" ]; then
mkdir -p "$path"
echo "Retrieving release at $url..."
tmp_download_dir=$(mktemp -d -t asdf_extract_XXXXXXX)
cd "$tmp_download_dir"
curl "${curl_opts[@]}" -o smithy.zip "$url" || fail "Request to '$url' returned bad response ($?)."
unzip -oq smithy.zip
mv smithy-cli-"$platform"-"$arch"/* "$path"
rm -rf smithy-cli-"$platform"-"$arch" smithy.zip
cd -
else
fail "Could not form url."
fi
fi
else
fail "Download by '$type' is not supported."
Expand Down

0 comments on commit b92ca6a

Please sign in to comment.