fix(release): inline changed-image detection, dropping the reusable workflow #101

Closed
ryangr0 wants to merge 1 commit from fix/inline-changed-images into main
Owner
No description provided.
fix(release): inline changed-image detection, dropping the reusable workflow
All checks were successful
[Workflow] On Source Change / Guard: per-image .releaserc.cjs present (push) Successful in 2s
[Workflow] On Source Change / Determine changed images (push) Successful in 2s
[Workflow] On Source Change / Resolve release matrix (push) Successful in 1s
a4caa1d000
Run 267 wedged. `Determine Changed Directories` (the reusable's inner job) succeeded; `Determine
changed images` (the caller) never left `blocked`, so nothing downstream could read its outputs and
three jobs rendered as "(incomplete matrix)". Nothing was running anywhere on the instance — it did
not fail, it stalled. And because `concurrency: group: push-<ref>` has no cancel-in-progress, a
wedged run on main blocks every later push to main until a human clears it in the UI.

That is the SECOND way Forgejo's reusable-workflow handling has broken this pipeline. The first is
already written into this file: v15 flattens an expanded reusable's inner jobs into the caller's
graph, so a caller job id equal to the inner job id produces a plan with no dependency-free job and
the workflow is rejected outright. Same construct, two unrelated failure modes, and the second one
has no in-repo recovery.

The work it was doing is a `git diff` and some string handling. It needs neither a cross-repo
workflow, nor a flattened job graph, nor tj-actions/changed-files — a full action clone act pays for
on every run. So it is inlined and the fragile construct is gone rather than worked around.

Semantics preserved exactly, including the two that have already cost releases:
  - dirname FIRST (matching tj-actions `dir_names: true`), THEN truncate to MAX_LEVEL segments.
    The other order emits FILE paths once MAX_LEVEL >= 2; verified both orders against a synthetic
    tree.
  - MAX_LEVEL 1 keeps every entry an IMMEDIATE child of ops/docker. Run 166 is why: at level 2,
    ops/docker/semantic-release-rust/toolchain/package-lock.json became its own release train named
    `toolchain`, had no .releaserc.cjs, and cut a ROOT v2.2.0 tag. Reproduced that exact behaviour
    at MAX_LEVEL=2 to prove the truncation is faithful, not merely similar.
  - output written with the heredoc form, because $GITHUB_OUTPUT is line-oriented and this value is
    JSON (run 253).

guard-releaserc greps for the pin, so it now tracks MAX_LEVEL rather than the reusable's max-level
input. Without that update the guard would fail on this very commit.

VERIFIED BEFORE SHIPPING, running the extracted step script in a throwaway clone checked out at each
real commit — not reasoned about:
    #88  -> [ci-runner]     #90  -> []
    #99  -> []              #100 -> [cve-gate]
each matching what CI actually did on those commits, with the emitted $GITHUB_OUTPUT re-parsed
through the runner's own key=value / key<<DELIM rules.

An earlier attempt at this verification ran `git checkout <sha> -- .` in the live working tree,
which clobbered the uncommitted edit and staged files from old commits. Nothing pushed was lost;
the harness now clones first and never touches the working tree.
ryangr0 closed this pull request 2026-08-08 05:08:09 +00:00
All checks were successful
[Workflow] On Source Change / Guard: per-image .releaserc.cjs present (push) Successful in 2s
[Workflow] On Source Change / Determine changed images (push) Successful in 2s
[Workflow] On Source Change / Resolve release matrix (push) Successful in 1s

Pull request closed

Sign in to join this conversation.
No reviewers
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
webgrip/infrastructure!101
No description provided.