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
timeout-minutes: 120
steps:
- name: Checkout
- name: Checkout the source tag
uses: https://code.forgejo.org/actions/checkout@v4
with:
# A tag push builds that tag; a manual dispatch builds the tag the
@ -37,6 +37,15 @@ jobs:
# release it is attached to).
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
run: |
set -euo pipefail
@ -62,7 +71,8 @@ jobs:
&& cargo build --release --locked \
&& strip /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"
mkdir -p dist
docker cp "$BUILD_CONTAINER:/target/release/onionwire" "dist/onionwire-$TARGET"
@ -80,16 +90,19 @@ jobs:
- name: Publish to the Forgejo release
env:
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
TARGET_COMMITISH: ${{ github.sha }}
REPO_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
EVENT_NAME: ${{ github.event_name }}
EVENT_SHA: ${{ github.sha }}
REF: ${{ github.ref_name }}
INPUT_TAG: ${{ github.event.inputs.tag }}
run: |
set -euo pipefail
cd "${GITHUB_WORKSPACE}"
tag="${INPUT_TAG:-$REF}"
echo "publishing $tag from $REPO_API"
scripts/publish-release.sh \
# Only a tag push may create the tag; a re-publish must not move it.
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" \
scripts/release-body.md \
.ci-tools/scripts/release-body.md \
"dist/onionwire-$TARGET" "dist/onionwire-$TARGET.sha256"

View file

@ -4,7 +4,9 @@
# Usage: publish-release.sh <tag> <release-name> <body-file> <asset> [<asset>...]
# Env: FORGEJO_TOKEN user PAT with repo write (required)
# 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
# same-named assets instead of failing with 409.
@ -17,7 +19,7 @@ shift 3
: "${FORGEJO_TOKEN:?FORGEJO_TOKEN is not set}"
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)"; }
@ -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'
import json, sys
body = open(sys.argv[1]).read().replace("@TAG@", sys.argv[2])
print(json.dumps({
payload = {
"tag_name": sys.argv[2],
"target_commitish": sys.argv[3],
"name": sys.argv[4],
"body": body,
"draft": 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
code=$(curl -s -o /tmp/release-rel.json -w '%{http_code}' \