Skip to content

Instantly share code, notes, and snippets.

Last active November 26, 2023 12:40
Star You must be signed in to star a gist
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
# 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:
# github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./
# Check dependencies.
set -e
xargs=$(which gxargs || which xargs)
# Validate settings.
[ "$TRACE" ] && set -x
for line in $CONFIG; do
eval "$line"
# Define variables.
AUTH="Authorization: token $github_api_token"
WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
if [[ "$tag" == 'LATEST' ]]; then
# 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="$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
Copy link

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

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
  "url": "",
  "assets_url": "",
  "upload_url": "{?name,label}",
  "html_url": "",
  "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