|
47 | 47 | type: boolean |
48 | 48 | default: true |
49 | 49 | release_draft: |
50 | | - description: "Create the GitHub Release as a draft" |
| 50 | + # Assets are always uploaded to a draft first (required for immutable |
| 51 | + # releases); this flag controls whether the draft is left unpublished |
| 52 | + # for manual review (true) or automatically published (false). |
| 53 | + description: "Leave the GitHub Release as an unpublished draft (skip the auto-publish step)" |
51 | 54 | required: false |
52 | 55 | type: boolean |
53 | 56 | default: false |
@@ -651,23 +654,34 @@ jobs: |
651 | 654 | GH_TOKEN: ${{ github.token }} |
652 | 655 | TAG_NAME: ${{ github.ref_name }} |
653 | 656 | run: | |
654 | | - # Create release if it doesn't exist |
| 657 | + # Always create the release as a DRAFT first, attach every asset, and |
| 658 | + # only THEN publish it. This ordering is mandatory for GitHub's |
| 659 | + # immutable releases: once a release is published its assets can't be |
| 660 | + # added, modified, or deleted, so all assets must be in place before |
| 661 | + # the publish transition. It is also strictly backward-compatible for |
| 662 | + # repos without immutability enabled — the final state is an identical |
| 663 | + # published release with all assets attached. |
655 | 664 | if ! gh release view "$TAG_NAME" &>/dev/null; then |
656 | | - DRAFT_FLAG="" |
657 | | - if [ "${{ inputs.release_draft }}" = "true" ]; then |
658 | | - DRAFT_FLAG="--draft" |
659 | | - fi |
660 | 665 | # Tags carrying a known pre-release identifier (rc, alpha, |
661 | 666 | # beta, pre/preview, dev) are published as pre-releases, |
662 | 667 | # with "Latest" left pinned to the most recent stable tag |
663 | 668 | # — otherwise an RC silently becomes the release that |
664 | 669 | # `@latest` installers and the release badge resolve to. |
| 670 | + # Set the pre-release/latest attributes at create time; they persist |
| 671 | + # through the publish transition below. |
665 | 672 | PRERELEASE_FLAG="" |
666 | 673 | case "$TAG_NAME" in |
667 | 674 | *-rc*|*-alpha*|*-beta*|*-pre*|*-dev*) |
668 | 675 | PRERELEASE_FLAG="--prerelease --latest=false" ;; |
669 | 676 | esac |
670 | | - gh release create "$TAG_NAME" $DRAFT_FLAG $PRERELEASE_FLAG --generate-notes |
| 677 | + gh release create "$TAG_NAME" --draft $PRERELEASE_FLAG --generate-notes |
671 | 678 | fi |
672 | | - # Upload assets (--clobber overwrites existing) |
| 679 | + # Attach all assets to the (still draft) release (--clobber overwrites |
| 680 | + # existing on re-runs). |
673 | 681 | gh release upload "$TAG_NAME" artifacts/* --clobber |
| 682 | + # Publish the draft — this transition fires the `release: published` |
| 683 | + # event and, when immutable releases are enabled, locks the tag and |
| 684 | + # assets. Skip it when the caller opted into a draft for manual review. |
| 685 | + if [ "${{ inputs.release_draft }}" != "true" ]; then |
| 686 | + gh release edit "$TAG_NAME" --draft=false |
| 687 | + fi |
0 commit comments