Rectangle 27 1

How to download GitHub Release from private repo using command line?


(export AUTH_TOKEN=<oauth-token>; \
 export ASSET_ID=$(wget -O - https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag>?access_token=$AUTH_TOKEN | python -c 'import sys, json; print json.load(sys.stdin)["assets"][0]["id"]'); \
 wget --header='Accept:application/octet-stream' -O <download-name> https://api.github.com/repos/<owner>/<repo>/releases/assets/$ASSET_ID?access_token=$AUTH_TOKEN)
<download-name>
<oauth-token>
<owner>
<repo>
<tag>
export AUTH_TOKEN=<oauth-token>
id
python -c 'import sys, json; print json.load(sys.stdin)["assets"][0]["id"]'
wget
wget --header='Accept:application/octet-stream' -O <tarball-name>.tar.gz https://api.github.com/repos/<owner>/<repo>/releases/assets/$ASSET_ID?access_token=$AUTH_TOKEN)
wget -O - https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag>?access_token=$AUTH_TOKEN
  • The outer parentheses create a subshell and ensure exported environment variables are discarded afterwards.

Here is a "one-liner" using wget for making HTTP requests and python for JSON parsing:

Note
Rectangle 27 1

How to download GitHub Release from private repo using command line?


(export AUTH_TOKEN=<oauth-token>; \
 export ASSET_ID=$(wget -O - https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag>?access_token=$AUTH_TOKEN | python -c 'import sys, json; print json.load(sys.stdin)["assets"][0]["id"]'); \
 wget --header='Accept:application/octet-stream' -O <download-name> https://api.github.com/repos/<owner>/<repo>/releases/assets/$ASSET_ID?access_token=$AUTH_TOKEN)
<download-name>
<oauth-token>
<owner>
<repo>
<tag>
export AUTH_TOKEN=<oauth-token>
id
python -c 'import sys, json; print json.load(sys.stdin)["assets"][0]["id"]'
wget
wget --header='Accept:application/octet-stream' -O <tarball-name>.tar.gz https://api.github.com/repos/<owner>/<repo>/releases/assets/$ASSET_ID?access_token=$AUTH_TOKEN)
wget -O - https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag>?access_token=$AUTH_TOKEN
  • The outer parentheses create a subshell and ensure exported environment variables are discarded afterwards.

Here is a "one-liner" using wget for making HTTP requests and python for JSON parsing:

Note
Rectangle 27 1

How to download GitHub Release from private repo using command line?


-L
<?xml version="1.0" encoding="UTF-8"?>
<Error>
 <Code>InvalidArgument</Code>
 <Message>
   Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified
 </Message>
 <ArgumentName>Authorization</ArgumentName>
 <ArgumentValue>token <yourtoken> </ArgumentValue><RequestId>4BEDDBA630688865</RequestId> <HostId>SXLsRKgKM6tPa/K7g7tSOWmQEqowG/4kf6cwOmnpObXrSzUt4bzOFuihmzxK6+gx</HostId>
</Error>
curl -i -H "Authorization: token <token>" -H "Accept:application/octet-stream" https://<token>:@api.github.com/repos/:owner/:repo/releases/assets/:id
wget --auth-no-challenge --header='Accept:application/octet-stream' https://<token>:@api.github.com/repos/:owner/:repo/releases/assets/:id -O app.zip

Can you explain how you would generate the <token> in your solutions? Sorry if it's a dumb question.

curl is forwarding the authentication header in the request to the AmazonS3 bucket, where the Github release assets are stored. Error response from S3:

if you use curl, the workaround is to put the authentication elsewhere than as a header. Like as query string parameter method (per the API docs, "URL?access_token=TOKEN") . Can you also do "TOKEN:@api.github.com/... with curl?

Note
Rectangle 27 1

How to download GitHub Release from private repo using command line?


-L
<?xml version="1.0" encoding="UTF-8"?>
<Error>
 <Code>InvalidArgument</Code>
 <Message>
   Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified
 </Message>
 <ArgumentName>Authorization</ArgumentName>
 <ArgumentValue>token <yourtoken> </ArgumentValue><RequestId>4BEDDBA630688865</RequestId> <HostId>SXLsRKgKM6tPa/K7g7tSOWmQEqowG/4kf6cwOmnpObXrSzUt4bzOFuihmzxK6+gx</HostId>
</Error>
curl -i -H "Authorization: token <token>" -H "Accept:application/octet-stream" https://<token>:@api.github.com/repos/:owner/:repo/releases/assets/:id
wget --auth-no-challenge --header='Accept:application/octet-stream' https://<token>:@api.github.com/repos/:owner/:repo/releases/assets/:id -O app.zip

Can you explain how you would generate the <token> in your solutions? Sorry if it's a dumb question.

curl is forwarding the authentication header in the request to the AmazonS3 bucket, where the Github release assets are stored. Error response from S3:

if you use curl, the workaround is to put the authentication elsewhere than as a header. Like as query string parameter method (per the API docs, "URL?access_token=TOKEN") . Can you also do "TOKEN:@api.github.com/... with curl?

Note
Rectangle 27 1

How to download GitHub Release from private repo using command line?


#!/usr/bin/env bash
# Script to download asset file from tag release using GitHub API v3.
# See: http://stackoverflow.com/a/35688093/55075    
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

# Check dependencies.
set -e
type curl grep sed tr >&2
xargs=$(which gxargs || which xargs)

# Validate settings.
[ -f ~/.secrets ] && source ~/.secrets
[ "$GITHUB_API_TOKEN" ] || { echo "Error: Please define GITHUB_API_TOKEN variable." >&2; exit 1; }
[ $# -ne 4 ] && { echo "Usage: $0 [owner] [repo] [tag] [name]"; exit 1; }
[ "$TRACE" ] && set -x
read owner repo tag name <<<$@

# Define variables.
GH_API="https://api.github.com"
GH_REPO="$GH_API/repos/$owner/$repo"
GH_TAGS="$GH_REPO/releases/tags/$tag"
AUTH="Authorization: token $GITHUB_API_TOKEN"
WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
CURL_ARGS="-LJO#"

# Validate token.
curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!";  exit 1; }

# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)
# Get ID of the asset based on given name.
eval $(echo "$response" | grep -C3 "name.:.\+$name" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
#id=$(echo "$response" | jq --arg name "$name" '.assets[] | select(.name == $name).id') # If jq is installed, this can be used instead. 
[ "$id" ] || { echo "Error: Failed to get asset id, response: $response" | awk 'length($0)<100' >&2; exit 1; }
GH_ASSET="$GH_REPO/releases/assets/$id"

# Download asset file.
echo "Downloading asset..." >&2
curl $CURL_ARGS -H 'Accept: application/octet-stream' "$GH_ASSET?access_token=$GITHUB_API_TOKEN"
echo "$0 done." >&2
./get_gh_asset.sh :owner :repo :tag :name
/settings/tokens
:owner
:repo
:token
ASSET_ID=$(echo "$response" | jq --arg name "$name" '.assets[] | select(.name == $name).id')
Authorization: token <token>
GITHUB_API_TOKEN=XXX
X-Amz-Algorithm
X-Amz-Signature
curl -vLJ -H 'Authorization: token <token>' -H 'Accept: application/octet-stream' https://api.github.com/repos/:owner/:repo/releases/assets/<id>
curl -vLJO -H 'Accept: application/octet-stream' 'https://api.github.com/repos/:owner/:repo/releases/assets/:id?access_token=:token'
https://api.github.com/repos/:owner/:repo/releases/tags/:tag
jq

:id is your asset id, can be found in tag release URL, like:

Before running, you need to set your GITHUB_API_TOKEN with your GitHub token (see: /settings/tokens at GH). This can be placed in your ~/.secrets file, like:

Here is the Bash script which can download asset file given specific name of file:

In case you wonder why curl fails sometimes with (as mentioned in other answer):

Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified.

Thanks for the script @kenorb, I will use it to allow my ansible playbook to grab release .deb files from a private repo. Much appreciated.

Then download the asset with curl command (change with appropriate values):

To download release file from private repo, you can use Personal access token which can be generated at /settings/tokens with Full control of private repositories scope.

and since GitHub redirects you from asset page (when requesting application/octet-stream), it populates credentials automatically in query string and since curl is passing over the same credentials in the request header (which you've specified), therefore they're conflicting. So as for workaround you can use access_token instead.

where name is your filename (or partial of it). Prefix script with TRACE=1 to debug it.

Note
Rectangle 27 1

How to download GitHub Release from private repo using command line?


#!/usr/bin/env bash
# Script to download asset file from tag release using GitHub API v3.
# See: http://stackoverflow.com/a/35688093/55075    
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

# Check dependencies.
set -e
type curl grep sed tr >&2
xargs=$(which gxargs || which xargs)

# Validate settings.
[ -f ~/.secrets ] && source ~/.secrets
[ "$GITHUB_API_TOKEN" ] || { echo "Error: Please define GITHUB_API_TOKEN variable." >&2; exit 1; }
[ $# -ne 4 ] && { echo "Usage: $0 [owner] [repo] [tag] [name]"; exit 1; }
[ "$TRACE" ] && set -x
read owner repo tag name <<<$@

# Define variables.
GH_API="https://api.github.com"
GH_REPO="$GH_API/repos/$owner/$repo"
GH_TAGS="$GH_REPO/releases/tags/$tag"
AUTH="Authorization: token $GITHUB_API_TOKEN"
WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
CURL_ARGS="-LJO#"

# Validate token.
curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!";  exit 1; }

# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)
# Get ID of the asset based on given name.
eval $(echo "$response" | grep -C3 "name.:.\+$name" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
#id=$(echo "$response" | jq --arg name "$name" '.assets[] | select(.name == $name).id') # If jq is installed, this can be used instead. 
[ "$id" ] || { echo "Error: Failed to get asset id, response: $response" | awk 'length($0)<100' >&2; exit 1; }
GH_ASSET="$GH_REPO/releases/assets/$id"

# Download asset file.
echo "Downloading asset..." >&2
curl $CURL_ARGS -H 'Accept: application/octet-stream' "$GH_ASSET?access_token=$GITHUB_API_TOKEN"
echo "$0 done." >&2
./get_gh_asset.sh :owner :repo :tag :name
/settings/tokens
:owner
:repo
:token
ASSET_ID=$(echo "$response" | jq --arg name "$name" '.assets[] | select(.name == $name).id')
Authorization: token <token>
GITHUB_API_TOKEN=XXX
X-Amz-Algorithm
X-Amz-Signature
curl -vLJ -H 'Authorization: token <token>' -H 'Accept: application/octet-stream' https://api.github.com/repos/:owner/:repo/releases/assets/<id>
curl -vLJO -H 'Accept: application/octet-stream' 'https://api.github.com/repos/:owner/:repo/releases/assets/:id?access_token=:token'
https://api.github.com/repos/:owner/:repo/releases/tags/:tag
jq

:id is your asset id, can be found in tag release URL, like:

Before running, you need to set your GITHUB_API_TOKEN with your GitHub token (see: /settings/tokens at GH). This can be placed in your ~/.secrets file, like:

Here is the Bash script which can download asset file given specific name of file:

In case you wonder why curl fails sometimes with (as mentioned in other answer):

Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified.

Thanks for the script @kenorb, I will use it to allow my ansible playbook to grab release .deb files from a private repo. Much appreciated.

Then download the asset with curl command (change with appropriate values):

To download release file from private repo, you can use Personal access token which can be generated at /settings/tokens with Full control of private repositories scope.

and since GitHub redirects you from asset page (when requesting application/octet-stream), it populates credentials automatically in query string and since curl is passing over the same credentials in the request header (which you've specified), therefore they're conflicting. So as for workaround you can use access_token instead.

where name is your filename (or partial of it). Prefix script with TRACE=1 to debug it.

Note