Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save steinwaywhw/a4cd19cda655b8249d908261a62687f8 to your computer and use it in GitHub Desktop.
Save steinwaywhw/a4cd19cda655b8249d908261a62687f8 to your computer and use it in GitHub Desktop.
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 -
@antofthy
Copy link

antofthy commented Jun 2, 2023

The problem is that what to search for VARIES from repo to repo.
MOST repos seem to use browser_download_url but I have also have seen repos that does not have that entry, but use tarball_url, zipball_url instead.

@si618
Copy link

si618 commented Sep 13, 2023

@fanuch

My hat in the ring.

VER=$(curl --silent -qI https://github.com/bakito/adguardhome-sync/releases/latest | awk -F '/' '/^location/ {print  substr($NF, 1, length($NF)-1)}'); \
wget https://github.com/bakito/adguardhome-sync/releases/download/$VER/adguardhome-sync_${VER#v}_linux_x86_64.tar.gz 

Thanks for the inspo in this thread - really should be easier than this ...

Agreed, and thanks for putting your hat in the ring; it was very close to what I needed 🙇‍♂️

@codelinx
Copy link

codelinx commented Sep 21, 2023

Using jq, i think this should work with most repos and allow you to saerch different values or fields as needed.

wget $(curl -s https://api.github.com/repos/bitwarden/clients/releases/latest  | \
 jq -r '.assets[] | select(.name | contains ("deb")) | .browser_download_url')
  • jq -r raw search
  • select( .name select the field to refine your download
  • | contains ("deb")) search criteria to get the download url
  • . browser_download_url' return string

NOTE: '--raw-output/-r' With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes

@notorand-it
Copy link

But why using wget and curl? Why not just wget or curl?

@codelinx
Copy link

But why using wget and curl? Why not just wget or curl?

This is a programmatic discussion for downloading the file(s). You cant wget/curl get the file because of links and internet things. Other issues are that the file name may change, the version, or the file you may need for your OS may change etc.

@codelinx
Copy link

But why using wget and curl? Why not just wget or curl?

you are just trolling.

@notorand-it
Copy link

notorand-it commented Sep 22, 2023

wget $(wget -q -O - https://api.github.com/repos/bitwarden/clients/releases/latest | jq -r '.assets[] | select(.name | contains ("deb")) | .browser_download_url')

I would say this is NOT trolling.
Just like this, IMHO.

@jrichardsz
Copy link

It worked at first attempt :)
Thank you so much !!

@NiceGuyIT
Copy link

dra is making huge strides in simplifying the download process.

dra helps you download release assets more easily:

  • no authentication for public repository (you cannot use gh without authentication)
  • Built-in generation of pattern to select an asset to download (with gh you need to provide glob pattern that you need to create manually).

@panscher
Copy link

@flightlesstux

The download with NotpadPlus x64.exe does not work.

`
@echo off

setlocal enabledelayedexpansion
set repo=notepad-plus-plus/notepad-plus-plus
for /f "tokens=1,* delims=:" %%A in ('curl -ks https://api.github.com/repos/%repo%/releases/latest ^| find "browser_download_url"') do (
call :download "%%B"
)
goto :eof

:download
set "url=%~1"
for %%i in (%url%) do set "filename=%%nxi"
if "%filename:
-9%"==".x64.exe" (
curl -kOL %url%
)
goto :eof
`

@notorand-it
Copy link

I am not unsubscribing because I actually want to see how far we get, and how long this conversation lasts with all these comments that everyone keeps adding. There are hundreds of ways and different languages to achieve the same result. But do we need the rosetta stone with all the programming languages on earth, and all the different approaches, or can we move on, after having received so many comments? OMG.

" different languages"?

The title reads "One Liner to Download the Latest Release from Github Repo".
I would say that the original thing aimed at using a single line command or pipeline.
So I would say it is aimed at a shell (bash/zsh/PowerShell) not any language.
Still on a single line.

IMHO, a few comments actually hit spot.
All the rest is either multi-line scripts (no. of lines > 1) or just not working.

@antofthy
Copy link

antofthy commented Dec 7, 2023

The title reads "One Liner to Download the Latest Release from Github Repo". I would say that the original thing aimed at using a single line command or pipeline. So I would say it is aimed at a shell (bash/zsh/PowerShell) not any language. Still on a single line.
IMHO, a few comments actually hit spot. All the rest is either multi-line scripts (no. of lines > 1) or just not working.

For a specific repository a single line to do the task can be created. BUT for a general repository, well, what is needed is next to impossible to do in one line, or even in a small script. There are always weird exceptions. I have seen many of them!

The general request that was made, was so general, it is also impossible to achieve. No one answer, or one simple line is itself a complete answer. So you get a multitude of answers, each of which will work for specific cases.

"Simplicity has a habit of expanding into catastrophe."
-- Anne McCaffrey, "The Ship who Sang"

In seeking the unattainable, simplicity only gets in the way.
-- Alan J. Perlis

@notorand-it
Copy link

notorand-it commented Dec 12, 2023

Fair.
A general 1-liner, even to a specific git repo, is quite difficult to achieve.
But a 1-liner that can be easily adapted to a number of cases is a different thing and is something much easier to do.
The type of things you can look for here and in any other question-and-answers web sites.

The original 1st post here, was already a Unix shell-based solution, not a question, using 5 different commands, none aimed at JSON parsing and with inexplicably replicated functions between wget and curl. If the output from GitHub were moved to "compact JSON" (no newlines at all) by GitHub itself, then most of those JSON-unaware scripts would stop working.
The original solution, as most of the subsequent replies, has likely been created by merging 2 different sources.

I have shared my (rather limited) knowledge about scripting to get a simple, minimal and reliable 1-liner that can be easily adapted to a large number of cases.
It only uses 2 tools (wget and jq) aimed exactly at their goals (downloading via HTTP and reliably parsing JSON strings).
This is according to the Unix command line art of simplicity, IMHO.

"Life is really simple, but we insist on making it complicated."
-- Confucius

"It is not a daily increase, but a daily decrease. Hack away at the inessentials."
-- Bruce Lee

"If you can't explain it to a six year old, you don't understand it yourself."
-- Albert Einstein

@TheRealMrWicked
Copy link

Here is a solution for Windows, you need to put the repo owner and name, as well as a string to identify the download, the generic version of the command is below.

for /f "tokens=1,* delims=:" %a in ('curl -s https://api.github.com/repos/<Put repo owner and repo name here>/releases/latest ^| findstr "browser_download_url" ^| findstr "<Put identifying string here>"') do (curl -kOL %b)

Example
Putting the repo as notepad-plus-plus/notepad-plus-plus and the identifying string as .x64.exe we get this command:

for /f "tokens=1,* delims=:" %a in ('curl -s https://api.github.com/repos/notepad-plus-plus/notepad-plus-plus/releases/latest ^| findstr "browser_download_url" ^| findstr ".x64.exe"') do (curl -kOL %b)

Which downloads the latest x64 installer of Notepad++ to the current directory.

@Chuckame
Copy link

Chuckame commented Jan 22, 2024

Here is the simplest way of getting the latest version with only curl and basename: Using the Forwarded url by github when accessing /latest:

basename $(curl -Ls -o /dev/null -w %{url_effective} https://github.com/<user>/<repo>/releases/latest)

Here another variant of it with only curl and a pure bash feature:

version=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/<user>/<repo>/releases/latest)
version=${version##*/}

@ivomarino
Copy link

Here is the simplest way of getting the latest version with only curl and basename: Using the Forwarded url by github when accessing /latest:

basename $(curl -Ls -o /dev/null -w %{url_effective} https://github.com/<user>/<repo>/releases/latest)

Here another variant of it with only curl and a pure bash feature:

version=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/<user>/<repo>/releases/latest)
version=${version##*/}

works great

@jessp01
Copy link

jessp01 commented Mar 12, 2024

Using jq to match a release file pattern (modsecurity-v.*.tar.gz$ in this example):

curl -sL https://api.github.com/repos/owasp-modsecurity/ModSecurity/releases/latest| \
jq -r '.assets[] | select(.name? | match("modsecurity-v.*.tar.gz$")) | .browser_download_url'

@healBvdb
Copy link

Another simple command using the fantastic Nushell
http get https://api.github.com/repos/<user>/<repo>/releases/latest | get tag_name

@JonnieCache
Copy link

JonnieCache commented May 22, 2024

here's one for getting the tag name of the latest release:

curl -s https://api.github.com/repos/<REPO>/releases/latest | jq -r '.tag_name'

this is useful for cloning the latest release, eg. with asdf:

local asdf_version=$(curl -s https://api.github.com/repos/asdf-vm/asdf/releases/latest | jq -r '.tag_name')
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch $asdf_version

@spvkgn
Copy link

spvkgn commented Jun 1, 2024

wget one-liner to get release tarball and extract its contents:

wget -qO- 'https://api.github.com/repos/<REPO>/releases/latest' | jq -r '.assets[] | select(.name | match("tar.(gz|xz)")) | .browser_download_url' | xargs wget -qO- | bsdtar -xf -

@Samueru-sama
Copy link

Better alternative that will work even if the json is pretty or not:

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | sed 's/[()",{}]/ /g; s/ /\n/g' | grep "https.*releases/download.*deb"

Using jq -c to turn the json compact and this is what happens:

Old:

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest \
| grep "browser_download_url.*deb" \
| cut -d : -f 2,3 \
| tr -d \"
 https://github.com/jgm/pandoc/releases/download/3.2/pandoc-3.2-1-amd64.deb
 https://github.com/jgm/pandoc/releases/download/3.2/pandoc-3.2-1-arm64.deb


curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | jq -c \
| grep "browser_download_url.*deb" \
| cut -d : -f 2,3 \
| tr -d \"
https://api.github.com/repos/jgm/pandoc/releases/155373146,assets_url

Alternative:

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | sed 's/[()",{}]/ /g; s/ /\n/g' | grep "https.*releases/download.*deb"         
https://github.com/jgm/pandoc/releases/download/3.2/pandoc-3.2-1-amd64.deb
https://github.com/jgm/pandoc/releases/download/3.2/pandoc-3.2-1-arm64.deb

curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | jq -c | sed 's/[()",{}]/ /g; s/ /\n/g' | grep "https.*releases/download.*deb"
https://github.com/jgm/pandoc/releases/download/3.2/pandoc-3.2-1-amd64.deb
https://github.com/jgm/pandoc/releases/download/3.2/pandoc-3.2-1-arm64.deb

Fedora 40 recently changed wget for wget2, and this causes github the send the json compact breaking scripts that were parsing it with grep.

I use gron when it is available, otherwise the sed tricks should work most of the time.

@motdotla
Copy link

This is it

wget https://github.com/aquasecurity/tfsec/releases/latest/download/tfsec-linux-amd64

this is the best solution. thank you @joshjohanning. everything else is unnecessarily complicated for users and could trip them up because of different shell versions and lack of installed libs like jq.

add in a bit of uname magic and all your users are good to go.

curl -L -o dotenvx.tar.gz "https://github.com/dotenvx/dotenvx/releases/latest/download/dotenvx-$(uname -s)-$(uname -m).tar.gz"
tar -xzf dotenvx.tar.gz
./dotenvx help

@bruteforks
Copy link

Since this gist is still very active, here's one I've made recently:

#!/usr/bin/env bash

# Fetch the latest release version
latest_version=$(curl -s https://api.github.com/repos/microsoft/vscode-js-debug/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")')

# Remove the 'v' prefix from the version number
version=${latest_version#v}

# Construct the download URL
download_url="https://github.com/microsoft/vscode-js-debug/releases/download/${latest_version}/js-debug-dap-${latest_version}.tar.gz"

# Download the tar.gz file
curl -L -o "js-debug-dap-${version}.tar.gz" "$download_url"

@initiateit
Copy link

initiateit commented Jul 17, 2024

Just curl and grep:

curl -s https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep '"browser_download_url":' | grep 'amd64.deb' | grep -vE '(\.pem|\.sig)' | grep -o 'https://[^"]*'

https://github.com/caddyserver/xcaddy/releases/download/v0.4.2/xcaddy_0.4.2_linux_amd64.deb

curl -s https://api.github.com/repos/aptly-dev/aptly/releases/latest | grep '"browser_download_url":' | grep 'amd64.deb' | grep -o 'https://[^"]*'

https://github.com/aptly-dev/aptly/releases/download/v1.5.0/aptly_1.5.0_amd64.deb

My apologies if it borrows from other answers.

@adriangalilea
Copy link

dra is making huge strides in simplifying the download process.

@NiceGuyIT thanks for bringing it up.

@timur-g
Copy link

timur-g commented Aug 5, 2024

Nice examples, thanks to all. Stil, not always is "browser_download_url" available.
Also, we should assume that *.deb is not enough, rather use amd64.deb or x86_64.deb.
It would be nice if we had permanent file name, to directly use wget of download link, but we also have file names with version.
lastversion works, just it has not in-built install for deb as for rpm.

Here is the example I assume should work for install of https://github.com/rustdesk/rustdesk/releases/latest (which does not work with most of solutions):
curl -sL https://github.com/rustdesk/rustdesk/releases/latest | grep "https.*releases/download.*amd64\|x86_64\.deb" | grep -oP 'href="\Khttps:.+?"' | sed 's/"//' | wget -i-

Or:
curl -sL https://github.com/rustdesk/rustdesk/releases/latest | grep -wo "https.*x86_64.deb\|https.*amd64.deb"

@dkebler
Copy link

dkebler commented Aug 15, 2024

I know this gist is for latest regular release but what if you need the latest pre-release like at tasmota/install where certain assets are ONLY built in pre-releases and not in the regular ones. I was unable to get lastversion to do this even with pre-release option because of how the CI generated the pre-releases.

the operative line being
url=$(curl --silent https://api.github.com/repos/tasmota/install/releases | grep "browser_download_url\": \"" | grep $bin | head -1 | sed 's/^.*: //' | xargs)

So for example.

unset dev
if [[ $1 == "--dev" ]]; then
dev=true 
shift 1
fi

chip=${1@L}
[[ $chip ]] && chip=${chip@L}
type=$2
bin=tasmota32$chip$([[ $type ]] && echo "-$type").factory.bin    

if ls $bin >/dev/null 2>&1 ; then
   echo $bin already exists locally so using it.
   echo delete $bin first and run again to pull the latest   
else
   if [[ $dev ]]; then
       echo downloading $bin from latest dev release
       url=$(curl --silent https://api.github.com/repos/tasmota/install/releases | grep "browser_download_url\": \"" | grep $bin | head -1 | sed 's/^.*: //' | xargs) 
       echo downloading $url
       if ! wget -O $bin "$url"; then
        echo can not find any assest for $bin at Tasmota Github Install Repo
        exit  
       fi
   else
      echo pulling latest regular release version of $bin
      url=http://ota.tasmota.com/tasmota32/release/$bin
      if ! wget -O $bin $url; then
      echo could not download $bin
      echo might be a development only binary try using --dev flag
      exit
      fi
   fi
fi

@smallest-quark
Copy link

smallest-quark commented Sep 2, 2024

Another one liner only using only wget and downloading the first (newest file from the releases):

wget -qO- --max-redirect=10 https://github.com/rustdesk/rustdesk/releases | grep -wo "https.*x86_64.deb\|https.*amd64.deb" | head -n 1 | xargs -I {} wget -O test.deb {}

@kwalker99
Copy link

not my original idea, but another way of doing this... never seen jq before, i love json so this will become a new fav...

curl -s https://api.github.com/repos/bitwarden/clients/releases/latest | jq -r '.assets[] | select(.name | contains ("zip")) | .browser_download_url' | head -n 1 | xargs -I {} wget {}

@maxadamo
Copy link

maxadamo commented Sep 16, 2024

does anyone realize that piping multiple commands (from 4 up to 10) is not elegant?
And the more you insist, the more it looks bad.

1 pipe and 2 commands (curl, awk) is enough, is more effective, and it does not require installing additional tools:

URL="https://api.github.com/repos/jgraph/drawio-desktop/releases/latest"

curl -s $URL | awk -F\" '/browser_download_url.*.deb/{system("curl -OL " $(NF-1))}'

In this case it fetches two artifacts: one for ARM one for AMD64, but if you want to fetch only one artifact, you don't need another 3 pipes, and you do as follows:

URL="https://api.github.com/repos/jgraph/drawio-desktop/releases/latest"

curl -s $URL | awk -F\" '/browser_download_url.*.deb/&&/amd64/{system("curl -OL " $(NF-1))}'

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