ci: resolve the release tag in one place
A bare dispatch carries no inputs through the API, so the tag now comes from the input, else the triggering tag, else the newest v* — and a ref that is not a v* tag stops the run rather than cutting a release named after a branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6da556b parent: 63a7159 modified
.rickub/workflows/release.yml +26 -5 | @@ -27,7 +27,32 @@ jobs: | ||
| 27 | 27 | steps: |
| 28 | 28 | - uses: actions/checkout@v4 |
| 29 | 29 | with: |
| 30 | - ref: ${{ inputs.tag || github.ref }} | |
| 30 | + fetch-depth: 0 # tags, so a dispatch can find the latest one | |
| 31 | + | |
| 32 | + # One place decides which tag is being released: the dispatch input, the | |
| 33 | + # tag that triggered the run, or — for a bare dispatch, which carries no | |
| 34 | + # inputs through the API — the newest v* tag. Anything else is a mistake | |
| 35 | + # worth stopping for, since the alternative is a release named after a | |
| 36 | + # branch. | |
| 37 | + - name: Resolve the tag | |
| 38 | + env: | |
| 39 | + INPUT_TAG: ${{ inputs.tag }} | |
| 40 | + run: | | |
| 41 | + set -euo pipefail | |
| 42 | + if [ -n "${INPUT_TAG:-}" ]; then | |
| 43 | + TAG="$INPUT_TAG" | |
| 44 | + elif [ "$GITHUB_REF_TYPE" = tag ]; then | |
| 45 | + TAG="$GITHUB_REF_NAME" | |
| 46 | + else | |
| 47 | + TAG=$(git tag --list 'v*' --sort=-v:refname | head -1) | |
| 48 | + fi | |
| 49 | + case "$TAG" in | |
| 50 | + v*) ;; | |
| 51 | + *) echo "refusing to release '$TAG': not a v* tag" >&2; exit 1 ;; | |
| 52 | + esac | |
| 53 | + git checkout --detach "$TAG" | |
| 54 | + echo "releasing $TAG at $(git rev-parse --short HEAD)" | |
| 55 | + echo "TAG=$TAG" >> "$GITHUB_ENV" | |
| 31 | 56 | |
| 32 | 57 | # Toolchains come straight from upstream tarballs rather than setup |
| 33 | 58 | # actions: two curls, pinned versions, nothing else to trust. |
| @@ -61,8 +86,6 @@ jobs: | ||
| 61 | 86 | run: nim c -d:release -d:ssl --hints:off -o:nimstatic-host src/nimstatic.nim |
| 62 | 87 | |
| 63 | 88 | - name: Build the static binary with itself |
| 64 | - env: | |
| 65 | - TAG: ${{ inputs.tag || github.ref_name }} | |
| 66 | 89 | run: | |
| 67 | 90 | set -euo pipefail |
| 68 | 91 | VERSION="${TAG#v}" |
| @@ -86,8 +109,6 @@ jobs: | ||
| 86 | 109 | # is whatever the API advertises in upload_url — the one value that |
| 87 | 110 | # cannot go stale. |
| 88 | 111 | - name: Attach the binaries to the release |
| 89 | - env: | |
| 90 | - TAG: ${{ inputs.tag || github.ref_name }} | |
| 91 | 112 | run: | |
| 92 | 113 | set -euo pipefail |
| 93 | 114 | api() { curl -sSfL -H "Authorization: Bearer $GITHUB_TOKEN" \ |
| @@ -27,7 +27,32 @@ jobs: | |||
| 27 | steps: | 27 | steps: |
| 28 | - uses: actions/checkout@v4 | 28 | - uses: actions/checkout@v4 |
| 29 | with: | 29 | with: |
| 30 | - ref: ${{ inputs.tag || github.ref }} | 30 | + fetch-depth: 0 # tags, so a dispatch can find the latest one |
| 31 | + | ||
| 32 | + # One place decides which tag is being released: the dispatch input, the | ||
| 33 | + # tag that triggered the run, or — for a bare dispatch, which carries no | ||
| 34 | + # inputs through the API — the newest v* tag. Anything else is a mistake | ||
| 35 | + # worth stopping for, since the alternative is a release named after a | ||
| 36 | + # branch. | ||
| 37 | + - name: Resolve the tag | ||
| 38 | + env: | ||
| 39 | + INPUT_TAG: ${{ inputs.tag }} | ||
| 40 | + run: | | ||
| 41 | + set -euo pipefail | ||
| 42 | + if [ -n "${INPUT_TAG:-}" ]; then | ||
| 43 | + TAG="$INPUT_TAG" | ||
| 44 | + elif [ "$GITHUB_REF_TYPE" = tag ]; then | ||
| 45 | + TAG="$GITHUB_REF_NAME" | ||
| 46 | + else | ||
| 47 | + TAG=$(git tag --list 'v*' --sort=-v:refname | head -1) | ||
| 48 | + fi | ||
| 49 | + case "$TAG" in | ||
| 50 | + v*) ;; | ||
| 51 | + *) echo "refusing to release '$TAG': not a v* tag" >&2; exit 1 ;; | ||
| 52 | + esac | ||
| 53 | + git checkout --detach "$TAG" | ||
| 54 | + echo "releasing $TAG at $(git rev-parse --short HEAD)" | ||
| 55 | + echo "TAG=$TAG" >> "$GITHUB_ENV" | ||
| 31 | 56 | ||
| 32 | # Toolchains come straight from upstream tarballs rather than setup | 57 | # Toolchains come straight from upstream tarballs rather than setup |
| 33 | # actions: two curls, pinned versions, nothing else to trust. | 58 | # actions: two curls, pinned versions, nothing else to trust. |
| @@ -61,8 +86,6 @@ jobs: | |||
| 61 | run: nim c -d:release -d:ssl --hints:off -o:nimstatic-host src/nimstatic.nim | 86 | run: nim c -d:release -d:ssl --hints:off -o:nimstatic-host src/nimstatic.nim |
| 62 | 87 | ||
| 63 | - name: Build the static binary with itself | 88 | - name: Build the static binary with itself |
| 64 | - env: | ||
| 65 | - TAG: ${{ inputs.tag || github.ref_name }} | ||
| 66 | run: | | 89 | run: | |
| 67 | set -euo pipefail | 90 | set -euo pipefail |
| 68 | VERSION="${TAG#v}" | 91 | VERSION="${TAG#v}" |
| @@ -86,8 +109,6 @@ jobs: | |||
| 86 | # is whatever the API advertises in upload_url — the one value that | 109 | # is whatever the API advertises in upload_url — the one value that |
| 87 | # cannot go stale. | 110 | # cannot go stale. |
| 88 | - name: Attach the binaries to the release | 111 | - name: Attach the binaries to the release |
| 89 | - env: | ||
| 90 | - TAG: ${{ inputs.tag || github.ref_name }} | ||
| 91 | run: | | 112 | run: | |
| 92 | set -euo pipefail | 113 | set -euo pipefail |
| 93 | api() { curl -sSfL -H "Authorization: Bearer $GITHUB_TOKEN" \ | 114 | api() { curl -sSfL -H "Authorization: Bearer $GITHUB_TOKEN" \ |