From 7570297052cd4381d4c7eb6e3040c2a0cdc07699 Mon Sep 17 00:00:00 2001 From: Kevin O'Brien Date: Wed, 26 Nov 2025 12:06:37 -0500 Subject: [PATCH] address some edge cases remove verbose from curl --- bin/install | 92 ++++++++++++++++++++++++++++++++-------------------- bin/list-all | 50 +++++++++++++++++----------- 2 files changed, 88 insertions(+), 54 deletions(-) diff --git a/bin/install b/bin/install index f695848..1d03db9 100755 --- a/bin/install +++ b/bin/install @@ -1,18 +1,17 @@ #!/usr/bin/env bash -set -e -set -o pipefail +set -euo pipefail -ASDF_INSTALL_TYPE=${ASDF_INSTALL_TYPE:-version } +ASDF_INSTALL_TYPE=${ASDF_INSTALL_TYPE:-version} TMPDIR=${TMPDIR:-/tmp} -[ -n "$ASDF_INSTALL_VERSION" ] || (>&2 echo 'Missing ASDF_INSTALL_VERSION' && exit 1) -[ -n "$ASDF_INSTALL_PATH" ] || (>&2 echo 'Missing ASDF_INSTALL_PATH' && exit 1) +[ -n "${ASDF_INSTALL_VERSION:-}" ] || { echo 'Missing ASDF_INSTALL_VERSION' >&2; exit 1; } +[ -n "${ASDF_INSTALL_PATH:-}" ] || { echo 'Missing ASDF_INSTALL_PATH' >&2; exit 1; } # Compare two semantic versions using portable sort # Returns 0 if version1 >= version2, 1 otherwise version_gte() { - local version1=$1 - local version2=$2 + local version1="$1" + local version2="$2" # Use portable version sorting (same logic as sort_versions in list-all) local sorted_first sorted_first=$(printf '%s\n%s\n' "$version1" "$version2" | \ @@ -22,49 +21,32 @@ version_gte() { [ "$sorted_first" = "$version2" ] } -install_opencode() { - local version=$2 - local install_path=$3 - local bin_install_path="$install_path/bin" - local download_url="$(get_download_url $version)" - - mkdir -p "${install_path}" - mkdir -p "${bin_install_path}" - local bin_path="${bin_install_path}/opencode" - echo "downloading opencode ${download_url}" - if [[ "$download_url" == *.tar.gz ]]; then - curl -sL "$download_url" | tar -xz -C "${install_path}" - else - curl -L -s "$download_url" | funzip > "${install_path}/opencode" - fi - mv "${install_path}/opencode" "${bin_install_path}/opencode" - chmod +x "$bin_path" -} - get_platform() { - local platform=$(uname | tr 'A-Z' 'a-z') - echo $platform + uname | tr '[:upper:]' '[:lower:]' } get_arch() { - local arch=$(uname -m) - case $arch in + local arch + arch=$(uname -m) + case "$arch" in x86_64|amd64) echo "x64" ;; *) - echo $arch + echo "$arch" ;; esac } - get_download_url() { local version="$1" - local platform="$(get_platform)" - local arch="$(get_arch)" + local platform + local arch local ext="zip" + platform="$(get_platform)" + arch="$(get_arch)" + # Linux switched from .zip to .tar.gz starting with v1.0.92 if [ "$platform" = "linux" ]; then if version_gte "$version" "1.0.92"; then @@ -75,4 +57,44 @@ get_download_url() { echo "https://github.com/sst/opencode/releases/download/v${version}/opencode-${platform}-${arch}.${ext}" } -install_opencode $ASDF_INSTALL_TYPE $ASDF_INSTALL_VERSION $ASDF_INSTALL_PATH \ No newline at end of file +install_opencode() { + local install_type="$1" + local version="$2" + local install_path="$3" + local bin_install_path="${install_path}/bin" + local download_url + + download_url="$(get_download_url "$version")" + + # Create a secure temporary directory for download + # Use global variable so trap can access it + TMP_DOWNLOAD_DIR=$(mktemp -d "${TMPDIR}/asdf-opencode.XXXXXX") + trap 'rm -rf "${TMP_DOWNLOAD_DIR:-}"' EXIT + + mkdir -p "$install_path" + mkdir -p "$bin_install_path" + + local bin_path="${bin_install_path}/opencode" + + echo "Downloading opencode from ${download_url}" + + # Note: Upstream does not publish checksums, so verification is not possible. + # Using --fail to error on HTTP failures instead of downloading error pages. + if [[ "$download_url" == *.tar.gz ]]; then + curl -fSL "$download_url" -o "${TMP_DOWNLOAD_DIR}/opencode.tar.gz" + tar -xzf "${TMP_DOWNLOAD_DIR}/opencode.tar.gz" -C "$TMP_DOWNLOAD_DIR" + else + curl -fSL "$download_url" -o "${TMP_DOWNLOAD_DIR}/opencode.zip" + funzip "${TMP_DOWNLOAD_DIR}/opencode.zip" > "${TMP_DOWNLOAD_DIR}/opencode" + fi + + mv "${TMP_DOWNLOAD_DIR}/opencode" "$bin_path" + chmod +x "$bin_path" + + rm -rf "$TMP_DOWNLOAD_DIR" + trap - EXIT + + echo "opencode ${version} installed to ${bin_path}" +} + +install_opencode "$ASDF_INSTALL_TYPE" "$ASDF_INSTALL_VERSION" "$ASDF_INSTALL_PATH" diff --git a/bin/list-all b/bin/list-all index 1f43850..7e2605b 100755 --- a/bin/list-all +++ b/bin/list-all @@ -1,35 +1,47 @@ #!/usr/bin/env bash -releases_path=https://api.github.com/repos/sst/opencode/releases +set -euo pipefail -cmd_prefix="curl --verbose --retry 10 --retry-delay 2 -s" -if [ -n "$GITHUB_API_TOKEN" ]; then - cmd_prefix="$cmd_prefix -H 'Authorization: token $GITHUB_API_TOKEN'" +releases_path="https://api.github.com/repos/sst/opencode/releases" + +# Build curl arguments as an array to avoid eval +curl_args=(--retry 10 --retry-delay 2 -sfS) +if [ -n "${GITHUB_API_TOKEN:-}" ]; then + curl_args+=(-H "Authorization: token $GITHUB_API_TOKEN") fi next_link="${releases_path}?per_page=100&page=1" all_versions="" while [ -n "${next_link}" ]; do - - # Download releases page - cmd="${cmd_prefix} \"${next_link}\"" - cmd_out=$(eval "${cmd}" 2>&1) - - # Get versions - versions=$(echo "${cmd_out}" | grep tag_name | cut -d'"' -f4 | sed 's/^v//') - all_versions="${versions}\n${all_versions}" - - # Get next link - next_link=$(echo "${cmd_out}" | grep '< link: ' | sed 's/< link: <\(.*\)>; rel="next".*$/\1/') - next_link=$(echo "${next_link}" | sed 's/^.*<\(.*\)$/\1/') - + # Download releases page, capture headers separately + header_file=$(mktemp) + trap "rm -f '$header_file'" EXIT + + response=$(curl "${curl_args[@]}" -D "$header_file" "$next_link") || { + rm -f "$header_file" + exit 1 + } + + # Get versions from response + versions=$(echo "$response" | grep '"tag_name"' | cut -d'"' -f4 | sed 's/^v//') + all_versions="${versions}"$'\n'"${all_versions}" + + # Get next link from headers (safely parse Link header) + next_link="" + if grep -qi '^link:' "$header_file"; then + # Extract URL marked as rel="next" + next_link=$(grep -i '^link:' "$header_file" | sed -n 's/.*<\([^>]*\)>; rel="next".*/\1/p') + fi + + rm -f "$header_file" + trap - EXIT done # stolen from https://github.com/rbenv/ruby-build/pull/631/files#diff-fdcfb8a18714b33b07529b7d02b54f1dR942 -function sort_versions() { +sort_versions() { sed 'h; s/[+-]/./g; s/.p\([[:digit:]]\)/.z\1/; s/$/.z/; G; s/\n/ /' | \ LC_ALL=C sort -t. -k 1,1 -k 2,2n -k 3,3n -k 4,4n -k 5,5n | awk '{print $2}' } -echo -e "${all_versions}" | sort_versions | tr '\n' ' ' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' +echo "$all_versions" | sort_versions | tr '\n' ' ' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'