Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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<major>` tags are updated with each release. If you want to enhance workflow stability and security against supply chain attacks, consider using the `@v<major>.<minor>.<patch>` 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.
Expand Down
26 changes: 24 additions & 2 deletions main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 -E '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']/ }"
Expand Down Expand Up @@ -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
Loading