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
@hiulit

This comment has been minimized.

Copy link

@hiulit hiulit commented Dec 18, 2017

Nice one! Thanks!

@santimendoza

This comment has been minimized.

Copy link

@santimendoza santimendoza commented Jan 16, 2018

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.

@peschu123

This comment has been minimized.

Copy link

@peschu123 peschu123 commented Apr 11, 2018

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! 👍

@jbelloncastro

This comment has been minimized.

Copy link

@jbelloncastro jbelloncastro commented Apr 30, 2018

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

You can also get the download link directly:
jq -r '.assets[0].browser_download_url
or by name:
jq -r '.assets[] | select(.name == "nameofobject").browser_download_url')
(replace nameofobject with whatever you want to get).

@robwithhair

This comment has been minimized.

Copy link

@robwithhair robwithhair commented Jul 17, 2018

To get latest tag, for example if the repo doesn't utilise GitHub releases.

function get_latest_release() {
    curl --silent "https://api.github.com/repos/certbot/certbot/tags" | jq -r '.[0].name'
}
@espoelstra

This comment has been minimized.

Copy link

@espoelstra espoelstra commented Jul 19, 2018

I combined all the resources I found on the subject to come up with a one-liner to grab docker-compose assuming you have jq installed.

curl --silent "https://api.github.com/repos/docker/compose/releases/latest" | jq --arg PLATFORM_ARCH "$(echo `uname -s`-`uname -m`)" -r '.assets[] | select(.name | endswith($PLATFORM_ARCH)).browser_download_url' | xargs sudo curl -L -o /usr/local/bin/docker-compose --url

This could easily be put into a function where you pass the name of the repository and the platform or architecture you want to download. Sadly for a lot of releases it could be anything, ie amd64, x86_64, i386, i586, i686, or others depending on the packaging tool they used. You can use select(.name | test "Linux*64") or some similar regex in that case to get pretty close, or just return a list of releases and have the user select the one they want via ncurses/zenity/etc.

@haruo31

This comment has been minimized.

Copy link

@haruo31 haruo31 commented Sep 25, 2018

Thanks a lot!! It worked perfectly on my problem!! I made a shell function to find assets of latest release that will work with python 3.x.

getLatest() {
  REPO=$1
  SUFFIX=$2
  curl --silent https://api.github.com/repos/$REPO/releases/latest | \
    python -c "import sys; from json import loads as l; x = l(sys.stdin.read()); print(''.join(s['browser_download_url'] for s in x['assets'] if s['name'].endswith('.$SUFFIX')))"
}
@HaleTom

This comment has been minimized.

Copy link

@HaleTom HaleTom commented Oct 15, 2018

Can be done with git ls-remote and sed too.

@chrcoluk

This comment has been minimized.

Copy link

@chrcoluk chrcoluk commented Nov 2, 2018

only the original is valid

if I try all the others I get "jq not found", jq is more bloated than sed so I dont see the logic in replacing sed with jq

-P is invalid flag for grep

The problem with the original is it outputs with v at the start which needs removing, otherwise its perfect.

append this to end cut -c 2-

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 cut -c 2- # Remove v

@NathanUrwin

This comment has been minimized.

Copy link

@NathanUrwin NathanUrwin commented Nov 13, 2018

See https://stackoverflow.com/questions/3074288/get-url-after-curl-is-redirected

An easier way:

$ curl -fsSLI -o /dev/null -w %{url_effective} https://github.com/hadolint/hadolint/releases/latest && echo
https://github.com/hadolint/hadolint/releases/tag/v1.15.0
@mathieu-aubin

This comment has been minimized.

Copy link

@mathieu-aubin mathieu-aubin commented Nov 16, 2018

@chrcoluk

if I try all the others I get "jq not found" ....

That's because it's not installed or in your $PATH

... jq is more bloated than sed so I dont see the logic in replacing sed with jq

jq is BY FAR alot more efficient than anything else at processing JSON.

-P is invalid flag for grep

From grep --help:

  -P, --perl-regexp         PATTERN is a Perl regular expression

PCRE - Perl Compatible Regular Expressions

The problem with the original is it outputs with v at the start which needs removing, otherwise its perfect.

How would that be a problem? Im curious.

append this to end cut -c 2-

Using cut makes alot less sense than the above quotes

From @peschu123 Post's

grep -Po '"tag_name": "\K.*?(?=")'

to

grep -Po '"tag_name": "v\K.*?(?=")'

A single character gets rid of your ennemy.

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 cut -c 2- # Remove v

By the looks of this one, it also seems you could use a Markdown syntax refresher course, dear sysadmin

@oslivan

This comment has been minimized.

Copy link

@oslivan oslivan commented Nov 30, 2018

Very nice.

@hoto

This comment has been minimized.

Copy link

@hoto hoto commented Dec 14, 2018

Perfect. Thanks.

@Zeka13

This comment has been minimized.

Copy link

@Zeka13 Zeka13 commented Dec 24, 2018

awesome

@paulequilibrio

This comment has been minimized.

Copy link

@paulequilibrio paulequilibrio commented Dec 24, 2018

See https://stackoverflow.com/questions/3074288/get-url-after-curl-is-redirected

An easier way:

$ curl -fsSLI -o /dev/null -w %{url_effective} https://github.com/hadolint/hadolint/releases/latest && echo
https://github.com/hadolint/hadolint/releases/tag/v1.15.0

Thanks a lot for that!

@wweir

This comment has been minimized.

Copy link

@wweir wweir commented Jan 19, 2019

curl -s "https://api.github.com/repos/$REPO/releases/latest" | awk -F '"' '/tag_name/{print $4}'

@vwal

This comment has been minimized.

Copy link

@vwal vwal commented Jan 30, 2019

Anyone know why this doesn't work on aws/aws-cli releases? It has releases, but

curl "https://api.github.com/repos/aws/aws-cli/releases/"

produces:

{
  "message": "Not Found",
  "documentation_url": "https://developer.github.com/v3/repos/releases/#get-the-latest-release"
}
@dan-compton

This comment has been minimized.

Copy link

@dan-compton dan-compton commented Feb 3, 2019

@wval

curl "https://api.github.com/repos/aws/aws-cli/tags" | jq '.[].name'          
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--100 12643  100 12643    0     0  17366      0 --:--:-- --:--:-- --:--:-- 17366
"2.0.0dev0"
"1.16.96"
"1.16.95"
"1.16.94"
"1.16.93"
"1.16.92"
"1.16.91"
"1.16.90"
"1.16.89"
"1.16.88"
"1.16.87"
"1.16.86"
"1.16.85"
"1.16.84"
"1.16.83"
"1.16.82"
"1.16.81"
"1.16.80"
"1.16.79"
"1.16.78"
"1.16.77"
"1.16.76"
"1.16.75"
"1.16.74"
"1.16.73"
"1.16.72"
"1.16.71"
"1.16.70"
"1.16.69"
"1.16.68"
@nouchka

This comment has been minimized.

Copy link

@nouchka nouchka commented Feb 4, 2019

@maboloshi

This comment has been minimized.

Copy link

@maboloshi maboloshi commented Feb 17, 2019

get_latest_release_number() {
  curl --silent "https://github.com/$1/releases/latest" | sed 's#.*tag/\(.*\)\".*#\1#'
}

# Usage
# $ get_latest_release_number "creationix/nvm"
# v0.34.0
@michaeltryby

This comment has been minimized.

Copy link

@michaeltryby michaeltryby commented Mar 5, 2019

Windows cmd no dependencies ...

FOR /F delims^=^"^ tokens^=2 %%g IN ('curl --silent %LATEST_URL%') DO ( set "LATEST_TAG=%%~nxg" )

@bitmvr

This comment has been minimized.

Copy link

@bitmvr bitmvr commented Mar 6, 2019

A more pure approach with no dependencies on jq.

Replace ${user} with the user/organization name and ${repository} with the project name in the one-liner below.

latest_tag_url=$(curl -sI https://github.com/${user}/${repository}/releases/latest | grep -iE "^Location:"); echo "${latest_tag_url##*/}"

What is it doing?

That "location url" is a part of the header, so I pass -I to get the headers and then grep for Location:. This provides the url for the latest tag. I wrapped the entire curl and grep operations to perform command substitution and assign it with a variable of latest_tag_url. I then echo the latest_tag_url variable and perform substring removal to show only the tag at the end of the url.

Real Example:

> latest_tag_url=$(curl -sI https://github.com/stedolan/jq/releases/latest | grep -iE "^Location:"); echo "${latest_tag_url##*/}"
jq-1.6
@michaeltryby

This comment has been minimized.

Copy link

@michaeltryby michaeltryby commented Mar 7, 2019

This is a bit cleaner ...
LATEST_TAG="$(curl -sI "${LATEST_URL}" | grep -Po 'tag\/\K(v\S+)')"

@randygrolemund

This comment has been minimized.

Copy link

@randygrolemund randygrolemund commented Mar 31, 2019

How about getting the release date information? I am trying to get version, and release date. I've got the version number parsed and cleaned up. Can someone please point me in the right direction?

@dvershinin

This comment has been minimized.

Copy link

@dvershinin dvershinin commented May 17, 2019

This is all too weak you guys. As mentioned by many - GitHub API won't return actual Releases in many cases when releases were not filed formally. Those releases that don't appear are not less releases than the others :)

Thus I have created lastversion CLI tool.

How about this one liner:

lastversion user/repo 

Will give you the version of latest release even if it's not present in the API response! :)

@3Dcube

This comment has been minimized.

Copy link

@3Dcube 3Dcube commented Jun 12, 2019

# Get final URL after curl is redirected
RELEASE_URL=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/docker/compose/releases/latest)
# Extract tag after the last forward slash 
TAG="${RELEASE_URL##*/}"
@Firedrops

This comment has been minimized.

Copy link

@Firedrops Firedrops commented Jun 25, 2019

Thanks a lot!! It worked perfectly on my problem!! I made a shell function to find assets of latest release that will work with python 3.x.

getLatest() {
  REPO=$1
  SUFFIX=$2
  curl --silent https://api.github.com/repos/$REPO/releases/latest | \
    python -c "import sys; from json import loads as l; x = l(sys.stdin.read()); print(''.join(s['browser_download_url'] for s in x['assets'] if s['name'].endswith('.$SUFFIX')))"
}

I have adapted this code for one of my use cases, in which the criteria to select for is the ".tgz" extension (as opposed to .zip etc for windows).

curl -s "https://api.github.com/repos/beast-dev/beast-mcmc/releases/latest" | jq --arg PLATFORM_ARCH "tgz" -r '.assets[] | select(.name | endswith($PLATFORM_ARCH)).browser_download_url' | xargs curl -L -o /beast1.tgz

This works as expected and downloads the release as beast1.tgz.

However, another use case is more complicated, its releases have many versions, separated by format (e.g. .tgz .zip) and inclusion of JRE. I'm trying to select for the .tgz WITHOUT JRE, which is denoted in the middle of the string (BEAST_with_JRE.v2.5.2.Linux.tgz vs BEAST.v2.5.2.Linux.tgz). I've attempted to use the wildcard, i.e. PLATFORM_ARCH "BEAST.v*.tgz" but this does not work. I've tried Googling "curl select endswith" to try and find the 'language' to find alternatives to 'endswith', but all I get are java methods which don't fit with this format.

Could anyone please give me a little hand here and point me in the right direction?

@andrew-aladev

This comment has been minimized.

Copy link

@andrew-aladev andrew-aladev commented Aug 22, 2019

Another variant that helps to install last version of dxvk before running Steam:

#!/bin/sh

archive_path="/tmp/dxvk"
mkdir -p "$archive_path"

curl -s "https://api.github.com/repos/doitsujin/dxvk/releases/latest" | \
  grep -Po "['\"]browser_download_url['\"]\s*:\s*['\"]\K(.*)(?=['\"])" | \
  xargs -n1 curl -sL | \
  tar xvfz - -C "$archive_path" --strip 1 > /dev/null

sh "$archive_path/setup_dxvk.sh" install

wine Steam.exe -no-cef-sandbox
@dvershinin

This comment has been minimized.

Copy link

@dvershinin dvershinin commented Aug 22, 2019

@andrew-aladev or, using lastversion

#!/bin/sh

archive_path="/tmp/dxvk"
mkdir -p "$archive_path"

curl -sL $(lastversion --source doitsujin/dxvk) | \
  tar xvfz - -C "$archive_path" --strip 1 > /dev/null

sh "$archive_path/setup_dxvk.sh" install

wine Steam.exe -no-cef-sandbox

which will take care of things if repo maintainer forgets to tag a formal release.

@jdoshi1

This comment has been minimized.

Copy link

@jdoshi1 jdoshi1 commented Oct 3, 2019

See https://stackoverflow.com/questions/3074288/get-url-after-curl-is-redirected

An easier way:

$ curl -fsSLI -o /dev/null -w %{url_effective} https://github.com/hadolint/hadolint/releases/latest && echo
https://github.com/hadolint/hadolint/releases/tag/v1.15.0

Thank you for this!

Building on this, we can use basename to get just the tag value. Don't need to install jq on your system.

basename $(curl -Ls -o /dev/null -w %{url_effective} ${LATEST_GITHUB_RELEASE_URL}

# Example:
basename $(curl -Ls -o /dev/null -w %{url_effective} https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/latest
v0.4.0
@archf

This comment has been minimized.

Copy link

@archf archf commented Oct 8, 2019

My attempt to solve this: ghi.

It aims to be the reverse operation of ghr CLI tool. More improvements can be done of course. Let me know what you think!

@islander

This comment has been minimized.

Copy link

@islander islander commented Dec 27, 2019

My solution for ansible playbooks. But it has python-jmespath dependency for json_query filter:

- name: Get urls for latest release of a heplify-server
  uri:
    url: https://api.github.com/repos/sipcapture/heplify-server/releases/latest
    body_format: json
    return_content: true
  changed_when: false
  register: helplify_server_version

- name: Parse heplify server binary url
  set_fact:
    heplify_server_url: "{{ helplify_server_version.json | json_query(query) | first }}"
  vars:
    query: "assets[?name=='heplify-server'].browser_download_url"

- debug:
    msg: "{{ heplify_server_url }}"

Produces:

TASK [test-role : debug] *****************************************************
 ok: [instance] => {
     "msg": "https://github.com/sipcapture/heplify-server/releases/download/1.13/heplify-server"
 }


@wiz

This comment has been minimized.

Copy link

@wiz wiz commented Jan 3, 2020

My hero

@KharmaScribbles

This comment has been minimized.

Copy link

@KharmaScribbles KharmaScribbles commented Mar 7, 2020

@dvershinin thank you for that tool! Just what I need and seems so much more simple than trying to use these various other approaches.

If only everything could be so universal!

"There is no consistency in human beings."
~~ https://github.com/dvershinin/lastversion

@leiless

This comment has been minimized.

Copy link

@leiless leiless commented Mar 15, 2020

A somewhat simpler one:

curl --silent https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4
@trentmillar

This comment has been minimized.

Copy link

@trentmillar trentmillar commented Mar 30, 2020

Anyone know why this doesn't work on aws/aws-cli releases? It has releases, but

curl "https://api.github.com/repos/aws/aws-cli/releases/"

produces:

{
  "message": "Not Found",
  "documentation_url": "https://developer.github.com/v3/repos/releases/#get-the-latest-release"
}

It's your trailing slash
curl "https://api.github.com/repos/aws/aws-cli/releases"

@dvershinin

This comment has been minimized.

Copy link

@dvershinin dvershinin commented Mar 30, 2020

@trentmillar this is due to API limitations. You can use lastversion, e.g.:

lastversion https://github.com/aws/aws-cli/
#> 1.18.31
@trentmillar

This comment has been minimized.

Copy link

@trentmillar trentmillar commented Mar 30, 2020

@trentmillar this is due to API limitations. You can use lastversion, e.g.:

lastversion https://github.com/aws/aws-cli/
#> 1.18.31

No, but I get his question and my response to it could be off topic, I was replying to @vwal's issue where he was attempting to hit an unknown route because he appended "/" at the end of the endpoint.

If you simply attempt to execute curl "https://api.github.com/repos/aws/aws-cli/releases/" which he did you will get "Not found" if you change it by dropping the trailing "/" it hits the endpoint. That's all

@TrustMe00

This comment has been minimized.

Copy link

@TrustMe00 TrustMe00 commented Apr 9, 2020

thanks you !

@artheus

This comment has been minimized.

Copy link

@artheus artheus commented May 7, 2020

what about using basename?

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

This comment has been minimized.

Copy link

@eeezae 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

This comment has been minimized.

Copy link

@lestofante 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

This comment has been minimized.

Copy link

@artheus artheus commented May 17, 2020

@artheus

This comment has been minimized.

Copy link

@artheus 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

This comment has been minimized.

Copy link

@lestofante 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

This comment has been minimized.

Copy link

@ikkemaniac 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.

@zeldor

This comment has been minimized.

Copy link

@zeldor zeldor 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

This comment has been minimized.

Copy link

@agambier agambier commented Aug 30, 2020

Very nice.
Good job !
Thanks

@coldfix

This comment has been minimized.

Copy link

@coldfix 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

This comment has been minimized.

Copy link

@hacker65536 hacker65536 commented Sep 18, 2020

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

This comment has been minimized.

Copy link

@artheus 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.