Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
@artheus
Copy link

artheus commented May 7, 2020

what about using basename?

lastversion() {
    basename $(curl -fs -o/dev/null -w %{redirect_url} $1/releases/latest)
}

@eeezae
Copy link

eeezae commented May 13, 2020

what about using basename?

lastversion() {
    basename $(curl -fs -o/dev/null -w %{redirect_url} $1/releases/latest)
}

Excellent! More concise!

@lestofante
Copy link

lestofante commented May 15, 2020

if you need a more universal answer, you can use git ls-remote --tags https://github.com/rust-lang/rust.git to get the list of all the tags from the remote without need to clone.

@artheus
Copy link

artheus commented May 17, 2020

@artheus
Copy link

artheus commented May 17, 2020

From what I can tell, the github. com/github/hub command line command will help us a lot with these things in the near future!

@lestofante
Copy link

lestofante commented May 17, 2020

@artheus yes I focus on be able to support any git repo more than specific github functionality.

Just some little correction

will only work if there are no more tags

in that case you can add some grep/awk to extrapolate only what you need.
Yes it will break if upstream change naming scheme, but on the other hand it work with any git repo, not only github

And as long as release versions are tracked as tags in the repository

i am pretty sure github (and others) create a tag for every release automatically and you cant opt-out

It would probably not differantiate between pre-releases and stable releases either.

yes you are right!
(My suggestion is to keep thin info in your naming scheme of release, so even just looking at that you know what is going on, or if you move to different service or working offline you still have those info, but of course everyone is king of its own repo)

@ikkemaniac
Copy link

ikkemaniac commented Jun 27, 2020

If you are in the repo, you can simply use: git tag | tail -1. Maybe you need to do a git fetch --tags before :).

But your approach is a good trick.

Good tip, thx! but...

might not work as git tag sorts alphabetically by default:

$ git tag
v12.6
v12.6.1
v12.7
v12.8
v2.5
v2.5.1
v2.5.2
v2.5.3

I'm using git tag --sort=committerdate | tail -1 to have it sorted by the date of the commit they're tagged to.

@ackbyte
Copy link

ackbyte commented Aug 17, 2020

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

@agambier
Copy link

agambier commented Aug 30, 2020

Very nice.
Good job !
Thanks

@coldfix
Copy link

coldfix commented Sep 4, 2020

if you need a more universal answer, you can use git ls-remote --tags https://github.com/rust-lang/rust.git to get the list of all the tags from the remote without need to clone.

Nice! This is the one that works best for me (I prefer not depending on jq and the curl <url>/releases/latest based answers only yield <url>/releases but not the tag - maybe because I didn't explicitly create releases?).

Anyway, just to make it even a little bit easier to use:

lastrelease() { git ls-remote --tags "$1" | cut -d/ -f3- | tail -n1; }

lastrelease https://github.com/USER/REPO

You may also want to add another | grep or | sed to the pipe to filter for desired tagname patterns (e.g. exclude prereleases).

@hacker65536
Copy link

hacker65536 commented Sep 18, 2020

git ls-remote --refs --sort="version:refname" --tags $repo | cut -d/ -f3-|tail -n1

@artheus
Copy link

artheus commented Sep 18, 2020

Must say I ❤️ the github community!
There are always people who takes the challenge to make something better, and shares their findings!
Keep up the great work everyone!

@edouard-lopez
Copy link

edouard-lopez commented Dec 1, 2020

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

andreasevers commented Dec 8, 2020

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

saki-osive commented Feb 17, 2021

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

Ishidres commented Apr 17, 2021

@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

CHN-STUDENT commented Jun 9, 2021

@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

spoelstraethan commented Aug 5, 2021

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

dweremeichik commented Sep 18, 2021

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

Jarmos-san commented Oct 4, 2021

+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

meramsey commented Apr 14, 2022

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

ChrisKader commented Apr 15, 2022

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'}}

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