fix: rig's ceremony CI guards resolve on Forgejo
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>
This commit is contained in:
parent
e7471edc41
commit
25f337494c
4 changed files with 81 additions and 21 deletions
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
|
|
@ -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,
|
||||
|
|
|
|||
31
README.md
31
README.md
|
|
@ -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
3
changelog.d/112.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
### Fixed
|
||||
|
||||
- rig's six ceremony CI guards resolve on Forgejo — bare names 404'd through `DEFAULT_ACTIONS_URL`; the two reusable callers stay bare (#112)
|
||||
44
test/cli.sh
44
test/cli.sh
|
|
@ -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 ]
|
||||
|
|
|
|||
Loading…
Reference in a new issue