From 302c7d52069108c627c0fc4e8d2f4dd20f677187 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Sun, 26 Apr 2026 11:01:05 +0000 Subject: [PATCH 1/2] Support draft release uploads Signed-off-by: Gregor Zeitlinger --- CHANGELOG.md | 2 ++ README.md | 5 +++++ main.sh | 26 ++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ab8ed52..b87c03dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +- Support uploading assets to existing draft releases. + ## [1.30.2] - 2026-04-17 - Enhance security when `dry-run` is true. diff --git a/README.md b/README.md index 9044d873..b84553db 100644 --- a/README.md +++ b/README.md @@ -718,6 +718,9 @@ The following two events are supported by default: types: [created] ``` +Draft releases are supported as long as the GitHub release already exists before this action runs. +This is useful for workflows that create a draft release first, upload all assets, and publish only after every upload succeeds. + You can upload binaries from arbitrary event to arbitrary tag by specifying the `ref` input option. For example, to upload binaries to the `my_tag` tag, specify `ref` input option as follows: @@ -727,6 +730,8 @@ with: ref: refs/tags/my_tag ``` +This also works when the existing release for `my_tag` is still a draft, such as when using release automation that creates draft releases first. + ## Security The `@v` tags are updated with each release. If you want to enhance workflow stability and security against supply chain attacks, consider using the `@v..` tag or their hash to pin the version and regularly updating with dependency cooldown. Since all releases are immutable, pinning the version in either way should have the same effect. diff --git a/main.sh b/main.sh index ac3da506..c4e16b53 100755 --- a/main.sh +++ b/main.sh @@ -29,6 +29,29 @@ warn() { info() { printf >&2 'info: %s\n' "$*" } +gh_api() { + GH_TOKEN="${token}" gh api "$@" +} +upload_release_assets() { + local release_id upload_url asset asset_name asset_id + release_id="$(gh_api "repos/${GITHUB_REPOSITORY}/releases" --paginate --jq ".[] | select(.tag_name == \"${tag}\") | .id" | head -n1)" + if [[ -z "${release_id}" ]]; then + bail "GitHub release for tag '${tag}' not found; create the release before running this action" + fi + upload_url="$(gh_api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" --jq '.upload_url' | sed 's/{?name,label}//')" + for asset in "${final_assets[@]}"; do + asset_name="$(basename "${asset}")" + asset_id="$(gh_api "repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets" --paginate --jq ".[] | select(.name == \"${asset_name}\") | .id" | head -n1 || true)" + if [[ -n "${asset_id}" ]]; then + retry gh_api "repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" -X DELETE >/dev/null + fi + retry curl --fail --silent --show-error \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/octet-stream" \ + "${upload_url}?name=${asset_name}" \ + --data-binary "@${asset}" >/dev/null + done +} normalize_comma_or_space_separated() { # Normalize whitespace characters into space because it's hard to handle single input contains lines with POSIX sed alone. local list="${1//[$'\r\n\t']/ }" @@ -635,6 +658,5 @@ if [[ -n "${dry_run}" ]]; then printf 'assets: %s\n' "${final_assets[*]}" IFS=$'\n\t' else - # https://cli.github.com/manual/gh_release_upload - GITHUB_TOKEN="${token}" retry gh release upload "${tag}" "${final_assets[@]}" --clobber + upload_release_assets fi From be8c554f0af3c867d716c7f950764f9ccaa2565a Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Sun, 26 Apr 2026 11:05:10 +0000 Subject: [PATCH 2/2] Fix shell tidy issues Signed-off-by: Gregor Zeitlinger --- main.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.sh b/main.sh index c4e16b53..03e5e1e0 100755 --- a/main.sh +++ b/main.sh @@ -38,9 +38,9 @@ upload_release_assets() { if [[ -z "${release_id}" ]]; then bail "GitHub release for tag '${tag}' not found; create the release before running this action" fi - upload_url="$(gh_api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" --jq '.upload_url' | sed 's/{?name,label}//')" + upload_url="$(gh_api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" --jq '.upload_url' | sed -E 's/{\?name,label}//')" for asset in "${final_assets[@]}"; do - asset_name="$(basename "${asset}")" + asset_name="$(basename -- "${asset}")" asset_id="$(gh_api "repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets" --paginate --jq ".[] | select(.name == \"${asset_name}\") | .id" | head -n1 || true)" if [[ -n "${asset_id}" ]]; then retry gh_api "repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" -X DELETE >/dev/null