fix(release): tooling checkout so old tags can publish; never move a tag
Some checks failed
ci / test (push) Successful in 5m46s
release / aarch64 (push) Failing after 5m46s

The dispatched build of v0.1.1 kept failing at publish with
'scripts/publish-release.sh: No such file or directory' — the workflow is
read from the dispatch ref (main) but the workspace was the v0.1.1 tag,
whose tree predates scripts/ and .forgejo/.

- second checkout of the workflow's own ref into .ci-tools; publish runs
  .ci-tools/scripts/publish-release.sh (and .ci-tools/scripts/release-body.md)
- exclude ./.ci-tools from the build context tar
- target_commitish is now opt-in in publish-release.sh and only set for a
  push event, so a re-publish can never move an existing tag

Verified locally end-to-end against a throwaway tag: create, idempotent
re-run (stale assets deleted + replaced), public download, sha256sum -c,
--version. Test release and tag deleted afterwards.
This commit is contained in:
Sirius DevOps 2026-09-10 13:58:46 -04:00
parent 154bed7823
commit 9441bccf58
No known key found for this signature in database
2 changed files with 30 additions and 11 deletions

View file

@ -29,7 +29,7 @@ jobs:
runs-on: docker runs-on: docker
timeout-minutes: 120 timeout-minutes: 120
steps: steps:
- name: Checkout - name: Checkout the source tag
uses: https://code.forgejo.org/actions/checkout@v4 uses: https://code.forgejo.org/actions/checkout@v4
with: with:
# A tag push builds that tag; a manual dispatch builds the tag the # A tag push builds that tag; a manual dispatch builds the tag the
@ -37,6 +37,15 @@ jobs:
# release it is attached to). # release it is attached to).
ref: ${{ github.event.inputs.tag || github.ref_name }} ref: ${{ github.event.inputs.tag || github.ref_name }}
- name: Checkout the CI tooling
uses: https://code.forgejo.org/actions/checkout@v4
with:
# scripts/ and .forgejo/ only exist on the branch (older tags predate
# them), and the workflow itself is read from the dispatched ref — so
# fetch the same ref into .ci-tools and run the scripts from there.
ref: ${{ github.ref_name }}
path: .ci-tools
- name: Build ${{ env.TARGET }} in a rust container - name: Build ${{ env.TARGET }} in a rust container
run: | run: |
set -euo pipefail set -euo pipefail
@ -62,7 +71,8 @@ jobs:
&& cargo build --release --locked \ && cargo build --release --locked \
&& strip /target/release/onionwire \ && strip /target/release/onionwire \
&& ls -l /target/release/onionwire' && ls -l /target/release/onionwire'
tar czf - --exclude=./target --exclude=./.git --exclude=./.worktrees . \ tar czf - --exclude=./target --exclude=./.git --exclude=./.worktrees \
--exclude=./.ci-tools . \
| docker start -a -i "$BUILD_CONTAINER" | docker start -a -i "$BUILD_CONTAINER"
mkdir -p dist mkdir -p dist
docker cp "$BUILD_CONTAINER:/target/release/onionwire" "dist/onionwire-$TARGET" docker cp "$BUILD_CONTAINER:/target/release/onionwire" "dist/onionwire-$TARGET"
@ -80,16 +90,19 @@ jobs:
- name: Publish to the Forgejo release - name: Publish to the Forgejo release
env: env:
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
TARGET_COMMITISH: ${{ github.sha }}
REPO_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} REPO_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
EVENT_NAME: ${{ github.event_name }}
EVENT_SHA: ${{ github.sha }}
REF: ${{ github.ref_name }} REF: ${{ github.ref_name }}
INPUT_TAG: ${{ github.event.inputs.tag }} INPUT_TAG: ${{ github.event.inputs.tag }}
run: | run: |
set -euo pipefail set -euo pipefail
cd "${GITHUB_WORKSPACE}" cd "${GITHUB_WORKSPACE}"
tag="${INPUT_TAG:-$REF}" tag="${INPUT_TAG:-$REF}"
echo "publishing $tag from $REPO_API" # Only a tag push may create the tag; a re-publish must not move it.
scripts/publish-release.sh \ if [ "$EVENT_NAME" = "push" ]; then export TARGET_COMMITISH="$EVENT_SHA"; fi
echo "publishing $tag from $REPO_API (event=$EVENT_NAME)"
.ci-tools/scripts/publish-release.sh \
"$tag" "OnionWire $tag" \ "$tag" "OnionWire $tag" \
scripts/release-body.md \ .ci-tools/scripts/release-body.md \
"dist/onionwire-$TARGET" "dist/onionwire-$TARGET.sha256" "dist/onionwire-$TARGET" "dist/onionwire-$TARGET.sha256"

View file

@ -4,7 +4,9 @@
# Usage: publish-release.sh <tag> <release-name> <body-file> <asset> [<asset>...] # Usage: publish-release.sh <tag> <release-name> <body-file> <asset> [<asset>...]
# Env: FORGEJO_TOKEN user PAT with repo write (required) # Env: FORGEJO_TOKEN user PAT with repo write (required)
# REPO_API default https://forgejo.siriusdevops.com/api/v1/repos/sirius/onionwire # REPO_API default https://forgejo.siriusdevops.com/api/v1/repos/sirius/onionwire
# TARGET_COMMITISH default: push-triggered commit (CI) else "main" # TARGET_COMMITISH optional; set it only when the release may have to
# create the tag (e.g. a push event's commit sha).
# Leave empty to never move an existing tag.
# #
# Idempotent: re-running for the same tag reuses the release and replaces # Idempotent: re-running for the same tag reuses the release and replaces
# same-named assets instead of failing with 409. # same-named assets instead of failing with 409.
@ -17,7 +19,7 @@ shift 3
: "${FORGEJO_TOKEN:?FORGEJO_TOKEN is not set}" : "${FORGEJO_TOKEN:?FORGEJO_TOKEN is not set}"
api="${REPO_API:-https://forgejo.siriusdevops.com/api/v1/repos/sirius/onionwire}" api="${REPO_API:-https://forgejo.siriusdevops.com/api/v1/repos/sirius/onionwire}"
target="${TARGET_COMMITISH:-main}" target="${TARGET_COMMITISH:-}"
jqp() { python3 -c "import json,sys; d=json.load(sys.stdin); print($1)"; } jqp() { python3 -c "import json,sys; d=json.load(sys.stdin); print($1)"; }
@ -25,14 +27,18 @@ jqp() { python3 -c "import json,sys; d=json.load(sys.stdin); print($1)"; }
python3 - "$body_file" "$tag" "$target" "$name" > /tmp/release-body.json <<'PY' python3 - "$body_file" "$tag" "$target" "$name" > /tmp/release-body.json <<'PY'
import json, sys import json, sys
body = open(sys.argv[1]).read().replace("@TAG@", sys.argv[2]) body = open(sys.argv[1]).read().replace("@TAG@", sys.argv[2])
print(json.dumps({ payload = {
"tag_name": sys.argv[2], "tag_name": sys.argv[2],
"target_commitish": sys.argv[3],
"name": sys.argv[4], "name": sys.argv[4],
"body": body, "body": body,
"draft": False, "draft": False,
"prerelease": False, "prerelease": False,
})) }
# Only send target_commitish when asked: on an existing tag it is a request to
# move the tag, which is never what a re-publish wants.
if sys.argv[3]:
payload["target_commitish"] = sys.argv[3]
print(json.dumps(payload))
PY PY
code=$(curl -s -o /tmp/release-rel.json -w '%{http_code}' \ code=$(curl -s -o /tmp/release-rel.json -w '%{http_code}' \