Skip to content

Instantly share code, notes, and snippets.

@stefanbuck
Last active November 26, 2023 12:40
Show Gist options
  • Save stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 to your computer and use it in GitHub Desktop.
Save stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 to your computer and use it in GitHub Desktop.
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
@allyshoww
Copy link

allyshoww 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
Copy link

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/

@allyshoww
Copy link

allyshoww 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
Copy link

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
Copy link

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
Copy link

janiez commented Feb 24, 2020

Good job!!

@zwhitchcox
Copy link

What is the variable $GITHUB_OAUTH_BASIC supposed to be?

@zwhitchcox
Copy link

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

@zwhitchcox
Copy link

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

@SjarletK
Copy link

SjarletK commented Jun 17, 2021

one little correction:

(...)
for row in $(echo $response | jq '.assets | map({name: .name, id: .id})' | jq -c '.[]'); do
  name=$(echo ${row} | jq -r '.name')
  if [ $name == basename $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
(...)

@lilianmoraru
Copy link

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

Github Actions is not available on our enterprise server.
This script is still quite useful, thanks.

@CaliforniaMountainSnake
Copy link

Got a curl: (3) URL using bad/illegal format or missing URL error, but this script seems to be working!

@sfuerte
Copy link

sfuerte commented Jun 8, 2022

just quick 2c:
if jq is available, then it's much easier, faster and less error-prone to extract IDs and URLs via it.
Here is a response example:

$ curl https://api.github.com/repos/hashicorp/packer/releases/latest
{
  "url": "https://api.github.com/repos/hashicorp/packer/releases/68048553",
  "assets_url": "https://api.github.com/repos/hashicorp/packer/releases/68048553/assets",
  "upload_url": "https://uploads.github.com/repos/hashicorp/packer/releases/68048553/assets{?name,label}",
  "html_url": "https://github.com/hashicorp/packer/releases/tag/v1.8.1",
  "id": 68048553,
...

Then an upload URL and its usage transforms to:

UPLOAD_URL=$(curl -sH "${GH_AUTH}" "$(GITHUB_REPO)/releases/tags/$(VERSION_FULL)" \
	| jq -r '.upload_url' | cut -d'{' -f1)

curl -X POST \
	-H "${GH_AUTH}" \
	-H "Accept: application/vnd.github.v3+json" \
	-H "Content-Type: $(file -b --mime-type ${FILE})" \
	-H "Content-Length: $(wc -c <${FILE} | xargs)" \
	-T "${FILE}" \
	"${UPLOAD_URL}?name=$(basename ${FILE})" | cat

Also, it automatically works for enterprise environments with custom URLs, e.g. in our environment uploads.github.<corp FQDN> is not available and github.<corp FQDN>/api/uploads/ is used instead.

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