Skip to content

Builtin function for downloading latest||specific release for your OS from GitHub #434

@SamuelMarks

Description

@SamuelMarks

Feature Description

It seems like a very common use-case.

Describe The Solution You'd Like

Here are two examples for getting the latest version:

rye_version="$(curl -s https://api.github.com/repos/astral-sh/rye/tags | jq -r .[0].name)"
oxen_version="$(curl -s https://api.github.com/repos/Oxen-AI/Oxen/releases/latest | jq -r .tag_name)"

arch='my-arch'

Now let's continue with just rye:

# https://github.com/astral-sh/rye/releases/tag/0.38.0
filename='rye-'"$ARCH"'-'"$OS"'.gz'
curl -OL 'https://github.com/astral-sh/rye/releases/download/'"$rye_version"'/'"$filename"
# you could also download the checksum and check it, then:
tar xf "$rye_version"
# then you might `install` this somewhere

Code Sample

*EDIT rewrote with jq because of #435. Can you embed jaq into duckscript? - Or fix that bug?

#!/usr/bin/env duck

fn get_tag_name_from_github
    if eq "${2}" "latest"
       echo "GET https://api.github.com/repos/${1}/tags"
       tags = http_client "https://api.github.com/repos/${1}/tags"
       json = json_parse ${tags}
       return "${json[0].name}"
    end

    return "${2}"
end

fn download_from_github
    download_attempts = set 3
    # First get `arch` and `os`
    if is_windows
      arch = get_env PROCESSOR_ARCHITECTURE
      if eq "${arch}" "AMD64"
        arch = set "x86_64"
      end
      os = set "windows"
      # ext = set ".exe"
    else
      os = os_family
      arch = exec --fail-on-error "uname" "-m"
      arch = trim "${arch.stdout}"
      # ext = set ".gz"
    end

    org = set "${1}"
    repo = set "${2}"
    version = set "${3}"
    # output_file = set "${repo}-${arch}-${os}${ext}"

    releases_file = set "./${org}_${repo}_${version}_releases.json"

:redownload

    if not is_file "${releases_file}"
        releases_url = set "https://api.github.com/repos/${org}/${repo}/releases/tags/${version}"
        echo "GET ${releases_url}"
        releases = http_client --output-file "${releases_file}" "${releases_url}"
        if not "${releases}"
          echo "unable to download ${releases_url} to ${releases_file}"
          exit 3
        end
    end
    releases = readfile "${releases_file}"

    org = set "${1}"
    repo = set "${2}"
    version = set "${3}"

#   releases = json_parse "${releases}"
#
#    candidate_url = set ""
#    candidate_name = set ""
#    releases_assets_range = range 0 ${releases.assets.length}
#
#    for i in ${releases_assets_range}
#      release = get_by_name releases.assets[${i}]
#      if eq ${os} "windows"
#          # TODO: .msi, .cmd, .zip
#          if eq content_type "application/x-msdownload"
#             if contains ${release.name} ${arch}
#                candidate_url = set ${release.url}
#                candidate_name = set ${release.name}
#                break
#             else if eq ${candidate_url} ""
#                candidate_url = set ${release.url}
#                candidate_name = set ${release.name}
#             end
#          else eq ${candidate_url} "" and ( contains ${release.name} ${os} or contains ${release.name} "win" )
#             candidate_url = set ${release.url}
#             candidate_name = set ${release.name}
#          end
#      else if eq ${os} "linux"
#          # TODO: .deb, .rpm
#          if contains ${release.name} ${os} or contains ${release.name} "Linux"
#             if contains ${release.name} ${arch}
#                candidate_url = set ${release.url}
#                candidate_name = set ${release.name}
#                break
#             else if eq ${candidate_url} ""
#                candidate_url = set ${release.url}
#                candidate_name = set ${release.name}
#             end
#          end
#      else if eq ${os} "macOS"
#          # TODO: .dmg, .pkg
#          if contains ${release.name} "mac" or contains ${release.name} "Mac"
#             if contains ${release.name} ${arch}
#                candidate_url = set ${release.url}
#                candidate_name = set ${release.name}
#                break
#             else if eq ${candidate_url} ""
#                candidate_url = set ${release.url}
#                candidate_name = set ${release.name}
#             end
#          end
#      end
#    end

    jq_str = set ".assets[] | pick(.name, .size, .content_type, .browser_download_url)"
    if eq "${os}" "windows"
      candidate = exec jq -r --arg arch "${arch}" "${jq_str} | select(.content_type == \"application/x-msdownload\" and (.name | contains(\"${arch}\")))" "${releases_file}"
    else if eq "${os}" "linux"
      candidate = exec jq -r --arg arch "${arch}" "${jq_str} | select(.content_type == \"application/gzip\" and (.name | contains(\"${arch}\")) and (.name | contains(\"${arch}\")) and (.name | ascii_downcase | contains(\"linux\")))" "${releases_file}"
    else if eq "${os}" "macos"
      candidate = exec jq -r --arg arch "${arch}" "${jq_str} | select(.content_type == \"application/gzip\" and (.name | contains(\"${arch}\")) and (.name | contains(\"${arch}\")) and (.name | ascii_downcase | contains(\"mac\")))" "${releases_file}"
    end
    candidate = trim "${candidate.stdout}"
    candidate = json_parse "${candidate}"
    download_filename = set "${candidate.name}"
    size = set "${candidate.size}"
    content_type = set "${candidate.content_type}"
    browser_download_url = set "${candidate.browser_download_url}"

    if not is_defined browser_download_url
      echo "Unable to determine download URL for your OS and architecture"
      exit 2
    end
    cwd = pwd
    target = join_path "${cwd}" / "${download_filename}"
    downloaded = get_file_size "${target}"
    if not eq "${size}" "${downloaded}"
        echo "GET ${browser_download_url}"
        downloaded = http_client --output-file "${download_filename}" "${browser_download_url}"
    end

    # could do proper checksum checking here after this check
    if not eq "${downloaded}" "${size}"
       echo "Expected ${size} downloaded got ${downloaded}"
       rm "${download_filename}"
       download_attempts = calc "${download_attempts}" - 1
       if eq "${download_attempts}" 0
          echo "All attempts to download failed."
          exit 3
       else
          rm "${releases_file}"
          goto :redownload
       end
    end
    # rm 
end

org = set "astral-sh"
repo = set "rye"
version = get_tag_name_from_github "${org}/${repo}" "latest"
# version = set "0.38.0"
download_from_github "${org}" "${repo}" "${version}"

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions