Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
SamuelMarks opened this issue Aug 4, 2024 · 1 comment
Assignees

Comments

@SamuelMarks
Copy link

SamuelMarks commented Aug 4, 2024

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}"
@sagiegurari
Copy link
Owner

as mentioned in #435 i don't see any bug. seems that access is just fine so double check your code against mine and the docs.

as for adding new commands, its possible to just PR this.
there are 2 ways for this,

  1. create a duckscript as you did (just not OS binary dependencies please) and wrap it as a command (like array concat command is actually implemented as duckscript)
  2. create a rust command (probably better in this case)

and just put it under src/sdk/std/scm/github or something.

and make sure to keep it super simple and not sophisticated and push basic options

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants