-
-
Save stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 to your computer and use it in GitHub Desktop.
#!/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 |
That's a very cool script, can you add a license to it so I could reuse it on other projects?
Thanks in advance
@babelouest License added, thanks for the hint.
@WesleyBatista Good spot. I removed $localAssetPath
, it's not really needed
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
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?
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.
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
@stefanbuck what in that case if we need to upload more then 1 zip file ?
@awais786327 I recommend using GitHub Actions for this task https://github.com/actions/upload-release-asset nowadays.
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
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?
@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/
@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
fromhttps://api.github.com
tohttp://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?
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,
@allysono
Error: Invalid repo, token or network issue!
- First thing first, check you repo is on
github.com
or your enterprise servergithub.<company>.com
- Second, check your token has scope with repo in web page
https://github.com/settings/tokens
, or maybehttps://github<company>.com/settings/tokens
Good job!!
What is the variable $GITHUB_OAUTH_BASIC supposed to be?
Oh never mind, I see, it's if you want to add your actual username and password as an option
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
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
(...)
@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.
Got a curl: (3) URL using bad/illegal format or missing URL
error, but this script seems to be working!
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.
Didn't tried your snippet, but
$localAssetPath
is not defined. It should work? maybe you meant$filename