Skip to content

Instantly share code, notes, and snippets.

@lukechilds
Created August 9, 2016 19:43
Show Gist options
  • Save lukechilds/a83e1d7127b78fef38c2914c4ececc3c to your computer and use it in GitHub Desktop.
Save lukechilds/a83e1d7127b78fef38c2914c4ececc3c to your computer and use it in GitHub Desktop.
Shell - Get latest release from GitHub
get_latest_release() {
curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub api
grep '"tag_name":' | # Get tag line
sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value
}
# Usage
# $ get_latest_release "creationix/nvm"
# v0.31.4
@edouard-lopez
Copy link

In Fish you can use string manipulation that is native, so no other dependencies than curl:

function get_latest_release_version \
    --argument-names user_repo

    curl \
        --silent \
        "https://api.github.com/repos/$user_repo/releases/latest" \
    | string match --regex  '"tag_name": "\K.*?(?=")'
end

Usage:

❯ get_latest_release_version "rafaelrinaldi/pure"
v3.0.0

@andreasevers
Copy link

Why not use jq?

curl --silent "https://api.github.com/repos/$1/releases/latest" | jq -r .tag_name

@CybotTM
Copy link

CybotTM commented Jan 20, 2021

Example to get highest version, not just latest - because an LTS bugfix 1.2.3 could be released after a new major 3.x version

curl https://api.github.com/repos/mautic/mautic/releases -s | jq -r .[].tag_name | grep '^[0-9]\.[0-9]*\.[0-9]*$' | sort -nr | head -n
1

or find latest version for a specific major version:

curl https://api.github.com/repos/mautic/mautic/releases -s | jq -r .[].tag_name | grep '^2\.[0-9]*\.[0-9]*$' -m1
curl https://api.github.com/repos/mautic/mautic/releases -s | jq -r .[].tag_name | grep '^3\.[0-9]*\.[0-9]*$' -m1

@saki-osive
Copy link

Hi,
I made a version without sed, only using grep.
Maybe it's useful...

curl --silent "https://api.github.com/repos/$1/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")'

Thanks, for sharing the great idea!

Thanks!

@juliyvchirkov
Copy link

juliyvchirkov commented Apr 1, 2021

oneliner to get link to the source archive of latest release

curl -s https://github.com/USER/REPO/releases | 
    grep -m1 -Eo "archive/refs/tags/[^/]+\.tar\.gz" | 
        xargs printf "https://github.com/USER/REPO/%s"

command to clone the source of latest release to the current folder w/o downloading archive to local disk

ghRepoCloneLatestRelease ()
{
    [[ ${1} =~ / ]] &&
        wget -qO- https://github.com/${1}/$(curl -s https://github.com/${1}/releases |
            grep -m1 -Eo "archive/refs/tags/[^/]+\.tar\.gz") |
                tar --strip-components=1 -xzv >/dev/null
}

usage: ghRepoCloneLatestRelease user/repo

@Ishidres
Copy link

@juliy V. Chirkov Thanks a lot!

@juliyvchirkov
Copy link

juliyvchirkov commented Apr 18, 2021

@Ishidres, you're welcome 💁‍♀️

@KEINOS
Copy link

KEINOS commented Apr 27, 2021

$ get_latest_release "ipfs/go-ipfs"
v0.8.0

nice. Thanks a lot.

@CHN-STUDENT
Copy link

@juliyvchirkov hi, How can i get the all latest release (including windows or linux etc.) and download it to a folder?
i mean i want to write a script to check releases,if they does not exist or they are older, my script will download update it; and if they are not change, not download?

curl -s https://api.github.com/repos/user/reop/releases/latest |  /root/jq-linux64 --raw-output '.assets[] | .browser_download_url' | xargs wget 

@pwillis-els
Copy link

pwillis-els commented Jun 17, 2021

If you don't want to get just the last release, but all releases with pagination, try this. It's very ugly, I hope somebody can simplify it with awk or something 😄

next_url="https://api.github.com/repos/Versent/saml2aws/releases"
while [ -n "$next_url" ] ; do
    out="$(curl -ifsSL "$next_url")" ; echo "$out" | awk -F '"' '/"tag_name":/{print $4}' | sed -e 's/^v//'
    next_url="$(echo "$out" | grep '^link:' \
        | sed -e 's/link: //; s/, /\n/g; s/[<>]//g; s/; rel/ rel/g; s/\(https:\/\/[^ ]\+\) rel="\([a-z]\+\)"/\2 \1/g' \
        | awk '/next / { print $2}')"
done

If you do use pagination, you might prefer to use the /tags API endpoint. The data returned is about 100x smaller than /releases, and for the most part the tag name and release names are the same. Same code here, just a different URI, and use "name": instead of "tag_name": above.

Anyone have a GraphQL query for this?

@spoelstraethan
Copy link

I've used a couple of the above snippets/tools for the initial download of the GitHub CLI, but with the GitHub CLI, you can use gh release download to download releases from a specific tag, or if you don't supply a tag at all it will use the latest release (and no, latest as the tag criteria doesn't work unless it actually exists in the repository).

https://cli.github.com/manual/gh_release_download

My favorite practical example might be using gh to download the latest version of itself. I also learned a new trick for Debian/Ubuntu at least. Since uname -a shows x86_64 on 64 bit installs I've often struggled with how to detect 32/64 bit and use the correct xxx_linux_amd64.deb where since it is a deb file we can pretty safely assume we are installing on Linux, but for a .tar.gz it might be for Solaris or BSD or Linux, so we really only care about the arch.

You can see I don't supply a tag before or after the repo, and I don't quote the * otherwise it triggers the shell to try and parse it.

gh release download -D /tmp -R cli/cli --pattern *$(dpkg --print-architecture).deb
sudo apt install /tmp/gh_*

@dweremeichik
Copy link

For PowerShell/Windows users, a better approach:

((Invoke-WebRequest -Uri https://api.github.com/repos/USER/REPO/releases/latest).Content | ConvertFrom-Json).tag_name

@Jarmos-san
Copy link

+1 for @spoelstraethan's suggestion! gh is easy to use & makes downloading the latest release asset an absolute breeze.

Would suggest using that instead of reinventing the wheel & using hacky Shell scripts on any day! And the best part it's available on Homebrew & on Windows so no worries about cross-platform support.

@KEINOS
Copy link

KEINOS commented Oct 5, 2021

Indeed gh is easier to use with no work-around, but I found this gist is useful for lightweight purposes such as Docker and CIs. Thanks!

@wgzhao
Copy link

wgzhao commented Nov 5, 2021

Hi, I made a version without sed, only using grep. Maybe its useful...

curl --silent "https://api.github.com/repos/$1/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")'

Thanks, for sharing the great idea! 👍

In MacOS, the built-in grep command has not -P option.

@meramsey
Copy link

Another mostly reusable one if you just want the latest deb

owner_repo='igniterealtime/Spark'; latest_version_url="$(curl -s https://api.github.com/repos/$owner_repo/releases/latest | grep "browser_download_url.*deb" | cut -d : -f 2,3 | tr -d \")"; echo $latest_version_url; basename $latest_version_url ; wget --content-disposition $latest_version_url

Example:

❯ owner_repo='igniterealtime/Spark'; latest_version_url="$(curl -s https://api.github.com/repos/$owner_repo/releases/latest | grep "browser_download_url.*deb" | cut -d : -f 2,3 | tr -d \")"; echo $latest_version_url; basename $latest_version_url ; wget -q --content-disposition $latest_version_url
https://github.com/igniterealtime/Spark/releases/download/v3.0.0-beta/spark_3_0_0-beta.deb
spark_3_0_0-beta.deb
~  took 1m46s  at 20:14:43 

❯ 

@ChrisKader
Copy link

Example Link: https://github.com/valinet/ExplorerPatcher/releases/latest/download/ep_setup.exe

Replace "ep_setup.exe" with whatever object name you want.

@meramsey
Copy link

meramsey commented Jun 3, 2022

Probably not Pythonic but Python way:

import requests
url = 'https://github.com/roundcube/roundcubemail/releases/latest'
r = requests.get(url)
version = r.url.split('/')[-1]

print(version)

Source: https://gist.github.com/zeldor/604a7817f9d142e908335e041ece0718

Made into a function and returns empty for the ones without it.

def get_github_latest_release(repo_path):
    import requests

    url = f"https://github.com/{repo_path}/releases/latest"
    r = requests.get(url, headers={"Content-Type": "application/vnd.github.v3+json"})
    release = r.url.split("/")[-1]
    if release in ["releases", "latest"]:
        release = ""

    return release

works nicely for ones with releases

github_repos = [
    "https://github.com/OpenVPN/openvpn",
    "https://github.com/atom/atom",
    "https://github.com/eneshecan/whatsapp-for-linux",
    "https://github.com/igniterealtime/Openfire",
    "https://github.com/igniterealtime/Spark",
    "https://github.com/jitsi/jitsi-meet",
    "https://github.com/jitsi/jitsi-meet-prosody",
    "https://github.com/jitsi/jitsi-meet-turnserver",
    "https://github.com/jitsi/jitsi-meet-web-config",
    "https://github.com/jitsi/jitsi-videobridge2",
    "https://github.com/mumble-voip/mumble",
    "https://github.com/pjsip/pjproject/",
    "https://github.com/pydio/pydio-core",
    "https://github.com/signalapp/Signal-Desktop",
    "https://github.com/sleuthkit/autopsy",
    "https://github.com/sleuthkit/sleuthkit",
    "https://github.com/ultravnc/ultravnc",
]


def get_repo_path(link):
    return link.replace("https://github.com/", "").strip().rstrip("/")


def get_repo_pkg(repo):
    return repo.split("/")[-1]


def get_repos_dict_from_urls(urls):
    packages = {}
    for repo in github_repos:
        pkg_name = get_repo_pkg(get_repo_path(repo))
        pkg_path = get_repo_path(repo)
        packages[pkg_name] = {
            "name": pkg_name,
            "repo_path": pkg_path,
            "url": repo,
            "latest_release": get_github_latest_release(pkg_path),
        }
    return packages


packages = get_repos_dict_from_urls(github_repos)
pprint(packages)

Result:

{'Openfire': {'latest_release': 'v4.7.1',
              'name': 'Openfire',
              'repo_path': 'igniterealtime/Openfire',
              'url': 'https://github.com/igniterealtime/Openfire'},
 'Signal-Desktop': {'latest_release': 'v5.45.0',
                    'name': 'Signal-Desktop',
                    'repo_path': 'signalapp/Signal-Desktop',
                    'url': 'https://github.com/signalapp/Signal-Desktop'},
 'Spark': {'latest_release': 'v3.0.0-beta',
           'name': 'Spark',
           'repo_path': 'igniterealtime/Spark',
           'url': 'https://github.com/igniterealtime/Spark'},
 'atom': {'latest_release': 'v1.60.0',
          'name': 'atom',
          'repo_path': 'atom/atom',
          'url': 'https://github.com/atom/atom'},
 'autopsy': {'latest_release': 'autopsy-4.19.3',
             'name': 'autopsy',
             'repo_path': 'sleuthkit/autopsy',
             'url': 'https://github.com/sleuthkit/autopsy'},
 'jitsi-meet': {'latest_release': 'jitsi-meet_7287',
                'name': 'jitsi-meet',
                'repo_path': 'jitsi/jitsi-meet',
                'url': 'https://github.com/jitsi/jitsi-meet'},
 'jitsi-meet-prosody': {'latest_release': '',
                        'name': 'jitsi-meet-prosody',
                        'repo_path': 'jitsi/jitsi-meet-prosody',
                        'url': 'https://github.com/jitsi/jitsi-meet-prosody'},
 'jitsi-meet-turnserver': {'latest_release': '',
                           'name': 'jitsi-meet-turnserver',
                           'repo_path': 'jitsi/jitsi-meet-turnserver',
                           'url': 'https://github.com/jitsi/jitsi-meet-turnserver'},
 'jitsi-meet-web-config': {'latest_release': '',
                           'name': 'jitsi-meet-web-config',
                           'repo_path': 'jitsi/jitsi-meet-web-config',
                           'url': 'https://github.com/jitsi/jitsi-meet-web-config'},
 'jitsi-videobridge2': {'latest_release': '',
                        'name': 'jitsi-videobridge2',
                        'repo_path': 'jitsi/jitsi-videobridge2',
                        'url': 'https://github.com/jitsi/jitsi-videobridge2'},
 'mumble': {'latest_release': 'v1.4.230',
            'name': 'mumble',
            'repo_path': 'mumble-voip/mumble',
            'url': 'https://github.com/mumble-voip/mumble'},
 'openvpn': {'latest_release': '',
             'name': 'openvpn',
             'repo_path': 'OpenVPN/openvpn',
             'url': 'https://github.com/OpenVPN/openvpn'},
 'pjproject': {'latest_release': '2.12.1',
               'name': 'pjproject',
               'repo_path': 'pjsip/pjproject',
               'url': 'https://github.com/pjsip/pjproject/'},
 'pydio-core': {'latest_release': '',
                'name': 'pydio-core',
                'repo_path': 'pydio/pydio-core',
                'url': 'https://github.com/pydio/pydio-core'},
 'sleuthkit': {'latest_release': 'sleuthkit-4.11.1',
               'name': 'sleuthkit',
               'repo_path': 'sleuthkit/sleuthkit',
               'url': 'https://github.com/sleuthkit/sleuthkit'},
 'ultravnc': {'latest_release': '',
              'name': 'ultravnc',
              'repo_path': 'ultravnc/ultravnc',
              'url': 'https://github.com/ultravnc/ultravnc'},
 'whatsapp-for-linux': {'latest_release': 'v1.4.3',
                        'name': 'whatsapp-for-linux',
                        'repo_path': 'eneshecan/whatsapp-for-linux',
                        'url': 'https://github.com/eneshecan/whatsapp-for-linux'}}

@begin-theadventure
Copy link

begin-theadventure commented Sep 1, 2022

owner_repo='igniterealtime/Spark'; latest_version_url="$(curl -s https://api.github.com/repos/$owner_repo/releases/latest | grep "browser_download_url.*deb" | cut -d : -f 2,3 | tr -d ")"; echo $latest_version_url; basename $latest_version_url ; wget --content-disposition $latest_version_url

Thanks a lot! Although it's possible to just use curl instead of wget:
owner_repo='owner/name'; latest_version_url="$(curl -s https://api.github.com/repos/$owner_repo/releases/latest| grep "browser_download_url.*extension" | cut -d : -f 2,3 | tr -d \")"; echo $latest_version_url; basename $latest_version_url ; curl -X GET $latest_version_url -LO or -Lo name.extension

@meramsey
Copy link

meramsey commented Sep 1, 2022

owner_repo='igniterealtime/Spark'; latest_version_url="$(curl -s https://api.github.com/repos/$owner_repo/releases/latest | grep "browser_download_url.*deb" | cut -d : -f 2,3 | tr -d ")"; echo $latest_version_url; basename $latest_version_url ; wget --content-disposition $latest_version_url

Thanks a lot! Although it's possible to just use curl instead of wget: owner_repo='owner/name'; latest_version_url="$(curl -s https://api.github.com/repos/$owner_repo/releases/latest| grep "browser_download_url.*extensions" | cut -d : -f 2,3 | tr -d \")"; echo $latest_version_url; basename $latest_version_url ; curl -X GET $latest_version_url -LO or -Lo name.extension

Awesome thanks for sharing. That is cool always wondered how to do that in curl but never bothered to look. Appreciate the share and explanation.

@pitoneux
Copy link

pitoneux commented Sep 20, 2022

With the last update, the above breaks for me. I'm now using this:

latest_version_raw="$(curl -s https://api.github.com/repos/$owner_repo/releases | grep -m 1 "html_url" | rev | cut -    d/ -f1 | rev  )"
latest_version="${latest_version_raw%??}" # remove last 2 characters

@rotty3000
Copy link

rotty3000 commented Sep 26, 2022

Here's an example with wget & jq:

REPO="..."
VERSION=$(wget -q -O- https://api.github.com/repos/${REPO}/releases/latest | jq -r '.name')

@roelds
Copy link

roelds commented Jan 1, 2023

Here's an example with wget & jq:

REPO="..."
VERSION=$(wget -q -O- https://api.github.com/repos/${REPO}/releases/latest | jq -r '.name')

that works great! but if prefer tag name instead of release name, use:

.tag_name

@roelds
Copy link

roelds commented Jan 1, 2023

for how todo this on GitLab site, see my gist here:
https://gist.github.com/roelds/b2cd9cc2ba6c7887ddaf6bde2ef7ef50

@fonic
Copy link

fonic commented Aug 26, 2023

One-liner using only curl + grep:
curl --silent "https://api.github.com/repos/${REPO}/releases/latest" | grep -Po "(?<=\"tag_name\": \").*(?=\")"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment