Compare commits

..

3 commits

Author SHA1 Message Date
90cbfe0057 Merge pull request 'fix: rig's ceremony CI guards resolve on Forgejo' (#113) from build/112-ceremony-uses-absolute into main
Some checks are pending
ci / check (push) Waiting to run
ci / install (push) Waiting to run
ci / db-integration (push) Waiting to run
release / release (push) Waiting to run
Reviewed-on: #113
Reviewed-by: kimi-reviewer-andresmgsl <andres+4@heavyduty.builders>
Reviewed-by: grok-reviewer-andresmgsl <andres+3@heavyduty.builders>
Reviewed-by: codex-reviewer-andresmgsl <andres+2@heavyduty.builders>
2026-07-29 13:37:33 +00:00
cluade-reviewer-andresmgsl
54efc17246 docs: the changelog fragment says what changed and stops
Some checks failed
ci / check (pull_request) Has been cancelled
ci / install (pull_request) Has been cancelled
ci / db-integration (pull_request) Has been cancelled
labels / labels (pull_request) Has been cancelled
CONTRIBUTING's changelog rule is explicit — the fragment answers one question,
what is different in this version; why it was wrong and what it implies belong
in the PR body and the commit message, which is where anyone chasing the
reasoning already goes. The entry carried both.

Review: codex-reviewer-andresmgsl on !113.

Refs #112

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:22:08 +00:00
cluade-reviewer-andresmgsl
25f337494c fix: rig's ceremony CI guards resolve on Forgejo
Some checks failed
ci / check (pull_request) Has been cancelled
ci / install (pull_request) Has been cancelled
ci / db-integration (pull_request) Has been cancelled
labels / labels (pull_request) Has been cancelled
The six ceremony actions in ci.yml are step `uses:` references, and a bare
step reference resolves through FORGEJO__actions__DEFAULT_ACTIONS_URL — which
this forge points at code.forgejo.org, where heavy-duty/ceremony does not
exist. Measured with a registered runner on the instance: `git clone
https://code.forgejo.org/heavy-duty/ceremony` -> 404, so every guard fails the
moment rig's own CI runs on the forge. Those six become absolute.

The two reusable-workflow callers do NOT move, and this is the correction to
the original "make all eight absolute" ruling. A reusable `uses:` never
consults DEFAULT_ACTIONS_URL; it resolves against the runner's own instance,
so both callers already fetch ceremony from this forge and expand. Absolutising
release.yml's would actively break CI: ceremony's docs-sync reads rig's pin out
of that one line with a grep anchored to the bare spelling, so the prefix makes
the pin invisible and docs-sync exits 1 with "no pin line ... found none".

actions/checkout@v4 stays bare in all three places — the mirror serves it, and
that is exactly what DEFAULT_ACTIONS_URL is for. The pin stays @0.3.0
throughout; no .ceremony/ edit, no pin bump.

test/cli.sh pins all three shapes, including the docs-sync grep verbatim, so
ceremony's parser and rig's caller cannot drift apart silently.

Refs #112

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:10:38 +00:00
4 changed files with 81 additions and 21 deletions

View file

@ -50,26 +50,38 @@ jobs:
# lives with its implementation upstream; the six pins below and the
# two workflow callers must always name the same ceremony tag.
#
# These six are ABSOLUTE and the two callers in release.yml/labels.yml
# are BARE. That asymmetry is measured, not stylistic (#112) — do not
# "tidy" either half into the other:
# a step `uses:` resolves a bare name through DEFAULT_ACTIONS_URL,
# which this forge points at code.forgejo.org, where ceremony does not
# exist — bare 404s and every guard below fails;
# a reusable-workflow `uses:` never consults DEFAULT_ACTIONS_URL at
# all — it resolves against the runner's own instance, so bare already
# works there. And release.yml's caller is the line ceremony's own
# docs-sync greps for its pin, anchored to the bare spelling: make it
# absolute and the pin goes invisible and docs-sync exits 1.
#
# changelog-armed: the version-keyed arming rule (rig#66; the
# unconditional form rig#44 reverted — this is its correct return).
- uses: heavy-duty/ceremony/actions/changelog-armed@0.3.0
- uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/changelog-armed@0.3.0
# changelog-monotonic: no shipped heading deleted or duplicated
# (#98, box#122). Strict by default: an unresolvable base ref is red,
# never a quiet skip — hence the fetch-depth: 0 above.
- uses: heavy-duty/ceremony/actions/changelog-monotonic@0.3.0
- uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/changelog-monotonic@0.3.0
# changelog-assembled: a release's stamped section must exactly match
# the fragments it consumed. Vacuous on non-release PRs.
- uses: heavy-duty/ceremony/actions/changelog-assembled@0.3.0
- uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/changelog-assembled@0.3.0
# drill-recorded: a release version carries drills/<version>.md
# (rig's drill meaning: drills/README.md). Vacuous on -dev trees.
- uses: heavy-duty/ceremony/actions/drill-recorded@0.3.0
- uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/drill-recorded@0.3.0
# runner-isolated: PR-triggered workflows never execute unreviewed
# branch code on a self-hosted runner.
- uses: heavy-duty/ceremony/actions/runner-isolated@0.3.0
- uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/runner-isolated@0.3.0
# docs-sync: the .ceremony/ doctrine mirror is byte-identical to the
# pin read from release.yml (ceremony#19) — a hand edit or a
# half-done pin bump goes red here.
- uses: heavy-duty/ceremony/actions/docs-sync@0.3.0
- uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/docs-sync@0.3.0
# The install LIFECYCLE against a tree install.sh itself produced — the four
# beats box and cast already run in CI (#106): install from this checkout,

View file

@ -1179,31 +1179,32 @@ FORGEJO__actions__ENABLED=true
FORGEJO__actions__DEFAULT_ACTIONS_URL=https://code.forgejo.org
```
> **`DEFAULT_ACTIONS_URL` is a single fallback, and rig's own workflows need
> two origins.** It decides where a *bare* `uses: owner/repo@ref` resolves.
> Measured:
> **`DEFAULT_ACTIONS_URL` is a single fallback, and it only governs *steps*.**
> It decides where a bare `uses: owner/repo@ref` on a **step** resolves — and
> nothing else. Measured on this instance, with a registered runner (#112):
>
> | reference | count in `.github/workflows/` | `code.forgejo.org` |
> |---|---|---|
> | `actions/checkout@v4` | 3 | **200** — mirrored |
> | `heavy-duty/ceremony/...@0.3.0` | 8 | **404** — lives on this Forgejo instead |
> | reference | count | bare resolves to | verdict |
> |---|---|---|---|
> | `actions/checkout@v4` (step) | 3 | `code.forgejo.org` | **200** — mirrored, keep bare |
> | `heavy-duty/ceremony/actions/…` (step) | 6 | `code.forgejo.org` | **404** — must be absolute |
> | `heavy-duty/ceremony/.github/workflows/…` (reusable) | 2 | **this instance** | **200** — keep bare |
>
> So with the value above, every ceremony guard (`changelog-armed`,
> `changelog-monotonic`, `changelog-assembled`, `drill-recorded`,
> `runner-isolated`, `docs-sync`, and the release/labels callers) fails to
> resolve the moment rig's CI actually runs on the forge.
> The last row is the one that surprises: a **reusable-workflow** `uses:` never
> consults `DEFAULT_ACTIONS_URL` at all. It resolves against the runner's own
> instance, so the `release.yml` and `labels.yml` callers already work bare.
>
> Forgejo accepts an absolute URL per step, so one side has to be explicit.
> **The ruling is to keep the value above**`actions/*` are exactly what
> `DEFAULT_ACTIONS_URL` is designed to resolve, and `code.forgejo.org` mirrors
> them — and to make the eight first-party references absolute:
> them — and to make only the **six** first-party *step* references absolute:
>
> ```yaml
> - uses: https://forgejo.heavyduty.builders/heavy-duty/ceremony/actions/docs-sync@0.3.0
> ```
>
> That change is tracked in forgejo#112, not made here. Until it lands,
> **rig's own CI is not expected to run on Forgejo**.
> The two reusable callers **stay bare**, and `release.yml`'s must: ceremony's
> `docs-sync` reads rig's pin out of that line with a grep anchored to the bare
> spelling, so absolutising it hides the pin and the guard exits 1. `test/cli.sh`
> pins all three shapes.
> `rig forgejo-runner` exists to run *your repositories'* workflows and does not
> depend on any of it — the value above is correct for those from the start.

3
changelog.d/112.md Normal file
View file

@ -0,0 +1,3 @@
### Fixed
- rig's six ceremony CI guards resolve on Forgejo (#112)

View file

@ -3516,6 +3516,50 @@ check "rig runner: still requires --repo, unchanged" 2 "--repo <owner/repo> is r
check "rig runner: still speaks github.com" 0 "https://github.com/" \
grep -o "https://github.com/\${repo}" "$ROOT/commands/lib/runner-config.sh"
# --- ceremony uses: resolution on this forge (#112) --------------------------
# The six ci.yml guards resolve ABSOLUTELY; the two reusable callers stay BARE.
# The two halves pull opposite ways and each was measured on the instance:
#
# guards — a step `uses:` resolves a bare name through DEFAULT_ACTIONS_URL
# (code.forgejo.org here), where heavy-duty/ceremony does not
# exist: a real run 404'd on `git clone`, so every guard failed.
# callers — a reusable-workflow `uses:` never consults DEFAULT_ACTIONS_URL;
# it resolves against the runner's own instance, so bare already
# fetches ceremony from this forge and expands.
#
# release.yml's caller additionally MUST stay bare: ceremony's docs-sync reads
# rig's pin out of that one line with an anchored grep, and an absolute prefix
# makes the pin invisible (docs-sync exits 1, `no pin line ... found none`).
# The pin regex below is that grep verbatim — if ceremony's parser and this
# file ever disagree, this is the test that says so.
wf_count() { printf 'count=%s\n' "$(grep -cE "$2" "$ROOT/.github/workflows/$1" || true)"; }
check "ci.yml: six ceremony guards, absolute at 0.3.0" 0 "count=6" \
wf_count ci.yml '^[[:space:]]*- uses: https://forgejo\.heavyduty\.builders/heavy-duty/ceremony/actions/[a-z-]+@0\.3\.0$'
check "ci.yml: no bare ceremony action survives (they 404 via DEFAULT_ACTIONS_URL)" 0 "count=0" \
wf_count ci.yml '^[[:space:]]*- uses: heavy-duty/ceremony/'
check "ci.yml: the three actions/checkout stay bare (the mirror resolves them)" 0 "count=3" \
wf_count ci.yml '^[[:space:]]*- uses: actions/checkout@v4$'
# The docs-sync pin grep, verbatim from ceremony 0.3.0
# actions/docs-sync/docs-sync.sh — exactly one line must match, or the guard
# dies with "expected exactly one" / "found none".
check "release.yml: the pin is still visible to ceremony's docs-sync grep" 0 "count=1" \
wf_count release.yml '^[[:space:]]*(-[[:space:]]*)?uses:[[:space:]]*heavy-duty/ceremony/\.github/workflows/release\.yml@'
check "labels.yml: the reusable caller stays bare too" 0 "count=1" \
wf_count labels.yml '^[[:space:]]*uses:[[:space:]]*heavy-duty/ceremony/\.github/workflows/labels\.yml@0\.3\.0$'
check "release.yml: the caller was not absolutised (docs-sync would go red)" 0 "count=0" \
wf_count release.yml '^[[:space:]]*uses:[[:space:]]*https://.*ceremony/\.github/workflows/'
check "labels.yml: the caller was not absolutised either" 0 "count=0" \
wf_count labels.yml '^[[:space:]]*uses:[[:space:]]*https://.*ceremony/\.github/workflows/'
# One tag governs all eight references — the pin may be bumped, never split.
ceremony_tags() {
grep -rhoE 'heavy-duty/ceremony/[^@]+@[^[:space:]]+' "$ROOT/.github/workflows/" \
| sed -E 's/.*@//' | sort -u | tr '\n' ' '
}
check "all eight ceremony references still name one tag" 0 "0.3.0" ceremony_tags
echo "---"
echo "$PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]