feat(reusable): resolve the reviewer image tag at runtime (GADFLY_REVIEWER_TAG) #28

Merged
steve merged 1 commits from reviewer-tag-runtime-var into main 2026-07-27 03:32:45 +00:00
3 changed files with 40 additions and 13 deletions
+30 -11
View File
@@ -24,11 +24,14 @@
# if you accept that exposure; the explicit form is recommended. GITEA_TOKEN is
# the automatic job token (no need to forward it).
#
# Advisory only — never blocks a merge. The image is pinned to an immutable
# :sha- tag here (act_runner caches :latest); bump it per Gadfly release.
# Consumers should pin `uses: ...@v1` — a curated release tag moved on deliberate
# releases, so central tuning here propagates without per-consumer edits — or a
# full `@<sha>` for an immutable pin. Avoid `@main` (moves on every push).
# Advisory only — never blocks a merge. The reviewer image tag ALSO resolves at
# runtime (inputs.reviewer_tag → user var GADFLY_REVIEWER_TAG → the fallback pin
# baked into the `container:` line below), so a Gadfly release is: build the
# image, update ONE variable — no consumer re-pin. Re-pin the workflow ref only
# for structural changes to this file.
# Consumers should pin `uses: ...@<sha>` — long-lived act_runners cache this file
# by ref, so a moved tag (@v1) or @main is often NOT re-fetched and silently runs
# a stale copy.
name: Gadfly review (reusable)
@@ -43,6 +46,7 @@ on:
# NOT re-fetched; only a runtime value or a fresh @<sha> bypasses the cache).
#
# Owner-set user-scope variables (see README "Central config via variables"):
# GADFLY_REVIEWER_TAG (the reviewer image tag this reusable runs),
# GADFLY_DEFAULT_MODELS, GADFLY_DEFAULT_SPECIALISTS,
# GADFLY_DEFAULT_PROVIDER_LENS_CONCURRENCY (the provider-wide lens budget),
# GADFLY_ENDPOINT_NETHERSTORM (the local GPU box endpoint).
@@ -59,6 +63,7 @@ on:
max_steps: { type: string, default: "14" } # GADFLY_MAX_STEPS
worker_model: { type: string, default: "" } # GADFLY_WORKER_MODEL
allowed_users: { type: string, default: "" } # GADFLY_ALLOWED_USERS (consumer-specific; set in your stub)
reviewer_tag: { type: string, default: "" } # reviewer image tag (e.g. "sha-b37cd09") — empty falls back to user var GADFLY_REVIEWER_TAG, then the pin baked into the container: line
# Gitea >= 1.27 does not propagate the CALLER's workflow_dispatch inputs into a
# called workflow's github.event, so a manual "review PR #N" dispatch arrived
# here with an empty PR and died at the entrypoint's "PR required" check. The
@@ -98,13 +103,27 @@ jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: ${{ inputs.timeout_minutes }}
# The reviewer runs as the JOB container (steps exec inside it), not a
Review

🟡 Reviewer-tag runtime-resolution priority chain is explained in full prose twice in the same file

maintainability · flagged by 1 model

🪰 Gadfly · advisory

🟡 **Reviewer-tag runtime-resolution priority chain is explained in full prose twice in the same file** _maintainability · flagged by 1 model_ <sub>🪰 Gadfly · advisory</sub>
# `uses: docker://` step: a job container image accepts ${{ }} expressions,
# while a `uses:` ref is parsed before any context exists. That lets the tag
# resolve per-run — inputs.reviewer_tag → user var GADFLY_REVIEWER_TAG → the
# baked fallback — so bumping the reviewer image is a ONE-variable edit that
# reaches every consumer despite their cached workflow ref.
#
# Always point the variable at an immutable :sha-<short> tag, never :latest —
# act_runner caches :latest and often does NOT re-pull a moved one; a fresh
# unique tag forces the pull. NB: vars are editable at will — whoever can edit
# the owner's variables redirects every consumer's reviewer image (same blast
# radius as editing this file, but without a commit trail).
#
# Fallback pin: sha-b37cd09 — the provider-wide lens budget (PR #27) on top of
# the opencode CLI engine (PR #26) and the Gitea >= 1.27 workflow_call
# reclassification. Keep it current-ish when touching this file anyway.
container:
image: gitea.stevedudenhoeffer.com/steve/gadfly:${{ inputs.reviewer_tag || vars.GADFLY_REVIEWER_TAG || 'sha-b37cd09' }}
steps:
# Pin the reviewer image to an immutable sha (act_runner caches :latest, so a
# moved :latest is often NOT re-pulled). sha-b37cd09 adds the provider-wide
# lens budget (PR #27: one shared lens-permit pool per provider, the model cap
# removed) on top of the opencode CLI engine (PR #26) and the Gitea >= 1.27
# workflow_call reclassification. Bump per Gadfly release.
- uses: docker://gitea.stevedudenhoeffer.com/steve/gadfly:sha-b37cd09
- name: Run the gadfly reviewer
run: /entrypoint.sh
env:
# --- event context (from the CALLER's github.*) -------------------
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
+7
View File
@@ -401,6 +401,12 @@ pinned version (plus `:latest`). Pin full-stub consumers to a `:vN` image tag fo
(`@v1`) or `@main` is often **not** re-fetched and silently runs a stale copy. A fresh `@<sha>` is the
only reliable way to roll out a *structural* change to the reusable.
Structural changes are the rare case, though: the reviewer **image tag** the reusable runs resolves at
runtime (`reviewer_tag` input → user var `GADFLY_REVIEWER_TAG` → the fallback pin baked into the
reusable), so a routine Gadfly release is *build the image → update `GADFLY_REVIEWER_TAG` to the new
`sha-<short>`* — every pinned consumer picks it up on its next review, no re-pin. Always point the
variable at an immutable `sha-` tag, never `:latest` (the runner caches `:latest`).
### Central config via variables
So you don't have to re-pin every consumer just to retune the swarm, the reusable resolves its config
@@ -410,6 +416,7 @@ on its next review **without** a re-pin or a tag move:
| Variable (user/org scope) | Sets |
|---|---|
| `GADFLY_REVIEWER_TAG` | the reviewer **image tag** the reusable runs (e.g. `sha-b37cd09`); empty ⇒ the fallback pin baked into the reusable |
| `GADFLY_DEFAULT_MODELS` | `GADFLY_MODELS` (csv) |
| `GADFLY_DEFAULT_SPECIALISTS` | the lens suite |
| `GADFLY_DEFAULT_PROVIDER_LENS_CONCURRENCY` | the per-provider lens budget (lens passes in flight per provider, shared across its models) |
+3 -2
View File
@@ -17,8 +17,9 @@
# review never touches), so prefer the explicit form. Pin to an immutable
# @<sha>: long-lived act_runners CACHE the reusable by ref, so a moved tag (@v1)
# or @main is often not re-fetched and silently runs a stale copy. Bump the @<sha>
# to adopt a structural change; routine swarm tuning rides owner variables (see
# the gadfly README "Central config via variables") with no re-pin needed.
# only to adopt a structural change; routine swarm tuning AND reviewer image
# releases ride owner variables (GADFLY_DEFAULT_*, GADFLY_REVIEWER_TAG — see the
# gadfly README "Central config via variables") with no re-pin needed.
#
# For custom named endpoints (GADFLY_ENDPOINT_<NAME>) or a provider the reusable
# doesn't map, use the full stub in adversarial-review.yml instead.