Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 96 additions & 16 deletions bin/zopen-install
Original file line number Diff line number Diff line change
Expand Up @@ -184,18 +184,69 @@ installDependencies()

handlePackageInstall()
{

fullname="$1"
isRuntimeDependency=$2
if [ -z "$isRuntimeDependency" ]; then
isRuntimeDependency=false
fi
printVerbose "Name to install: ${fullname}, parsing any version ('=') or tag ('%') has been specified"
name=$(echo "${fullname}" | sed -e 's#[=%].*##')

operator=""
versioned=""
tagged=""

# Support: name=ver, name>=ver, name>ver, name<=ver, name<ver, name%tag
name=$(echo "${fullname}" | sed -E 's/[=><%].*//')
repo="${name}"
versioned=$(echo "${fullname}" | cut -s -d '=' -f 2)

# Get everything after the name
suffix="${fullname#$name}"

# Extract operator (can be >=, <=, >, <, =) from the beginning of suffix
operator=$(echo "${suffix}" | sed -E -n 's/^([>=<]{1,2}).*/\1/p')

if [ -n "${operator}" ]; then
# Validate operator
case "${operator}" in
"="|">"|">="|"<"|"<=") ;;
*) printError "Invalid operator '${operator}' in package specification. Supported operators are: =, >, >=, <, <=" ;;
esac

# Extract version: take everything after operator and before %
rest="${suffix#$operator}"
versioned="${rest%%%*}"

if [ -z "${versioned}" ]; then
printError "A version must be provided when using an operator (e.g., ${name}${operator}1.0)."
fi
else
# Fallback to older '=' parsing if no explicit operator found but versioned exists
# (Checking if suffix starts with '=' and taking everything before '%')
case "${suffix}" in
=*)
operator="="
rest="${suffix#=}"
versioned="${rest%%%*}"
;;
esac
fi

# Validate that the version string contains valid characters (alphanumeric, dots, hyphens)
if [ -n "${versioned}" ]; then
# Strip leading 'v' if present for validation
v_to_check="${versioned#v}"
Comment thread
sachintu47 marked this conversation as resolved.
# Must contain at least one digit and only valid chars
if [ -z "${v_to_check}" ] || ! echo "${v_to_check}" | grep -q "[0-9]" || echo "${v_to_check}" | grep -q "[^0-9a-zA-Z.-]" || echo "${v_to_check}" | grep -q "^\." || echo "${v_to_check}" | grep -q "\.$" || echo "${v_to_check}" | grep -q "\.\."; then
printError "Invalid version string '${versioned}'. Versions must be numeric segments separated by dots (e.g., 1.2.3) and can include alphanumeric suffixes (e.g., 1.2.3-rc1)."
fi
fi

# Extract tag
tagged=$(echo "${fullname}" | cut -s -d '%' -f 2)
Comment thread
sachintu47 marked this conversation as resolved.
printDebug "Name:${name};version:${versioned};tag:${tagged};repo:${repo}"

if [ -n "${versioned}" ] && [ -n "${tagged}" ]; then
printError "Ambiguous package specification '${fullname}'. Provide either a version constraint or a tag, but not both."
fi
printDebug "Name:${name};operator:${operator};version:${versioned};tag:${tagged};repo:${repo}"
printInfo "${NC}${HEADERCOLOR}${BOLD}Processing package: ${name}${NC}"

nameSansPort=$(echo "${name}" | sed -e 's#\(.*\)port$#\1#')
Expand Down Expand Up @@ -242,14 +293,35 @@ handlePackageInstall()
# Options where the user explicitly sets a version/tag/releaseline currently ignore any configured release-line,
# either for a previous package install or system default
if [ -n "${versioned}" ]; then
printVerbose "Specific version ${versioned} requested - checking existence and URL."
printVerbose "Specific version ${versioned} requested with operator '${operator}' - checking existence and URL."
requestedMajor=$(echo "${versioned}" | awk -F'.' '{print $1}')
requestedMinor=$(echo "${versioned}" | awk -F'.' '{print $2}')
requestedPatch=$(echo "${versioned}" | awk -F'.' '{print $3}')
requestedSubrelease=$(echo "${versioned}" | awk -F'.' '{print $4}')
requestedVersion="${requestedMajor}\\\.${requestedMinor}\\\.${requestedPatch}\\\.${requestedSubrelease}"
printVerbose "Finding URL for latest release matching version prefix: requestedVersion: ${requestedVersion}"
releaseMetadata=$(/bin/printf "%s" "${releases}" | jq -e -r '. | map(select(.assets[].name | test("'${requestedVersion}'")))[0]')
if [ -z "${requestedMajor}" ]; then
printError "A major version must be provided when specifying a version (e.g., ${name}=1)."
fi

# Convert requested version string to a JSON array of numbers for jq
req_v_json=$(echo "${versioned#v}" | tr '.' '\n' | jq -R 'tonumber' | jq -s -c .)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version validation above allows alphanumeric/hyphen suffixes (e.g. 1.2.3-rc1), but req_v_json is built via jq -R 'tonumber' and will error on any non-numeric segment. This makes some “valid” version inputs fail at runtime during release selection, so it may be worth ensuring the accepted version format matches what the jq parsing/comparison can actually handle.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


printVerbose "Finding latest release matching ${operator} ${versioned}"
# Unified selection logic using jq:
# 1. to_v: Converts a dot-delimited version string to a numeric array, padded with 0s to length 8
# 2. match_v: Handles prefix match for '=' and numeric comparison for others
releaseMetadata=$(/bin/printf "%s" "${releases}" | jq -e -r --arg op "${operator}" --argjson req_v "${req_v_json}" '
def to_v: if . == null then [0] else split(".") | map(tonumber? // 0) end | . + [range(0; 8 - length) | 0];
def match_v(rv; op):
to_v as $av |
(rv + [range(0; 8 - (rv | length)) | 0]) as $nrv |
if op == "=" then $av[0:(rv | length)] == rv
elif op == ">=" then $av >= $nrv
elif op == ">" then $av > $nrv
elif op == "<=" then $av <= $nrv
elif op == "<" then $av < $nrv
else false end;
map(select(.assets[0].version != null and (.assets[0].version | match_v($req_v; $op)))) |
sort_by([(.assets[0].version | to_v), .date, .tag_name]) | reverse | .[0]')
if [ -z "${releaseMetadata}" ] || [ "${releaseMetadata}" = "null" ]; then
printError "Could not find a release of '${name}' matching '${operator}${versioned}'"
fi
elif [ -n "${tagged}" ]; then
printVerbose "Explicit tagged version '${tagged}' specified. Checking for match."
releaseMetadata=$(/bin/printf "%s" "${releases}" | jq -e -r '.[] | select(.tag_name == "'${tagged}'")')
Expand Down Expand Up @@ -861,18 +933,26 @@ if ${all}; then
done
installArray=$(strtrim "${installArray}")
else
chosenRepos=$(echo "${chosenRepos}" | tr ',' ' ' | tr -s ' ')
chosenRepos=$(strtrim "${chosenRepos}")
invalidlist=""
for chosenRepo in $(echo "${chosenRepos}" | tr ',' ' ' | tr -s ' '); do
for chosenRepo in ${chosenRepos}; do
printVerbose "Processing repo: ${chosenRepo}"
printVerbose "Stripping any version (%), tag (#) or port suffixes"
toolrepo=$(echo "${chosenRepo}" | sed -e 's#%.*##' -e 's#=.*##')
printVerbose "Stripping any version operator (>, >=, <, <=, =), tag (%) or port suffixes"
Comment thread
sachintu47 marked this conversation as resolved.
toolrepo=$(echo "${chosenRepo}" | sed -E 's/[=><%].*//')
toolfound=$(echo "${repo_results}" | awk -vtoolrepo="${toolrepo}" '$0 == toolrepo {print}')
if [ "${toolfound}" = "${toolrepo}" ]; then
printVerbose "Adding '${chosenRepo}' to the install queue."
installArray="${installArray} ${chosenRepo}"
printVerbose "Removing valid port from input list."
chosenRepos=$(echo "${chosenRepos}" | sed -e "s#^${chosenRepo}\$##")
# Safely remove the word from the space-separated list
newChosenRepos=""
for r in ${chosenRepos}; do
Comment thread
sachintu47 marked this conversation as resolved.
if [ "$r" != "${chosenRepo}" ]; then
newChosenRepos="${newChosenRepos} $r"
fi
done
chosenRepos=$(strtrim "${newChosenRepos}")
else
invalidlist=$(/bin/printf "%s %s" "${invalidlist}" "${chosenRepo}")
fi
Expand Down
Loading