Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
One Liner to Download the Latest Release from Github Repo
  • Use curl to get the JSON response for the latest release
  • Use grep to find the line containing file URL
  • Use cut and tr to extract the URL
  • Use wget to download it
curl -s https://api.github.com/repos/jgm/pandoc/releases/latest \
| grep "browser_download_url.*deb" \
| cut -d : -f 2,3 \
| tr -d \" \
| wget -qi -
@qiwihui

This comment has been minimized.

Copy link

@qiwihui qiwihui commented Jul 30, 2017

可以只用 cut 分割:

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest \
| grep "browser_download_url.*deb" \
| cut -d '"' -f 4 \
| wget -qi -
@aaronnguyen

This comment has been minimized.

Copy link

@aaronnguyen aaronnguyen commented Aug 9, 2017

Wildcard didn't work on Docker ubuntu:latest
But this did. just broke out the greps.

curl -s https://api.github.com/repos/mozilla/geckodriver/releases/latest \
  | grep browser_download_url \
  | grep linux64 \
  | cut -d '"' -f 4 \
  | wget -qi -
@ozbillwang

This comment has been minimized.

Copy link

@ozbillwang ozbillwang commented Mar 1, 2018

why some repos supports releases, some not?

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

@rlewkowicz

This comment has been minimized.

Copy link

@rlewkowicz rlewkowicz commented Mar 1, 2018

@ozbillwang I've noticed this when a repo is mirrored vs native to github

@w0rd-driven

This comment has been minimized.

Copy link

@w0rd-driven w0rd-driven commented Apr 18, 2018

In case anyone else stumbles upon this stupidly useful technique, the one thing that varies between projects is the grep line. browser_download_url.*deb looks for files ending in .deb. You likely need to tailor just this line for most repositories.

@JedMeister

This comment has been minimized.

Copy link

@JedMeister JedMeister commented May 28, 2018

This looks really cool, but as noted by @ozbillwang and @rlewkowicz it's not always reliable. It appears that it depends on how the Dev creates their "releases".

From what I can gather it should always be reliable for projects which provide specific binary file formats (e.g. .deb as per the OP). But may not be reliable for repos which just provide a source code tarball/zipball (and even if it is now, may not be in the future).

FWIW it seems that the difference is whether or not the dev explicitly creates a "Release" via GH (which is required to include specific binary formats). Everytime a repo is tagged, a new source code bundle will automagically show up on the GH "Releases" page. However, these automagic "releases" will not appear via the API, nor via the https://github.com/:owner/:repo/releases/latest URL. They will only be visible via the projects Release webpage and/or via the API under repos/:owner/:repo/tags. And unfortunately, it can't be assumed that the tags are in chronological order... 😢

@dsifford

This comment has been minimized.

Copy link

@dsifford dsifford commented Jun 28, 2018

Shorter PCRE grep: (just get's the version number)

curl -s https://api.github.com/repos/USER/REPO/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")'
@blockloop

This comment has been minimized.

Copy link

@blockloop blockloop commented Oct 5, 2018

I use jq

curl -sL https://api.github.com/repos/USER/REPO/releases/latest | jq -r '.assets[].browser_download_url'
@PizzaLovingNerd

This comment has been minimized.

Copy link

@PizzaLovingNerd PizzaLovingNerd commented Oct 11, 2018

Thanks

@Arezhik

This comment has been minimized.

Copy link

@Arezhik Arezhik commented Oct 12, 2018

curl -s https://api.github.com/repos/USER/REPO/releases/latest | jq -r ".assets[] | select(.name | contains(\"search param for specific download url\")) | .browser_download_url" | wget -i -

Then pass into wget also to download it.

@TaChao

This comment has been minimized.

Copy link

@TaChao TaChao commented Oct 19, 2018

curl --silent "https://api.github.com/repos/USER/REPO/releases/latest" | # Get latest release from GitHub api
    grep '"tag_name":' |                                                 # Get tag line
    sed -E 's/.*"([^"]+)".*/\1/' |
    xargs -I {} curl -sOL "https://github.com/USER/REPO/archive/"{}'.tar.gz'
@zwbetz-gh

This comment has been minimized.

Copy link

@zwbetz-gh zwbetz-gh commented Oct 21, 2018

Thanks for this @steinwaywhw. I used it to write a script to install the latest release of the Hugo binary.
See https://zwbetz.com/script-to-install-latest-hugo-release-on-linux-and-mac/ for the write-up.

#!/bin/bash

pushd /tmp/

curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest \
| grep "browser_download_url.*hugo_[^extended].*_Linux-64bit\.tar\.gz" \
| cut -d ":" -f 2,3 \
| tr -d \" \
| wget -qi -

tarball="$(find . -name "*Linux-64bit.tar.gz")"
tar -xzf $tarball

chmod +x hugo

mv hugo /usr/local/bin/

popd

location="$(which hugo)"
echo "Hugo binary location: $location"

version="$(hugo version)"
echo "Hugo binary version: $version"
@Inkimar

This comment has been minimized.

Copy link

@Inkimar Inkimar commented Oct 23, 2018

Glad to see the activity here.
I have a question, which more has to do with releases and 'the latest release'
Checking the mozilla/geckodriver-repo (thanks @aaronnguyen ) , https://github.com/mozilla/geckodriver/releases , which makes the following request possible : https://api.github.com/repos/mozilla/geckodriver/releases/latest
and comparing it to one of my own projects , https://github.com/Inkimar/cp_dina-collections/releases , where I cannot request the latest using https://api.github.com/repos/inkimar/cp_dina-collections/releases/latest
I can see that the the mozilla/geckodriver-repo has the tag 'latest release' on the left hand side in https://github.com/mozilla/geckodriver/releases where my repos does not have it https://github.com/Inkimar/cp_dina-collections/releases .
I am pushing to my repo using the following 'git push && git push --tags && rm -rf build/temp ' and I get asked a question on the release ( patch, minor or major) ....
My Q: how can I get the tag 'latest release' so that I can use the same request as to the mozilla/geckodriver ?

-i

@dkebler

This comment has been minimized.

Copy link

@dkebler dkebler commented Nov 8, 2018

@zwbetz-gh thx. Set it up so one can run it without preface of sudo and included a check of current version vs installed first and then proceed on new release. One could do this as a daily cron job and one would always be running the latest release (if that was the goal).

Another improvement would be the check the system architecture and install the correct one (not just the amd 64)

Looks like u already post this to hugo discourse https://discourse.gohugo.io/t/script-to-install-latest-hugo-release-on-macos-and-ubuntu/14774/8 :-)

#!/bin/bash

CUR_VERSION="$(hugo version | cut -d'v' -f2 | cut -c 3-5)"
NEW_VERSION="$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep tag_name | cut -d'.' -f2 | cut -d'"' -f1)"
echo "Current Version: $CUR_VERSION => New Version: $NEW_VERSION"

if [ "$NEW_VERSION" -ne "$CUR_VERSION" ]; then

  echo "Installing version $NEW_VERSION"

  pushd /tmp/

  curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest \
  | grep "browser_download_url.*hugo_[^extended].*_Linux-64bit\.tar\.gz" \
  | cut -d ":" -f 2,3 \
  | tr -d \" \
  | wget -qi -

  tarball="$(find . -name "*Linux-64bit.tar.gz" 2>/dev/null)"
  tar -xzf $tarball

  chmod +x hugo

  sudo mv hugo /usr/local/bin/

  popd

  location="$(which hugo)"
  echo "Hugo binary location: $location"

  version="$(hugo version)"
  echo "New Hugo binary version installed!: $version"

else
  echo Latest version already installed
fi

@zwbetz-gh

This comment has been minimized.

Copy link

@zwbetz-gh zwbetz-gh commented Nov 8, 2018

@dkebler 👍

@drmikecrowe

This comment has been minimized.

Copy link

@drmikecrowe drmikecrowe commented Nov 13, 2018

Thanks to @blockloop for the jq hint. Here's what I came up with (checks for binary with linux-amd64 in the name):

function get_download_url {
	wget -q -nv -O- https://api.github.com/repos/$1/$2/releases/latest 2>/dev/null |  jq -r '.assets[] | select(.browser_download_url | contains("linux-amd64")) | .browser_download_url'
}

Usage: get_download_url 99designs aws-vault

Followed with:

function install_binary {
	URL=$(get_download_url $1 $2)
	mkdir -p ~/bin
	BASE=$(basename $URL)
	wget -q -nv -O $BASE $URL 
	if [ ! -f $BASE ]; then
		echo "Didn't download $URL properly.  Where is $BASE?"
		exit 1	
	fi
	mv $BASE ~/bin
	chmod +x ~/bin/$BASE
}

Usage: install_binary 99designs aws-vault

@evankanderson

This comment has been minimized.

Copy link

@evankanderson evankanderson commented Jan 7, 2019

Note that "latest release" will only work if the release is not tagged as a "draft" or "prerelease". For repos which are still under development, you might want to fetch /releases and then use the most recent one:

curl -s https://api.github.com/repos/$1/$2/releases | \
  jq ".[0].assets | map(select(.name == \"$3\")) | .[0].browser_download_url"
@oddlots

This comment has been minimized.

Copy link

@oddlots oddlots commented Jan 9, 2019

Thanks @TaChao

It's a little beyond the scope of this but I have also added a | tar -xz --strip-components=1 -C target/dir in order to facilitate a one liner update of a library.

@agustif

This comment has been minimized.

Copy link

@agustif agustif commented Feb 9, 2019

curl -s https://api.github.com/repos/user/repo/releases/latest |
grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi -

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Feb 21, 2019

curl -s https://api.github.com/repos/user/repo/releases | jq ".[0].assets | .[].browser_download_url" | grep $(lsb_release -cs) | grep $([[ $(arch) == x86_64 ]] && echo amd64 || echo i386) | sed 's/"//g'

note: trying to find better solution for arch.

@josh

This comment has been minimized.

Copy link

@josh josh commented Feb 22, 2019

Heh, this was bugging me how tricky it was to do as well. I just added a new redirect so https://github.com/user/repo/releases/latest/download/foo.zip redirects to the latest tagged asset. Hope it's handy!

@derekbekoe

This comment has been minimized.

Copy link

@derekbekoe derekbekoe commented Feb 24, 2019

Thanks @josh!

@aaronliu0130

This comment has been minimized.

Copy link

@aaronliu0130 aaronliu0130 commented Mar 10, 2019

how do I find a specific link when there are multiple releases?

@bgokden

This comment has been minimized.

Copy link

@bgokden bgokden commented Mar 13, 2019

Heh, this was bugging me how tricky it was to do as well. I just added a new redirect so https://github.com/user/repo/releases/latest/download/foo.zip redirects to the latest tagged asset. Hope it's handy!

This works for us. I wrote a small script to install binary locally:

base=https://github.com/magneticio/forklift/releases/latest/download &&
  curl -L $base/forklift-$(uname -s)-$(uname -m) >/usr/local/bin/forklift &&
  chmod +x /usr/local/bin/forklift
@ReSearchITEng

This comment has been minimized.

Copy link

@ReSearchITEng ReSearchITEng commented Mar 15, 2019

For anyone interested how to do that with ansible, see below.

  vars:
    k8s_ver: "v1.13.1"
  tasks:
    - name: limit k8s_ver to vMajor.Minor
      set_fact:
        k8s_ver_major_minor: "{{k8s_ver | regex_replace('^v([0-9])\\.([0-9]*).*', 'v\\1.\\2') }}"

    - name: get all releases as json file
      get_url:
        url: "https://api.github.com/repos/ReSearchITEng/kubeadm-playbook/releases"
        dest: /tmp/all.releases.json
        force: yes

    - name: parse releases json
      command: cat /tmp/all.releases.json
      register: allreleases

    - name: download the targz
      get_url:
        url: '{{allreleases.stdout_lines[0] | from_json | json_query(query) | join ("") }}'
        dest: /tmp/sourcesofthedesiredrelease.tar.gz
        force: yes
      vars:
        query: "[?name=='{{k8s_ver_major_minor}}'].tarball_url"

For getting the tar.gz of the latest release, the above would become more simple.

@mweibel

This comment has been minimized.

Copy link

@mweibel mweibel commented Mar 19, 2019

I just released https://gitreleases.dev/ which solves this issue with a simple URL.

@rpdelaney

This comment has been minimized.

Copy link

@rpdelaney rpdelaney commented Mar 25, 2019

@mweibel Very cool, but what if the release asset has an unpredictable name? For instance, have a look at https://github.com/funtoo/keychain/releases/latest

@coocheenin

This comment has been minimized.

Copy link

@coocheenin coocheenin commented Mar 27, 2019

Everytime a repo is tagged, a new source code bundle will automagically show up on the GH "Releases" page. However, these automagic "releases" will not appear via the API

@JedMeister I can confirm this. Sad, but true.

@mweibel

This comment has been minimized.

Copy link

@mweibel mweibel commented Mar 28, 2019

@rpdelaney (sorry, somehow I didn't get a notification for this comment?)

@mweibel Very cool, but what if the release asset has an unpredictable name? For instance, have a look at https://github.com/funtoo/keychain/releases/latest

There are two possible things here:
a) Release doesn't contain a release asset but only the automatically generated one by GitHub (tarball, zipball)
b) Release contains a release asset which contains the version in the filename again (e.g. in the linked example a possibility would be keychain_2.8.5.zip)

Both are currently not well supported by gitreleases, but I plan to support them: mweibel/gitreleases#2 and mweibel/gitreleases#3.
Feel free to comment on those issues or open new issues 👍

@jhuckaby

This comment has been minimized.

Copy link

@jhuckaby jhuckaby commented Apr 17, 2019

For those that may have missed it above, @josh from GitHub released an official fix for this:

https://gist.github.com/steinwaywhw/a4cd19cda655b8249d908261a62687f8#gistcomment-2845270

Heh, this was bugging me how tricky it was to do as well. I just added a new redirect so https://github.com/USER/REPO/releases/latest/download/FILENAME.zip redirects to the latest tagged asset. Hope it's handy!

This works perfectly for me. Just replace USER, REPO and FILENAME with your own stuff.

@loganmarchione

This comment has been minimized.

Copy link

@loganmarchione loganmarchione commented Apr 17, 2019

@josh thank you! This works perfectly!

@hartmannr76

This comment has been minimized.

Copy link

@hartmannr76 hartmannr76 commented Apr 18, 2019

@josh Doesn't seem to work if the repo is private. I've tried providing Basic and Token as an auth header and keep getting a 404. Would love to use this though!!

@kurtroberts

This comment has been minimized.

Copy link

@kurtroberts kurtroberts commented Apr 30, 2019

Here's a version that uses python to parse the JSON, in case you don't have jq available (like working with tools installed by default on macOS):

curl -s https://api.github.com/repos/sheagcraig/yo/releases/latest |  python -c 'import json,sys;obj=json.load(sys.stdin);print obj["assets"][0]["browser_download_url"];'
@joaovitor

This comment has been minimized.

Copy link

@joaovitor joaovitor commented May 14, 2019

curl -sL https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/latest | \
grep "releases/download.*linux_amd64" | \
cut -d \" -f 2 | \
tr -d \" | \
sed -e 's#.*download/v\(.*\)/aws-iam-authenticator.*#\1#g'
@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! :)

@Contextualist

This comment has been minimized.

Copy link

@Contextualist Contextualist commented May 23, 2019

For anyone who don't bother installing extra dependencies or crafting multi-line commands, try my handy service get latest release:

curl -fLO https://glare.now.sh/<user>/<repo>/<asset_regex>

e.g.

curl -fLO https://glare.now.sh/jgm/pandoc/deb

Why?

  • Succinct and maintainable
  • Match asset names containing version numbers with regex
@vojtech2

This comment has been minimized.

Copy link

@vojtech2 vojtech2 commented May 27, 2019

Possibly also an option:

git ls-remote --tags https://github.com/jgm/pandoc.git | sed -nE 's#.*refs/tags/(v?[0-9]+(\.[0-9]+)*)$#\1#p' | sort -Vr | head -n 1

See https://stackoverflow.com/questions/10649814/get-last-git-tag-from-a-remote-repo-without-cloning

@Puvipavan

This comment has been minimized.

Copy link

@Puvipavan Puvipavan commented Jul 10, 2019

curl -s https://api.github.com/repos/cloud-custodian/cloud-custodian/releases/latest | sed -n 's/.*tag_name":\s"\(.*\)".*/\1/p' | head -1

@steinwaywhw

This comment has been minimized.

Copy link
Owner Author

@steinwaywhw steinwaywhw commented Jul 10, 2019

Wow, thank you so much for all the comments/tips/improvements. I didn't even notice until today. You're awesome ❤️

@evandrix

This comment has been minimized.

Copy link

@evandrix evandrix commented Jul 13, 2019

wget -c $(curl -ksL "https://api.github.com/repos/x64dbg/x64dbg/releases/latest" | jq -r ".assets[0].browser_download_url")

@fakuivan

This comment has been minimized.

Copy link

@fakuivan fakuivan commented Aug 2, 2019

Expanding on @evandrix's answer

curl -s https://api.github.com/repos/pyrovski/wrtbwmon/releases | \
  jq -r '[[.[] |
    select(.draft != true) |
    select(.prerelease != true)][0] |
    .assets |
    .[] |
    select(.name | endswith(".ipk")) |
    .browser_download_url][0]'
@xeruf

This comment has been minimized.

Copy link

@xeruf xeruf commented Aug 4, 2019

Much simpler: curl -s https://api.github.com/repos/user/repo/releases/latest | grep -o "http.*deb"

Now in my .zshrc (works just as well with bash):

# Gets the download url for the latest release of a package provided via GitHub Releases
# Usage: ghrelease USER REPO [PATTERN]
ghrelease() {
	curl -s "https://api.github.com/repos/$1/$2/releases/latest" | grep -o "http.*${3:-deb}"
}

Perfect pair with:

# Installs a local or remote(http/https) deb package and removes it after installation
installdeb() {
	set -e
	loc="/tmp/install.deb"
	case $1 in 
	http*) sudo wget -O "$loc" $1;;
	*) loc="$1"
	esac
	sudo dpkg -i "$loc"
	sudo apt -f install
	sudo rm -f "$loc"
}

Example use: installdeb $(ghrelease sharkdp bat "bat_.*_amd64.deb")
Perfect 👌

@koddsson

This comment has been minimized.

Copy link

@koddsson koddsson commented Aug 28, 2019

Hey hey!

This feature has been added to github, see https://help.github.com/en/articles/linking-to-releases#linking-to-the-latest-release

If you'd like to link directly to a download of your latest release asset you can link to /owner/name/releases/latest/download/asset-name.zip.

Example: curl -L https://github.com/primer/octicons/releases/latest/download/svg.zip

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

@jaekyeom

This comment has been minimized.

Copy link

@jaekyeom jaekyeom commented Oct 12, 2019

Hey hey!

This feature has been added to github, see https://help.github.com/en/articles/linking-to-releases#linking-to-the-latest-release

If you'd like to link directly to a download of your latest release asset you can link to /owner/name/releases/latest/download/asset-name.zip.

Example: curl -L https://github.com/primer/octicons/releases/latest/download/svg.zip

That's great, but can we get the link to the tarball/zipball of the latest release, too?

@yngvark

This comment has been minimized.

Copy link

@yngvark yngvark commented Nov 8, 2019

VERSION=$(curl -s https://github.com/Versent/saml2aws/releases/latest/download 2>&1 | grep -Po [0-9]+\.[0-9]+\.[0-9]+)
echo version: $VERSION
wget https://github.com/Versent/saml2aws/releases/latest/download/saml2aws_$VERSION\_linux_amd64.tar.gz
@contributorpw

This comment has been minimized.

Copy link

@contributorpw contributorpw commented Nov 22, 2019

bazelbuild/bazel .sh for linux

curl -s https://api.github.com/repos/bazelbuild/bazel/releases/latest \
| grep "browser_download_url.*linux.*sh\"" \                               
| cut -d : -f 2,3 \                 
| tr -d \" \       
| wget -qi - 
@ulwlu

This comment has been minimized.

Copy link

@ulwlu ulwlu commented Mar 2, 2020

For those who installed fzf, I use this alias every time. It's quick and clear.

function dlgr() {
	URL=`curl -s "${@}" | grep "browser_download_url" | cut -d '"' -f 4 | fzf`
	curl -O ${URL}
}

It's same as this one liner.

curl -s https://api.github.com/repos/bazelbuild/bazel/releases/latest \
	| grep "browser_download_url" \       
	| cut -d '"' -f 4 \                 
	| fzf \       
	| curl -O
@gmolveau

This comment has been minimized.

Copy link

@gmolveau gmolveau commented Mar 4, 2020

To download the latest .tar.gz release using only curl :

curl -s https://api.github.com/repos/<user>/<repo>/releases/latest \
| grep 'browser_download_url.*tar.gz"' \
| cut -d : -f 2,3 \
| tr -d \" \
| xargs -n 1 curl -O -sSL

and if you want to download + extract in the current folder :

curl -s https://api.github.com/repos/<user>/<repo>/releases/latest \
| grep 'browser_download_url.*tar.gz"' \
| cut -d : -f 2,3 \
| tr -d \" \
| xargs -n 1 curl -sSL \
| tar -xz --strip-components=1
@miguelslemos

This comment has been minimized.

Copy link

@miguelslemos miguelslemos commented Mar 9, 2020

curl -s "https://api.github.com/repos/<user>/<repo>/releases/latest?access_token=$GITHUB_TOKEN" \
    | jq '.assets[] | select(.name == "mob-macos.tar.gz") | .url' \
    | xargs -I {} curl -sSL -H 'Accept: application/octet-stream' "{}?access_token=$GITHUB_TOKEN" \
    | tar -xzf -
@x5engine

This comment has been minimized.

Copy link

@x5engine x5engine commented Apr 5, 2020

just send the release to ipfs and download it instead of adding the accesstoken..

@skillfr

This comment has been minimized.

Copy link

@skillfr skillfr commented Apr 19, 2020

In case there are multiple files

v=$(wget -q https://api.github.com/repos/<user>/<repo>/releases/latest -O - | grep -E tag_name | awk -F '[""]' '{print $4}') wget https://github.com/<user>/<repo>/releases/download/$v/file.linuxAMDx64.tar.gz

or

wget https://github.com/<user>/<repo>/releases/download/$v/file.linuxARMx32.tar.gz

@aslafy-z

This comment has been minimized.

Copy link

@aslafy-z aslafy-z commented Apr 23, 2020

My 2 cents

wget -q -O - --header "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/<user>/<repo>/releases/latest" \
    | python -c 'import sys, json; print(json.load(sys.stdin)["tarball_url"])' \
    | xargs -I {} wget -q -O - --header "Authorization: token $GITHUB_TOKEN"  "{}" \
    | tar -xz --strip-components=1

Comes with a bonus:

sudo tar --strip=1 --transform "s/original_folder/new_name_folder/" --wildcards -C /usr/local/share/ca-certificates -xzf - "*/original_folder"
# outputs to /usr/local/share/ca-certificates/new_name_folder
@chris-gillatt

This comment has been minimized.

Copy link

@chris-gillatt chris-gillatt commented Apr 30, 2020

For anyone else who lands on this thread wanting to solve this problem for either this app or others, I've resorted to using a one/two-liner consisting of a simple grep and a not-so-simple (but robust) perl expression to extract semvers. If you really wanted to expand this, you could use positional parameters for the asset type.

#!/bin/bash -e

LATEST=$(curl -sL --fail https://api.github.com/repos/jgm/pandoc/releases/latest | grep "tag_name" | perl -pe 'if(($_)=/([0-9]+([.][0-9]+)+)/){$_.="\n"}') ; curl -vsL "https://github.com/jgm/pandoc/releases/download/$LATEST/pandoc-$LATEST-macOS.zip" -O
@Evidlo

This comment has been minimized.

Copy link

@Evidlo Evidlo commented May 1, 2020

This isn't necessary anymore as Github has a direct link to the latest release:

https://github.com/[user]/[repo]/releases/latest/download/[asset-name.zip]

https://help.github.com/en/github/administering-a-repository/linking-to-releases

@chris-gillatt

This comment has been minimized.

Copy link

@chris-gillatt chris-gillatt commented May 4, 2020

@Evidlo - Did you actually try following that? It only works if you do it manually. This entire thread is about automating the problem.

@Evidlo

This comment has been minimized.

Copy link

@Evidlo Evidlo commented May 6, 2020

@chris-gill

Yes, it works for me. E.g. https://github.com/evidlo/remarkable_news/releases/latest/download/release.zip

I'm not sure what you mean by 'do it manually'.

Of course it doesn't work if the asset name changes between releases, so I guess it's not useful for Pandoc.

@chris-gillatt

This comment has been minimized.

Copy link

@chris-gillatt chris-gillatt commented May 7, 2020

@Evidlo Ahh - that's the thing then - with many project releases including some of my own, the assets are named with the version in the filename string, meaning that the name changes with each release like can be seen here:
https://github.com/jgm/pandoc/releases/

The automation problem arises there, meaning we need 1/2 lines of bash to get $latest of those files.
What I mean by manually, is that you can still do this by following the steps on the page you provided, but only manually through the GUI and clicking the link to the file to download it.

@codedge

This comment has been minimized.

Copy link

@codedge codedge commented May 26, 2020

This one works for me:

curl -LJO https://github.com/[user]/[repo]/tarball/[tag]
@MaluNoPeleke

This comment has been minimized.

Copy link

@MaluNoPeleke MaluNoPeleke commented Jun 5, 2020

How to handle cases where the latest release is for an older version (e.g. v2.x maintenance update) but you always want to get latest release of the current version (highest version number).
One example is this repository (2.38.2 has been released lately but 3.18.1 is the latest): https://github.com/TryGhost/Ghost/releases

@Contextualist

This comment has been minimized.

Copy link

@Contextualist Contextualist commented Jun 5, 2020

@MaluNoPeleke Shamelessly promoting my own project https://github.com/Contextualist/glare . You can use semver matching for releases.

# To get the latest 3.x:
curl -L https://glare.now.sh/TryGhost/Ghost@3.x/Ghost -o ghost.zip
# To get the latest 2.x:
curl -L https://glare.now.sh/TryGhost/Ghost@2.x/Ghost -o ghost.zip
# Highest version, whatever:
curl -L https://glare.now.sh/TryGhost/Ghost@x/Ghost -o ghost.zip
@eggbean

This comment has been minimized.

Copy link

@eggbean eggbean commented Jun 5, 2020

For those who installed fzf, I use this alias every time. It's quick and clear.

function dlgr() {
	URL=`curl -s "${@}" | grep "browser_download_url" | cut -d '"' -f 4 | fzf`
	curl -O ${URL}
}

I like that, but it doesn't download the correct file when I try. It seems you need to add the -L switch, due to redirects. This is what I am using, only requiring USER/REPO.

dlgr ()
{
    URL=$(curl -s https://api.github.com/repos/"${@}"/releases/latest   | jq -r '.assets[].browser_download_url' | fzf);
    curl -LO ${URL}
}
@MaluNoPeleke

This comment has been minimized.

Copy link

@MaluNoPeleke MaluNoPeleke commented Jun 5, 2020

@Contextualist I would rather use direct features of shell and GitHub instead of relying on another service and if possible have also a generic solution instead of a hard-coded version number but thanks for your quick support.

@iofirag

This comment has been minimized.

Copy link

@iofirag iofirag commented Aug 31, 2020

  curl -s https://api.github.com/repos/<user-name>/<repo-name>/releases/latest \
    | grep browser_download_url \
    | cut -d '"' -f 4 \
    | wget -qi-
  tarfilename="$(find . -name "*.tar.gz")"
  tar -xzf $tarfilename
  sudo rm $tarfilename
@jwillikers

This comment has been minimized.

Copy link

@jwillikers jwillikers commented Sep 17, 2020

Long live awk! 😉

$ curl -s https://api.github.com/repos/<user>/<repo>/releases/latest \
    | awk -F': '  '/browser_download_url/ && /\.<extension>/ {gsub(/"/, "", $(NF)); system("curl -LO " $(NF))}'

Just substitute in the user or organization name, the project's name, and the desired file extension for the <user>, <repo>, and <extension> fields respectively.
It's also easy enough to modify the match pattern for the file extension to make it more specific if need be.

@unfor19

This comment has been minimized.

Copy link

@unfor19 unfor19 commented Sep 19, 2020

No dependencies are needed, plain simple Bash and curl

  1. Get the latest version - Assuming the versioning format is - "v0.0.1"
    If the version format is different from "v0.0.1" then change the cut -d'v' -f2 section
ORG_NAME=hashicorp
REPO_NAME=terraform
LATEST_VERSION=$(curl -s https://api.github.com/repos/${ORG_NAME}/${REPO_NAME}/releases/latest | grep "tag_name" | cut -d'v' -f2 | cut -d'"' -f1)

# Output: 0.13.3
  1. Download the latest version
curl -L -o ${REPO_NAME}.tar.gz https://github.com/${ORG_NAME}/${REPO_NAME}/archive/v${LATEST_VERSION}.tar.gz

It's not a oneliner, but it's how I use it in a Dockerfile, I hope it helps

@maggie0002

This comment has been minimized.

Copy link

@maggie0002 maggie0002 commented Sep 27, 2020

So many ways to skin a cat.

Retrieving source files:

A shell script, incorporating authentication in order to access private repositories and use tag-name based retrieval to fetch a .tar from the latest release for when browser_download_url isn't available (browser_download_url won't be in the api output if only the standard tar and zip archives are available in your release).

Ensure jq is installed on the system running the script:

#!/usr/bin/env bash
USER_NAME=your-user-name
REPO_NAME=your-repo-name
TOKEN=your-token # To generate a token see: https://docs.github.com/en/enterprise/2.15/user/articles/creating-a-personal-access-token-for-the-command-line

curl -H "Authorization: token ${TOKEN}" https://api.github.com/repos/${USER_NAME}/${REPO_NAME}/releases/latest | jq -r .tag_name |
xargs -I {} curl -H "Authorization: token ${TOKEN}" -sSL https://github.com/${USER_NAME}/${REPO_NAME}/archive/{}.tar.gz |
tar -xzf - --strip-components=1

Retrieve binaries:

A shell script to fetch the latest release when binary files are available, in a private repository.

Ensure jq is installed.

Script may need modification if there is more than one binary file as at the moment it will only fetch the first.

#!/usr/bin/env bash

USER_NAME=your-user-name
REPO_NAME=your-repo-name
TOKEN=your-token # To generate a token see: https://docs.github.com/en/enterprise/2.15/user/articles/creating-a-personal-access-token-for-the-command-line

ASSET_ID=$(curl -H "Authorization: token ${TOKEN}" https://api.github.com/repos/${USER_NAME}/${REPO_NAME}/releases/latest | jq .assets[0].id); \
curl -H "Authorization: token ${TOKEN}" https://api.github.com/repos/${USER_NAME}/${REPO_NAME}/releases/assets/${ASSET_ID} -LJOH 'Accept: application/octet-stream'
@sebma

This comment has been minimized.

Copy link

@sebma sebma commented Oct 2, 2020

@ryuta69 Hi, is fzf the command-line fuzzy finder ?

@ulwlu

This comment has been minimized.

Copy link

@ulwlu ulwlu commented Oct 2, 2020

@sebma
yes

@hongkongkiwi

This comment has been minimized.

Copy link

@hongkongkiwi hongkongkiwi commented Oct 19, 2020

I needed something one liner with no external tools but also cross platform this works:
VERSION=$(curl -s "https://github.com/cloudposse/tfmask/releases/latest/download" 2>&1 | sed "s/^.*download\/\([^\"]*\).*/\1/")

For those using this in a docker to download files, here's a nice snippet which can handle either setting the version manually via build argument or if blank will auto find the latest version:

ARG TFMASK_VERSION=
ARG PLATFORM_ARCH=amd64

RUN if [ -z $TFMASK_VERSION ]; then echo "Finding latest TFMask Version..."; TFMASK_VERSION=$(curl -s "https://github.com/cloudposse/tfmask/releases/latest/download" 2>&1 | sed "s/^.*download\/\([^\"]*\).*/\1/"); else echo "TFMask version passed in build argument v${TFMASK_VERSION}"; fi && \
    echo "Downloading TFMask v${TFMASK_VERSION}..." && \
    curl -sLo "/usr/bin/tfmask" "https://github.com/cloudposse/tfmask/releases/download/${TFMASK_VERSION}/tfmask_linux_${PLATFORM_ARCH}"
@mando7

This comment has been minimized.

Copy link

@mando7 mando7 commented Nov 10, 2020

My short one line for latest DBeaver-ce.
Shorten url https://api.github.com/repos/dbeaver/dbeaver/releases/latest whith https://git.io
curl -Ls follow a location silently
grep -wo print the exactly
wget -qi download silently

which gives:

curl -Ls https://git.io/Jkk0N | grep -wo "https.*amd64.deb" | wget -qi -

@pascalandy

This comment has been minimized.

Copy link

@pascalandy pascalandy commented Dec 14, 2020

script

_org_name="firepress-org"
_project_name="ghostfire"
_latest_version_is=$(curl -s https://api.github.com/repos/${_org_name}/${_project_name}/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")')
echo ${_latest_version_is}

output

3.40.1
@ulwlu

This comment has been minimized.

Copy link

@ulwlu ulwlu commented Dec 14, 2020

I changed script a bit from a year ago.

dlgr() {
    read repo"?type https://api.github.com/repos/{author}/{repo}/releases/latest : ";
    URL=`curl -s "${repo}" | grep "browser_download_url" | cut -d '"' -f 4 | fzf`
    curl -sOL ${URL}
}

ix2m5us3t6p72fvorbiiv90q4soh

If you cursor the target and enter, it downloads.

@zero88

This comment has been minimized.

Copy link

@zero88 zero88 commented Jan 4, 2021

If anyone interest, give a try: https://github.com/zero88/gh-release-downloader

@contributorpw

This comment has been minimized.

Copy link

@contributorpw contributorpw commented Jan 4, 2021

If anyone interest, give a try: https://github.com/zero88/gh-release-downloader

It seems you missed an one line ghrd:latest download cmd 😼

@crazy-matt

This comment has been minimized.

Copy link

@crazy-matt crazy-matt commented Feb 18, 2021

This looks really cool, but as noted by @ozbillwang and @rlewkowicz it's not always reliable. It appears that it depends on how the Dev creates their "releases".

From what I can gather it should always be reliable for projects which provide specific binary file formats (e.g. .deb as per the OP). But may not be reliable for repos which just provide a source code tarball/zipball (and even if it is now, may not be in the future).

FWIW it seems that the difference is whether or not the dev explicitly creates a "Release" via GH (which is required to include specific binary formats). Everytime a repo is tagged, a new source code bundle will automagically show up on the GH "Releases" page. However, these automagic "releases" will not appear via the API, nor via the https://github.com/:owner/:repo/releases/latest URL. They will only be visible via the projects Release webpage and/or via the API under repos/:owner/:repo/tags. And unfortunately, it can't be assumed that the tags are in chronological order... 😢

Solving this problem:

_latest_version="$(curl --silent "https://api.github.com/repos/${USER}/${REPO}/releases/latest" | grep "tag_name" | cut -d '"' -f4)"
if [[ -z "${_version}" ]]; then
  local _latest_version="$(curl --silent "https://api.github.com/repos/${USER}/${REPO}/tags" | grep "name" | grep -v "rc" | cut -d '"' -f4 | sort -rV | head -n 1)"
                                                    # not reliable but trying to exclude the release candidate tags --^^
fi
@crazy-matt

This comment has been minimized.

Copy link

@crazy-matt crazy-matt commented Feb 18, 2021

I changed script a bit from a year ago.

dlgr() {
    read repo"?type https://api.github.com/repos/{author}/{repo}/releases/latest : ";
    URL=`curl -s "${repo}" | grep "browser_download_url" | cut -d '"' -f 4 | fzf`
    curl -sOL ${URL}
}

ix2m5us3t6p72fvorbiiv90q4soh

If you cursor the target and enter, it downloads.

Sweeeet, thanks for that one

@philthynz

This comment has been minimized.

Copy link

@philthynz philthynz commented Mar 3, 2021

Here's what I did to get the latest azure pipeline agent:

curl -s https://api.github.com/repos/microsoft/azure-pipelines-agent/releases/latest | grep -hnr "https://vstsagentpackage.azureedge.net/agent/2.182.1/pipelines-agent-linux-x64-" | cut -d '"' -f 4 | xargs wget -qO pipeline-agent.tar.gz
@graphik55

This comment has been minimized.

Copy link

@graphik55 graphik55 commented Mar 17, 2021

If someone needs a PowerShell only version (example for Microsofts vsts agent):

$githubLatestReleases = "https://api.github.com/repos/microsoft/azure-pipelines-agent/releases/latest"   
$githubLatestReleasesJson = ((Invoke-WebRequest $gitHubLatestReleases) | ConvertFrom-Json).assets.browser_download_url  
$Uri = (((Invoke-WebRequest $githubLatestReleasesJson | ConvertFrom-Json).downloadUrl) | Select-String "vsts-agent-win-x64").ToString()  
@MostHated

This comment has been minimized.

Copy link

@MostHated MostHated commented Apr 9, 2021

If someone needs a PowerShell only version (example for Microsofts vsts agent):

Exactly what I was hoping to find. 👍

I made a slight adjustment to it for my needs.

$githubLatestReleases = 'https://api.github.com/repos/microsoft/winget-cli/releases/latest'   
$githubLatestRelease = (((Invoke-WebRequest $gitHubLatestReleases) | ConvertFrom-Json).assets.browser_download_url | select-string -Pattern 'appxbundle').Line
Invoke-WebRequest $githubLatestRelease -OutFile 'Microsoft.DesktopAppInstaller.appxbundle'
@alerque

This comment has been minimized.

Copy link

@alerque alerque commented Apr 14, 2021

The cut/tr shenanigans in the original post dodging the colon in the URL drives me crazy. This snippet has propogated everywhere, very few people that copy / paste it know how it works, and it leaves so much room for improvement. @dsifford was on the same track but stopped short of passing the result to the final download. (Edit At first pass I missed @mando7's similar solution, the extra grep -w flag is a nice touch but not required.)

Lets start with swapping grep | cut | tr for grep -o. If you're already searching for a string, why not just print the bit that matches your search instead of searching for it with context then stripping away the context? No good reason. Since the ".deb" from the original post happens to be ambiguous for this project now I'm including a match.

Also, why use two tools curl then wget when curl is arguably more capable than the latter for both jobs. wget - can be curl -fsSLJO.

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest |
	grep -o "https://.*\.amd64\.deb" |
	xargs curl -fsLJO

If the grep doesn't suit you and you have jq handy you can swap in the equivalent:

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest |
	jq -r '.assets[].browser_download_url | select(test("arm64.deb"))' |
	xargs curl -fsLJO
@flightlesstux

This comment has been minimized.

Copy link

@flightlesstux flightlesstux commented May 6, 2021

I don't understand why there are so many comments here. It's really easy with bash. Here is the example, I did it for you.

if [ "${OSTYPE}" = "x86_64" ]; then
    BIN="amd64"
else
    BIN="arm64"
fi

LATEST=$(curl -s https://api.github.com/repos/prometheus/node_exporter/releases/latest | grep "linux-${BIN}.tar.gz" | cut -d '"' -f 4 | tail -1)

cd /tmp/
curl -s -LJO $LATEST
@Sy3Omda

This comment has been minimized.

Copy link

@Sy3Omda Sy3Omda commented May 7, 2021

easy and fast
curl -s https://api.github.com/repos/user/reponame/releases/latest | grep -E 'browser_download_url' | grep linux_amd64 | cut -d '"' -f 4 | wget -qi -

@Frikster

This comment has been minimized.

Copy link

@Frikster Frikster commented May 18, 2021

To download the latest tarball for a repo I was able to just do this:

curl https://api.github.com/repos/user/reponame/releases/latest | grep "browser_download_url" | grep -Eo 'https://[^\"]*' | xargs wget

I think if you are on Windows you have to change it to:

curl https://api.github.com/repos/user/reponame/releases/latest | grep "browser_download_url" | grep -Eo 'https://[^/"]*' | xargs wget

If you want to download the latest tar and immediately extract what was downloaded:

curl https://api.github.com/repos/user/reponame/releases/latest | grep "browser_download_url" | grep -Eo 'https://[^\"]*' | xargs wget -O - | tar -xz

@vithalreddy

This comment has been minimized.

Copy link

@vithalreddy vithalreddy commented May 23, 2021

i have developed a small bash function which will take git repo as fn argument and has an option to define install methods. and cleans the data after instalation is complete.

ghInstall author/repo
function ghInstall(){
    local loc="/tmp/gh-downloads/$1"
    local repo="https://api.github.com/repos/$1/releases/latest"
    local URL=`curl -s "${repo}" | grep "browser_download_url" | cut -d '"' -f 4 | fzf`
    echo "Repo: $repo Temp Dir: $loc"

    mkdir -p $loc
    curl -sOL --output-dir $loc  ${URL}
    local tarfilename="$(find $loc  -name "*.tar.gz")"
    tar xvzf $tarfilename -C $loc
    ls $loc -al

    local PS3='Please choose the Installing Method: '
    local gh_options=("Move to Bin Dir" "Make install" "Quit")
    select opt in "${gh_options[@]}"
    do
        case $opt in
            "Move to Bin Dir")
                rm -rf $tarfilename
                sudo mv $loc/* /usr/local/bin/
                break
                ;;
            "Make install")
                make install
                break
                ;;
            "Quit")
                break
                ;;
            *) echo "invalid option $REPLY";;
        esac
    done

    rm -rf $loc
}
@SuperJC710e

This comment has been minimized.

Copy link

@SuperJC710e SuperJC710e commented May 27, 2021

If someone needs a PowerShell only version (example for Microsofts vsts agent):

$githubLatestReleases = "https://api.github.com/repos/microsoft/azure-pipelines-agent/releases/latest"   
$githubLatestReleasesJson = ((Invoke-WebRequest $gitHubLatestReleases) | ConvertFrom-Json).assets.browser_download_url  
$Uri = (((Invoke-WebRequest $githubLatestReleasesJson | ConvertFrom-Json).downloadUrl) | Select-String "vsts-agent-win-x64").ToString()  

Perfect! Thank you!

@majick777

This comment has been minimized.

Copy link

@majick777 majick777 commented May 31, 2021

"To link directly to a download of your latest release asset, link to /owner/name/releases/latest/download/asset-name.zip."
Source: https://docs.github.com/en/github/administering-a-repository/releasing-projects-on-github/linking-to-releases

This means you can just download the asset URL directly without the API?
So I'm not sure why this is still a thing? (Tho my guess is maybe this redirect was added in 2019 but this thread started in 2017.)

@alerque

This comment has been minimized.

Copy link

@alerque alerque commented May 31, 2021

@majick777 That only works for projects that post assets with simple filenames that don't have any changing version data in the asset name itself. Some projects do that, but not all can be downloaded that way and while it makes this easy doing that makes other things hard (like keeping multiple versions of something in a directory). Hence these solutions are still useful in 2021. If the project you are downloading from doesn't have version info in the asset name by all means use those "latest" links.


As for the proliferation of comments here:

  1. The majority of people posting clearly aren't proficient shell coders. There are a number of gems above, but many of the solutions are overly complex, spawn more processes than necessary, make shell quoting blunders, etc. Some claim to be "bash" but are actually "sh" and vise versa. Many of the examples here using 2 greps and a cut could be simplified to a single grep if you pay attention to the URL scheme to match. Many examples above make the mistake of using xargs, then only handling one possible output. These will fail badly if more than one match is found.
  2. Half the comments seem to be unaware that the exact syntax will need to be adjusted based on the upstream project's asset naming schemes. There is no 1-size-fits-all command for this because almost all of these rely on some form of pattern matching or assumptions about the naming scheme. We don't need 20 more "this is the one that works" posts! Sure you had to adjust your syntax for the project you were downloading from, but that doesn't mean it will work best for everyone.
  3. Different tools are used and some situations might call for that. I posted examples with jq and with grep above to illustrate how different tooling could be used to advantage. Likewise swapping wget -qi and curl -fsLJO can be a matter of system tooling choice.
  4. Some of these are better for scripting, some are better for interactive use.

Before posting more, please seriously consider whether your solution offers something more in the way of a better implementation or more explanation than existing options. If you just copied and tweaked an existing one to match some other project URL scheme, please refrain since that won't add anything.

@yaneony

This comment has been minimized.

Copy link

@yaneony yaneony commented Jun 17, 2021

Shameless self-promotion, but i did that for community as well. https://ghd.one
Enter repository URL, filter down the list to only one single file, get your permanent link for download.

Example for Gitea repository: https://ghd.one/go-gitea/gitea
Filtered to Linux binary 64bit without extension: https://ghd.one/go-gitea/gitea?includes=linux+amd64&excludes=amd64.
Filtered to Windows 64bit executable file: https://ghd.one/go-gitea/gitea?includes=windows+amd64&excludes=gogit+.exe.

More about it on reddit: https://www.reddit.com/r/programming/comments/o1yit0/ghd_get_github_direct_links_without_pain_wip/

@alerque

This comment has been minimized.

Copy link

@alerque alerque commented Jun 17, 2021

@yaneony That’s kinda spiffy to help people that can't write match expressions for grep come up with something useful, but it would be cooler if the UI gave out a shell command with the appropriate way to get the original URL rather than bouncing everybody’s downloads through a 3rd party service!

@yaneony

This comment has been minimized.

Copy link

@yaneony yaneony commented Jun 17, 2021

@alerque you've right, the only problem is: different platforms - different methods.
Some people using bash, some wget, other nodejs or even php... I went thru all of them. It did a lot of pain changing regular expressions every time developer change naming pattern. That's how i came to idea of making that website.

@psychowood

This comment has been minimized.

Copy link

@psychowood psychowood commented Jul 14, 2021

Just in case this could be useful for anyone, I'm using this oneliner from an alpine docker image, to pull the latest tarball from a github release and extract it in the current folder, skipping the original root folder:

curl -s https://api.github.com/repos/ghostfolio/ghostfolio/releases/latest | sed -n 's/.*"tarball_url": "\(.*\)",.*/\1/p' | xargs -n1 wget -O - -q | tar -xz --strip-components=1

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