ci: fix HTTP-code parsing that left auto-build releases without a jar #3

Merged
wait4mi merged 2 commits from fix/ci-release-upload into main 2026-09-05 20:15:05 +00:00
Contributor

Why the last three releases had no jar

Every scheduled auto-build since 2026-09-01 (v6.0.1-unlock, v6.0.3-unlock, v6.0.5-unlock) created the release and then exited 1 about two seconds later, before uploading the patched jar. Cause, in the "Tag and create release" step:

REL_HTTP=$(echo "$REL_RESP" | grep -E '^__HTTP__' | cut -d_ -f4-)   # -> "_201", not "201"
if [ "$REL_HTTP" != "201" ]; then ... exit 1

cut -d_ -f4- on __HTTP__201 returns _201, so the guard always fired. This code path had never run before 09-01 (the v5.4.1-unlock1/2 tags are manual and the prefix gate skipped every earlier run), which is why it went unnoticed. Verified by elimination: a 47 MB .jar upload with the Actions GITHUB_TOKEN from the lesannes-ubuntu runner works fine (probe repo, since deleted). Actions logs are not readable on this instance (task … resource does not exist), hence the reproduction.

Changes

  • Status codes are split off with parameter expansion (${RESP##*__HTTP__}) instead of cut.
  • Modrinth resolver filters on game_versions (TARGET_MC=26.2). Run #135 (09-02) failed because 6.0.2 was published for 1.21.x only and the patcher was fed a 1.21.4 jar.
  • Gate rebuilds when the tag exists but its release has no asset, so the daily cron self-heals a run that died between release creation and upload.
  • Tag / release / asset steps are idempotent (reuse existing tag, PATCH existing release, replace a same-name asset), so force_rebuild now actually works instead of dying on git tag.

Validation

Ran end to end in a throwaway repo (wait4mi/ekaxiom-ci-test, same runner, deleted afterwards):

  1. workflow_dispatch with force_rebuild=true on a repo without tags → tag v6.0.5-unlock pushed, release created, Axiom-6.0.5-for-MC26.2-Ekaxiom.jar (47 074 587 bytes) attached, sha256 d8b4b915… identical to the one the failed 09-04 run had computed.
  2. Asset deleted by hand, dispatch without force → gate reports "tag exists but release has no asset", rebuild, release reused, jar re-attached.
  3. Dispatch again with the asset present → gate reports "nothing to do".

Releases already repaired

The three jars were rebuilt locally from the sha512-verified upstream files with the patcher at eba340e and attached by hand on 2026-09-05; their release notes carry the hashes of the attached files.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BKdUPzBxffNM3DpudgeyXn

## Why the last three releases had no jar Every scheduled auto-build since 2026-09-01 (`v6.0.1-unlock`, `v6.0.3-unlock`, `v6.0.5-unlock`) created the release and then exited 1 about two seconds later, before uploading the patched jar. Cause, in the "Tag and create release" step: ```bash REL_HTTP=$(echo "$REL_RESP" | grep -E '^__HTTP__' | cut -d_ -f4-) # -> "_201", not "201" if [ "$REL_HTTP" != "201" ]; then ... exit 1 ``` `cut -d_ -f4-` on `__HTTP__201` returns `_201`, so the guard always fired. This code path had never run before 09-01 (the `v5.4.1-unlock1/2` tags are manual and the prefix gate skipped every earlier run), which is why it went unnoticed. Verified by elimination: a 47 MB `.jar` upload with the Actions `GITHUB_TOKEN` from the `lesannes-ubuntu` runner works fine (probe repo, since deleted). Actions logs are not readable on this instance (`task … resource does not exist`), hence the reproduction. ## Changes - Status codes are split off with parameter expansion (`${RESP##*__HTTP__}`) instead of `cut`. - Modrinth resolver filters on `game_versions` (`TARGET_MC=26.2`). Run #135 (09-02) failed because 6.0.2 was published for 1.21.x only and the patcher was fed a 1.21.4 jar. - Gate rebuilds when the tag exists but its release has **no asset**, so the daily cron self-heals a run that died between release creation and upload. - Tag / release / asset steps are idempotent (reuse existing tag, `PATCH` existing release, replace a same-name asset), so `force_rebuild` now actually works instead of dying on `git tag`. ## Validation Ran end to end in a throwaway repo (`wait4mi/ekaxiom-ci-test`, same runner, deleted afterwards): 1. `workflow_dispatch` with `force_rebuild=true` on a repo without tags → tag `v6.0.5-unlock` pushed, release created, `Axiom-6.0.5-for-MC26.2-Ekaxiom.jar` (47 074 587 bytes) attached, sha256 `d8b4b915…` identical to the one the failed 09-04 run had computed. 2. Asset deleted by hand, dispatch without force → gate reports "tag exists but release has no asset", rebuild, release reused, jar re-attached. 3. Dispatch again with the asset present → gate reports "nothing to do". ## Releases already repaired The three jars were rebuilt locally from the sha512-verified upstream files with the patcher at `eba340e` and attached by hand on 2026-09-05; their release notes carry the hashes of the attached files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01BKdUPzBxffNM3DpudgeyXn
Every scheduled run since 2026-09-01 (v6.0.1, v6.0.3, v6.0.5-unlock) created
the release and then exited 1 before uploading the patched jar:
`cut -d_ -f4-` on `__HTTP__201` yields `_201`, so the "!= 201" guard always
fired. Status codes are now split off with parameter expansion.

Also:
- filter Modrinth versions on game_versions (TARGET_MC=26.2): 6.0.2 was
  published for 1.21.x only and the patcher was fed a 1.21.4 jar (run #135)
- gate rebuilds when the tag exists but its release has no asset, so the
  daily cron self-heals a run that died between release and upload
- tag/release/asset steps are idempotent (reuse tag, PATCH existing
  release, replace same-name asset) so force_rebuild actually works

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKdUPzBxffNM3DpudgeyXn
Review follow-up:
- gate: only HTTP 200/404 are interpreted; any other status (expired
  token, 5xx, timeout) aborts the run instead of being read as "no tag /
  no asset", which would have deleted and re-uploaded a good asset
- stale-asset cleanup: listing and DELETE are status-checked so a failed
  cleanup cannot leave duplicate-named jars on the release

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKdUPzBxffNM3DpudgeyXn
Author
Contributor

Independent review (fresh reviewer, diff only) found nothing blocking; two hardening points addressed in 79c0051: the gate now aborts on any API status other than 200/404 instead of reading it as "no tag / no asset" (which would have deleted and re-uploaded a good jar), and the stale-asset cleanup checks both the listing and the DELETE status so duplicates cannot pile up. Re-validated end to end on a throwaway repo (same runner): fresh build, repair of an empty release, no-op when the asset exists, tag without release object, and force_rebuild with everything present � all five publish exactly one jar. Merging.

Independent review (fresh reviewer, diff only) found nothing blocking; two hardening points addressed in `79c0051`: the gate now aborts on any API status other than 200/404 instead of reading it as "no tag / no asset" (which would have deleted and re-uploaded a good jar), and the stale-asset cleanup checks both the listing and the DELETE status so duplicates cannot pile up. Re-validated end to end on a throwaway repo (same runner): fresh build, repair of an empty release, no-op when the asset exists, tag without release object, and `force_rebuild` with everything present � all five publish exactly one jar. Merging.
wait4mi deleted branch fix/ci-release-upload 2026-09-05 20:15:05 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
exo/Ekaxiom!3
No description provided.