[ci] Release artifacts not published — --from-ci install path blind to this repo #13

Closed
opened 2026-05-03 23:36:11 +00:00 by mik-tf · 2 comments
Owner

Audit context

Filed during Phase 2 CI audit (hero_demo#54, session 55).

State

  • Tags exist: v0.1.0, v0.1.1, v0.1.2
  • Forgejo Releases: none
  • Forgejo package registry: hero_biz 0.1.2 ✓ binary present
  • build-linux.yaml runs report status=success

Root cause

scripts/build_lib.sh::publish_binaries writes only to the Forgejo package registry (/api/packages/<owner>/generic/<pkg>/<version>/<asset>). It never writes a Forgejo Release nor uploads to /api/v1/repos/<repo>/releases/<id>/assets.

svc_install_from_ci (in hero_skills/tools/modules/services/lib.nu) downloads from forge.ourworld.tf/<repo>/releases/download/<tag>/<asset> — i.e. release assets. Net: this repo's CI succeeds but --from-ci hero_biz cannot find any artifact.

Cluster

Same root cause: hero_books#118, hero_browser#16. hero_foundry#26 is likely affected too (Feb-2026 runs predate publish step working). All four use the same shared-helper template.

Mirror hero_proc / hero_router pattern: add explicit Create Release + Upload Release Assets steps to build-linux.yaml (using /api/v1/repos/<repo>/releases and /releases/<id>/assets). Keep the existing pkg-registry publish.

Highest leverage: add a publish_release_assets helper to scripts/build_lib.sh and call it alongside publish_binaries. The fix would propagate to all repos using build_lib.sh once they pull the update.

Effort

Quick win — single new step modeled on hero_proc's build-linux.yaml lines 53-106. Estimated 1-2 h to implement, tag, validate. Phase 2 implementation.

## Audit context Filed during Phase 2 CI audit ([hero_demo#54](https://forge.ourworld.tf/lhumina_code/hero_demo/issues/54), session 55). ## State - Tags exist: `v0.1.0`, `v0.1.1`, `v0.1.2` - Forgejo Releases: **none** - Forgejo package registry: `hero_biz 0.1.2` ✓ binary present - `build-linux.yaml` runs report `status=success` ## Root cause `scripts/build_lib.sh::publish_binaries` writes only to the Forgejo **package registry** (`/api/packages/<owner>/generic/<pkg>/<version>/<asset>`). It never writes a Forgejo **Release** nor uploads to `/api/v1/repos/<repo>/releases/<id>/assets`. `svc_install_from_ci` (in [hero_skills/tools/modules/services/lib.nu](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/tools/modules/services/lib.nu#L510)) downloads from `forge.ourworld.tf/<repo>/releases/download/<tag>/<asset>` — i.e. **release assets**. Net: this repo's CI succeeds but `--from-ci hero_biz` cannot find any artifact. ## Cluster Same root cause: [hero_books#118](https://forge.ourworld.tf/lhumina_code/hero_books/issues/118), [hero_browser#16](https://forge.ourworld.tf/lhumina_code/hero_browser/issues/16). [hero_foundry#26](https://forge.ourworld.tf/lhumina_code/hero_foundry/issues/26) is likely affected too (Feb-2026 runs predate publish step working). All four use the same shared-helper template. ## Recommended fix Mirror hero_proc / hero_router pattern: add explicit **Create Release** + **Upload Release Assets** steps to `build-linux.yaml` (using `/api/v1/repos/<repo>/releases` and `/releases/<id>/assets`). Keep the existing pkg-registry publish. Highest leverage: add a `publish_release_assets` helper to `scripts/build_lib.sh` and call it alongside `publish_binaries`. The fix would propagate to all repos using build_lib.sh once they pull the update. ## Effort Quick win — single new step modeled on hero_proc's [`build-linux.yaml` lines 53-106](https://forge.ourworld.tf/lhumina_code/hero_proc/src/branch/development/.forgejo/workflows/build-linux.yaml#L53). Estimated 1-2 h to implement, tag, validate. Phase 2 implementation.
Member
Fix CI so that `--from-ci hero_biz` installs correctly. `build-linux.yaml` only calls `publish_binaries`, which pushes to the Forgejo generic package registry. `svc_install_from_ci` resolves binaries via the Forgejo Release asset endpoint (`/releases/download/<tag>/<asset>`), which is never populated by CI. This spec adds a `publish_release_assets` helper to `scripts/build_lib.sh` and two new steps to `build-linux.yaml` that mirror the hero_proc pattern.

Implementation Spec for Issue #13

Objective

Fix CI so that --from-ci hero_biz installs correctly. build-linux.yaml only calls publish_binaries, which pushes to the Forgejo generic package registry. svc_install_from_ci resolves binaries via the Forgejo Release asset endpoint (/releases/download/<tag>/<asset>), which is never populated by CI. This spec adds a publish_release_assets helper to scripts/build_lib.sh and two new steps to build-linux.yaml that mirror the hero_proc pattern.


Requirements

  1. A publish_release_assets function in scripts/build_lib.sh that creates (or retrieves existing) a Forgejo Release for the current tag and uploads each binary as a release asset.
  2. The function must be idempotent: if a release already exists for the tag, it retrieves the ID rather than failing.
  3. build-linux.yaml grows two new steps ("Create Release", "Upload Release Assets") placed before the existing "Publish" step.
  4. The existing "Publish" step (package registry) remains unchanged — both publication paths coexist.
  5. jq must be available in the CI container (added to the "Setup toolchain" step).
  6. The existing "Publish" step must explicitly export BIN_DIR, SERVER, and OWNER — a latent bug where it relies on env vars that don't survive across steps.

Files to Modify

File Change
.forgejo/workflows/build-linux.yaml Add jq to toolchain step; add "Create Release" and "Upload Release Assets" steps; fix "Publish" step env exports
scripts/build_lib.sh Add publish_release_assets function after publish_binaries

Implementation Plan

Step 1: Add publish_release_assets to build_lib.sh

File: scripts/build_lib.sh
Insert after closing of publish_binaries. The function:

  • Validates required env vars: BIN_DIR, BINARIES, FORGEJO_TOKEN, SERVER, OWNER, PROJECT_NAME
  • POSTs to /api/v1/repos/$OWNER/$PROJECT_NAME/releases to create the release
  • Falls back to GET /releases/tags/$TAG if POST returns null id (already exists)
  • Iterates $BINARIES, uploads each as <bin>-<suffix> via POST to /releases/$RELEASE_ID/assets?name=<artifact>
  • Uses Content-Type: application/x-pie-executable (matches hero_proc pattern)
    Dependencies: none (parallel with Step 2)

Step 2: Add jq to "Setup toolchain" step in build-linux.yaml

File: .forgejo/workflows/build-linux.yaml
Append apt-get install -y jq after the existing setup_linux_toolchain call.
Dependencies: none (parallel with Step 1)

Step 3: Add "Create Release" and "Upload Release Assets" steps to build-linux.yaml

File: .forgejo/workflows/build-linux.yaml
Insert between the "Build" step and the existing "Publish" step:

"Create Release" step (inline YAML shell, outputs release_id via $GITHUB_OUTPUT):

  • Creates release via API using github.ref_name, github.server_url, github.repository
  • Falls back to GET by tag if already exists
  • Writes release_id=<id> to $GITHUB_OUTPUT

"Upload Release Assets" step (calls publish_release_assets from build_lib.sh):

  • Sources build_lib.sh
  • Exports BIN_DIR="$(bin_dir "${{ matrix.target }}")", SERVER, OWNER
  • Calls publish_release_assets "${{ github.ref_name }}" "${{ matrix.artifact }}"

Dependencies: Steps 1 and 2

Step 4: Fix the existing "Publish" step env exports

File: .forgejo/workflows/build-linux.yaml
Add export BIN_DIR="$(bin_dir "${{ matrix.target }}")", SERVER, OWNER to the existing "Publish" step before calling publish_binaries.
Dependencies: Step 3


Acceptance Criteria

  • Pushing a v* tag produces a Forgejo Release at /releases/tag/<tag> with four assets: hero_biz-linux-amd64, hero_biz_ui-linux-amd64, hero_biz-linux-arm64, hero_biz_ui-linux-arm64
  • svc_install_from_ci hero_biz succeeds — downloads from /releases/download/<tag>/<asset> without 404
  • Package registry upload continues to work (generic packages still present)
  • Re-running the workflow for the same tag does not fail (idempotent release creation)
  • publish_release_assets can be called locally with env vars set

Notes

  • The matrix (x86_64 + aarch64) runs as two independent jobs. Each creates-or-retrieves the same release and uploads its own two assets. The idempotent fallback handles the race where both jobs attempt release creation simultaneously.
  • buildenv.sh declares VERSION="0.1.0" but Cargo.toml workspace has version = "0.5.0" — pre-existing mismatch tracked separately. The CI derives release tag from github.ref_name, not VERSION, so this does not block the fix.
  • This same pattern (publish_release_assets in build_lib.sh) would propagate to hero_books#118, hero_browser#16, and hero_foundry#26 once they pull the shared helper update.

--- ## Implementation Spec for Issue #13 ### Objective Fix CI so that `--from-ci hero_biz` installs correctly. `build-linux.yaml` only calls `publish_binaries`, which pushes to the Forgejo generic package registry. `svc_install_from_ci` resolves binaries via the Forgejo Release asset endpoint (`/releases/download/<tag>/<asset>`), which is never populated by CI. This spec adds a `publish_release_assets` helper to `scripts/build_lib.sh` and two new steps to `build-linux.yaml` that mirror the hero_proc pattern. --- ### Requirements 1. A `publish_release_assets` function in `scripts/build_lib.sh` that creates (or retrieves existing) a Forgejo Release for the current tag and uploads each binary as a release asset. 2. The function must be idempotent: if a release already exists for the tag, it retrieves the ID rather than failing. 3. `build-linux.yaml` grows two new steps ("Create Release", "Upload Release Assets") placed before the existing "Publish" step. 4. The existing "Publish" step (package registry) remains unchanged — both publication paths coexist. 5. `jq` must be available in the CI container (added to the "Setup toolchain" step). 6. The existing "Publish" step must explicitly export `BIN_DIR`, `SERVER`, and `OWNER` — a latent bug where it relies on env vars that don't survive across steps. --- ### Files to Modify | File | Change | |---|---| | `.forgejo/workflows/build-linux.yaml` | Add `jq` to toolchain step; add "Create Release" and "Upload Release Assets" steps; fix "Publish" step env exports | | `scripts/build_lib.sh` | Add `publish_release_assets` function after `publish_binaries` | --- ### Implementation Plan #### Step 1: Add `publish_release_assets` to `build_lib.sh` File: `scripts/build_lib.sh` Insert after closing of `publish_binaries`. The function: - Validates required env vars: `BIN_DIR`, `BINARIES`, `FORGEJO_TOKEN`, `SERVER`, `OWNER`, `PROJECT_NAME` - POSTs to `/api/v1/repos/$OWNER/$PROJECT_NAME/releases` to create the release - Falls back to GET `/releases/tags/$TAG` if POST returns null id (already exists) - Iterates `$BINARIES`, uploads each as `<bin>-<suffix>` via POST to `/releases/$RELEASE_ID/assets?name=<artifact>` - Uses `Content-Type: application/x-pie-executable` (matches hero_proc pattern) Dependencies: none (parallel with Step 2) #### Step 2: Add `jq` to "Setup toolchain" step in `build-linux.yaml` File: `.forgejo/workflows/build-linux.yaml` Append `apt-get install -y jq` after the existing `setup_linux_toolchain` call. Dependencies: none (parallel with Step 1) #### Step 3: Add "Create Release" and "Upload Release Assets" steps to `build-linux.yaml` File: `.forgejo/workflows/build-linux.yaml` Insert between the "Build" step and the existing "Publish" step: **"Create Release" step** (inline YAML shell, outputs `release_id` via `$GITHUB_OUTPUT`): - Creates release via API using `github.ref_name`, `github.server_url`, `github.repository` - Falls back to GET by tag if already exists - Writes `release_id=<id>` to `$GITHUB_OUTPUT` **"Upload Release Assets" step** (calls `publish_release_assets` from `build_lib.sh`): - Sources `build_lib.sh` - Exports `BIN_DIR="$(bin_dir "${{ matrix.target }}")"`, `SERVER`, `OWNER` - Calls `publish_release_assets "${{ github.ref_name }}" "${{ matrix.artifact }}"` Dependencies: Steps 1 and 2 #### Step 4: Fix the existing "Publish" step env exports File: `.forgejo/workflows/build-linux.yaml` Add `export BIN_DIR="$(bin_dir "${{ matrix.target }}")"`, `SERVER`, `OWNER` to the existing "Publish" step before calling `publish_binaries`. Dependencies: Step 3 --- ### Acceptance Criteria - [ ] Pushing a `v*` tag produces a Forgejo Release at `/releases/tag/<tag>` with four assets: `hero_biz-linux-amd64`, `hero_biz_ui-linux-amd64`, `hero_biz-linux-arm64`, `hero_biz_ui-linux-arm64` - [ ] `svc_install_from_ci hero_biz` succeeds — downloads from `/releases/download/<tag>/<asset>` without 404 - [ ] Package registry upload continues to work (generic packages still present) - [ ] Re-running the workflow for the same tag does not fail (idempotent release creation) - [ ] `publish_release_assets` can be called locally with env vars set --- ### Notes - The matrix (x86_64 + aarch64) runs as two independent jobs. Each creates-or-retrieves the same release and uploads its own two assets. The idempotent fallback handles the race where both jobs attempt release creation simultaneously. - `buildenv.sh` declares `VERSION="0.1.0"` but `Cargo.toml` workspace has `version = "0.5.0"` — pre-existing mismatch tracked separately. The CI derives release tag from `github.ref_name`, not `VERSION`, so this does not block the fix. - This same pattern (`publish_release_assets` in `build_lib.sh`) would propagate to hero_books#118, hero_browser#16, and hero_foundry#26 once they pull the shared helper update. ---
Member

Resolved by PR #23 (merged 2026-05-04), which replaced publish_binaries with the inline three-step pattern (Create Release → Upload Release Assets → Upload to Package Registry) in build-linux.yaml, and switched asset naming to target triples (x86_64-unknown-linux-musl). Consumer side wired in hero_skills#204. The spec comment posted earlier today is superseded by this.

Resolved by PR #23 (merged 2026-05-04), which replaced `publish_binaries` with the inline three-step pattern (Create Release → Upload Release Assets → Upload to Package Registry) in `build-linux.yaml`, and switched asset naming to target triples (`x86_64-unknown-linux-musl`). Consumer side wired in hero_skills#204. The spec comment posted earlier today is superseded by this.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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
lhumina_code/hero_biz#13
No description provided.