Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Script to upload a release asset using the GitHub API v3.
#!/usr/bin/env bash
#
# Author: Stefan Buck
# License: MIT
# https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447
#
#
# This script accepts the following parameters:
#
# * owner
# * repo
# * tag
# * filename
# * github_api_token
#
# Script to upload a release asset using the GitHub API v3.
#
# Example:
#
# upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./build.zip
#
# Check dependencies.
set -e
xargs=$(which gxargs || which xargs)
# Validate settings.
[ "$TRACE" ] && set -x
CONFIG=$@
for line in $CONFIG; do
eval "$line"
done
# 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#"
if [[ "$tag" == 'LATEST' ]]; then
GH_TAGS="$GH_REPO/releases/latest"
fi
# 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 filename.
eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
[ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; }
# Upload asset
echo "Uploading asset... "
# Construct url
GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)"
curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET
@WesleyBatista

This comment has been minimized.

Copy link

@WesleyBatista WesleyBatista commented Feb 8, 2018

Didn't tried your snippet, but $localAssetPath is not defined. It should work? maybe you meant $filename

@babelouest

This comment has been minimized.

Copy link

@babelouest babelouest commented Feb 13, 2018

That's a very cool script, can you add a license to it so I could reuse it on other projects?

Thanks in advance

@stefanbuck

This comment has been minimized.

Copy link
Owner Author

@stefanbuck stefanbuck commented Feb 14, 2018

@babelouest License added, thanks for the hint.

@WesleyBatista Good spot. I removed $localAssetPath, it's not really needed

@huxingyi

This comment has been minimized.

Copy link

@huxingyi huxingyi commented Apr 15, 2018

Thanks @stefanbuck, very convenient script.

And for those who want overwrite the asset with the same filename:

… …

# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)

# !!! Change start from here until the end !!!

# Get ID of the release.
eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
[ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; }
release_id="$id"

# Get ID of the asset based on given filename.
id=""
eval $(echo "$response" | grep -C1 "name.:.\+$filename" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
assert_id="$id"
if [ "$assert_id" = "" ]; then
    echo "No need to overwrite asset"
else
    echo "Deleting asset($assert_id)... "
    curl "$GITHUB_OAUTH_BASIC" -X "DELETE" -H "Authorization: token $github_api_token" "https://api.github.com/repos/$owner/$repo/releases/assets/$assert_id"
fi

# Upload asset
echo "Uploading asset... "

# Construct url
GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$release_id/assets?name=$(basename $filename)"

curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET

@zhanghongtong

This comment has been minimized.

Copy link

@zhanghongtong zhanghongtong commented Nov 2, 2018

I tried uploading the file using upload_url but it didn't work.

$ echo "uploading asset to release to url : $upload_url"
uploading asset to release to url : https://uploads.github.com/repos/zhanghongtong/test-trello/releases/13626806/assets?client_id=559b37ca1dfdad45d224&client_secret=a39a8fd8b5a2ce54eaa07axxxxxxxxxxxxxx

$ curl -s  \
        -H "Content-Type: application/zip" \
        --data-binary @test.zip  \
        "$upload_url&name=test.zip&label=some-binary.zip"
{"message":"Not Found","request_id":"F743:6DD1:903D2:CCF7E:5BDBAE02","documentation_url":"https://developer.github.com/v3"}%

Any idea what is going sour here?

@pjheslin

This comment has been minimized.

Copy link

@pjheslin pjheslin commented Apr 13, 2019

Brilliant! This is just what I needed. Thanks. I do get a message saying "curl: (3) malformed" whenever I run it, but that doesn't seem to interfere with its functioning.

@anandchakru

This comment has been minimized.

Copy link

@anandchakru anandchakru commented Aug 6, 2019

What is basename ?

Edit: Nevermind, basename reduced the full path of the file to just filename.

Update: Note to self, if switching to gh-actions to publish to gh-pages, refer this simple yml and don't forget to generate a PAT (Personal Access Tokens) here and add it into your repo -> settings -> secrets under key: GITHUB_PAT

@awais786327

This comment has been minimized.

Copy link

@awais786327 awais786327 commented Nov 24, 2019

@stefanbuck what in that case if we need to upload more then 1 zip file ?

@stefanbuck

This comment has been minimized.

Copy link
Owner Author

@stefanbuck stefanbuck commented Nov 25, 2019

@awais786327 I recommend using GitHub Actions for this task https://github.com/actions/upload-release-asset nowadays.

@typebrook

This comment has been minimized.

Copy link

@typebrook typebrook commented Dec 27, 2019

Thanks! This script is really useful when working under cli !
Also, Github Actions and hub-release works fine in other cases.

@stefanbuck I followed the hint from @huxingyi and added some improvement with my fork
Now it supports parameter overwrite

@allysono

This comment has been minimized.

Copy link

@allysono allysono commented Feb 7, 2020

Hello!

When i run this command:

./upload-github-release-asset.sh github_api_token=xxx owner=night repo=moon tag=4.4 filename=./artifact.zip

I receive this message:

Error: Failed to get release id for tag: 4.4
{
  "message": "Bad credentials",
  "documentation_url": "https://developer.github.com/v3"
}

I have a release 4.4 and use github enterprise.

Can you help me?

@typebrook

This comment has been minimized.

Copy link

@typebrook typebrook commented Feb 8, 2020

@allysono

I have a release 4.4 and use github enterprise.

Enterprise has it's own server, looks like your token is not valid for github.com.
So maybe you need to replace your API URL
from https://api.github.com
to http://api.github.internal.example.com/

@allysono

This comment has been minimized.

Copy link

@allysono allysono commented Feb 10, 2020

@typebrook, thanks! 👍

@allysono

I have a release 4.4 and use github enterprise.

Enterprise has it's own server, looks like your token is not valid for github.com.
So maybe you need to replace your API URL
from https://api.github.com
to http://api.github.internal.example.com/

I change my url to https://api.github.company.com and now I receive this error message:

Error: Invalid repo, token or network issue!

I think this repo and token it's okay:
I'm running this command:

./upload-github-release-asset.sh github_api_token=xxx owner=night repo=moon tag=4.4 filename=./artifact.zip
It's correct, right?

@trevorsandy

This comment has been minimized.

Copy link

@trevorsandy trevorsandy commented Feb 11, 2020

This block is failing to retrieve my asset id - returns "".

# Get ID of the asset based on given filename.
id=""
eval $(echo "$response" | grep -C1 "name.:.\+$filename" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
assert_id="$id"

jq did the trick however - with a lot less code and complexity:

# Get ID of the release.
release_id="$(echo $response | jq -r .id)"

# Get ID of the asset based on given filename.
asset_id="$(echo $response | jq -r '.assets[] | select(.name == '\"$ASSET\"').id')"

Cheers,

@typebrook

This comment has been minimized.

Copy link

@typebrook typebrook commented Feb 12, 2020

@allysono

Error: Invalid repo, token or network issue!

  • First thing first, check you repo is on github.com or your enterprise server github.<company>.com
  • Second, check your token has scope with repo in web page https://github.com/settings/tokens, or maybe https://github<company>.com/settings/tokens
@janiez

This comment has been minimized.

Copy link

@janiez janiez commented Feb 24, 2020

Good job!!

@zwhitchcox

This comment has been minimized.

Copy link

@zwhitchcox zwhitchcox commented Sep 6, 2020

What is the variable $GITHUB_OAUTH_BASIC supposed to be?

@zwhitchcox

This comment has been minimized.

Copy link

@zwhitchcox zwhitchcox commented Sep 6, 2020

Oh never mind, I see, it's if you want to add your actual username and password as an option

@zwhitchcox

This comment has been minimized.

Copy link

@zwhitchcox zwhitchcox commented Sep 6, 2020

Delete asset if exists using jq

sudo apt install jq

# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)
# Get ID of the release.
eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
[ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; }
release_id="$id"

# ------New Code starts Here------
# Get ID of the asset based on given filename.
id=""
for row in $(echo $response | jq '.assets | map({name: .name, id: .id})' | jq -c '.[]'); do
  name=$(echo ${row} | jq -r '.name')
  if [ $name == $filename ]; then
    asset_id=$(echo ${row} | jq -r '.id')
    echo "Deleting asset($asset_id)... "
    DELETE_URL="https://api.github.com/repos/${owner}/${repo}/releases/assets/${asset_id}"
    curl  -X "DELETE" -H "Authorization: token $github_api_token" "${DELETE_URL}"
  fi
done


# Upload asset
echo "Uploading asset... "

# Construct url
GH_ASSET="https://uploads.github.com/repos/${owner}/${repo}/releases/${release_id}/assets?name=$(basename ${filename})"
echo $GH_ASSET

curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET
echo
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.