Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions scripts/deploy-standard-workflows.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,27 +246,19 @@ batch_pr_body() {
deploy_repo() {
local repo="$1"

# Fully-exempt repos (no per-workflow opt-in) skip with a single log line.
if is_skipped_repo "$repo"; then
local opts_in_any=false w
for w in "${WORKFLOWS[@]}"; do
if repo_opts_into "$repo" "$w"; then opts_in_any=true; break; fi
done
if [[ "$opts_in_any" == "false" ]]; then skip "$repo (exempt)"; return; fi
fi
# NOTE: a skipped meta-repo (.github / .github-private) is NOT short-circuited
# here — the per-workflow loop below fetches each stub to tell a self-host `./`
# ref (stays exempt) from a channel-pinned consumer ref (re-pinned by the F5
# sweep, #704). Each exempt workflow still logs its own `(exempt)` line.

# Collect the drifted stubs for this repo (path/template pairs + names). For
# ring-managed reusables the deployed template is REWRITTEN to pin the repo's
# tier channel (major-scoped `v<M>-<tier>` when the agent has a release) — the
# emit ref (#657 F5). Rewritten templates land in temp files cleaned up below.
local -a paths=() templates=() names=() emits=() tmpfiles=()
local workflow template target_path raw existing_sha existing_content emit deploy_template base
local workflow template target_path raw existing_sha existing_content emit deploy_template base repin_source
for workflow in "${WORKFLOWS[@]}"; do
base=""
if is_skipped_repo "$repo" && ! repo_opts_into "$repo" "$workflow"; then
skip "$repo/$workflow (exempt)"
continue
fi
template="$STANDARDS_DIR/$workflow"
target_path=".github/workflows/$workflow"
if [[ ! -f "$template" ]]; then
Expand All @@ -276,16 +268,41 @@ deploy_repo() {
raw=$(fetch_existing "$repo" "$target_path")
existing_sha="${raw%%$'\t'*}"
existing_content="${raw#*$'\t'}"

# Meta-repo handling (#704). A skipped repo is exempt from blanket stub
# deployment because it self-hosts most reusables via local `./` refs — a
# channel-pinned template must never overwrite those. But a meta-repo also
# CONSUMES the reusables it does not host (e.g. .github consumes dev-lead from
# .github-private), and those channel-pinned consumer stubs must be re-pinned
# by the F5 sweep or the major-scope migration skips them (freezing the inner
# canary rings) and --retire-bare stays blocked. So process a skipped repo's
# workflow only when its existing stub is a ring-managed channel CONSUMER; a
# missing stub, a non-ring reusable, or a local `./` self-host ref stays exempt
# (opt-ins via SKIP_OVERRIDES keep their verbatim-template path unchanged).
repin_source="$template"
if is_skipped_repo "$repo" && ! repo_opts_into "$repo" "$workflow"; then
base="$(reusable_base_of "$template")"
if [[ -z "$existing_sha" ]] || [[ -z "$base" ]] || ! ring_is_ring_reusable "$base" \
|| ring_stub_selfhosts "$base" <<< "$existing_content"; then
skip "$repo/$workflow (exempt)"
continue
fi
# A channel consumer stub: re-pin the meta-repo's OWN stub body in place —
# never overwrite its bespoke content with the generic template.
repin_source="$(mktemp)"; tmpfiles+=("$repin_source")
printf '%s' "$existing_content" > "$repin_source"
fi

if [[ -n "$existing_sha" ]] && [[ "$FORCE" == "false" ]] && is_already_compliant "$existing_content" "$template" "$repo"; then
skip "$repo/$target_path already compliant"
continue
fi
emit="$(emit_ref_for "$template" "$repo")"
deploy_template="$template"
deploy_template="$repin_source"
if [[ -n "$emit" ]] && [[ "$DRY_RUN" != "true" ]]; then
base="$(reusable_base_of "$template")"
deploy_template="$(mktemp)"
ring_repin_uses "$base" "$emit" < "$template" > "$deploy_template"
ring_repin_uses "$base" "$emit" < "$repin_source" > "$deploy_template"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
tmpfiles+=("$deploy_template")
fi
paths+=("$target_path"); templates+=("$deploy_template"); names+=("$workflow"); emits+=("$emit")
Comment thread
don-petry marked this conversation as resolved.
Expand Down
14 changes: 14 additions & 0 deletions scripts/lib/ring-pins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,17 @@ ring_vform_tier_aligned() {
tier="$(ring_tier_for_repo "$repo")"
[[ "$ref" =~ ^${base}/v[0-9]+-${tier}$ ]]
}

# ring_stub_selfhosts <channel-base> -> 0 iff the workflow stub read on stdin
# references THIS agent's reusable via a LOCAL `./` self-host ref (e.g.
# `uses: ./.github/workflows/<base>-reusable.yml`) rather than a channel-pinned
# consumer ref (`uses: <org>/<repo>/.../<base>-reusable.yml@<base>/<tier>`).
#
# The meta-repos (.github / .github-private) HOST some reusables locally — their
# own stubs pin those via `./`, which the F5 re-pin sweep must never touch — while
# CONSUMING others by channel (e.g. .github consumes dev-lead from .github-private),
# which the sweep must re-pin like any other consumer (#704). Pure (grep only).
ring_stub_selfhosts() {
local base="$1"
grep -qE "^[[:space:]]*uses:[[:space:]]*\./[^@[:space:]]*/${base}-reusable\.yml([[:space:]]|\$)"
}
51 changes: 51 additions & 0 deletions test/scripts/deploy-standard-workflows/emit-vform.bats
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ stub_pinning() { # <ref> → base64 of a minimal auto-rebase stub pinning the r
base64 -w 0 <<<"$body" 2>/dev/null || base64 -b 0 <<<"$body"
}

devlead_stub_pinning() { # <ref> → base64 of a .github dev-lead CONSUMER stub pinned at <ref>
local body="jobs:
dev-lead:
uses: petry-projects/.github-private/.github/workflows/dev-lead-reusable.yml@$1
with:
agent_ref: $1
secrets: inherit"
base64 -w 0 <<<"$body" 2>/dev/null || base64 -b 0 <<<"$body"
}

selfhost_stub() { # <base> → base64 of a meta-repo SELF-HOST stub using a local ./ ref
local body="jobs:
$1:
uses: ./.github/workflows/$1-reusable.yml # local ref — always current
secrets: inherit"
base64 -w 0 <<<"$body" 2>/dev/null || base64 -b 0 <<<"$body"
}

@test "emits @<agent>/v<M>-<tier> when the reusable has a release" {
export GH_RELEASE_REFS="refs/tags/auto-rebase/v2.3.1
refs/tags/auto-rebase/v1.0.0"
Expand Down Expand Up @@ -93,3 +111,36 @@ refs/tags/auto-rebase/v1.0.0"
[ "$status" -eq 0 ]
echo "$output" | grep -qE 'Would open PR for TalkTerm .* auto-rebase.yml'
}

# ── meta-repo consumer stubs (#704) ────────────────────────────────────────────
# The meta-repos (.github / .github-private) are exempt from blanket stub
# deployment because they self-host most reusables via local `./` refs. But they
# ARE channel consumers of the reusables they do NOT host — e.g. .github (ring0)
# consumes dev-lead from .github-private — and those consumer stubs must be
# re-pinned by the F5 sweep like any other consumer, or the major-scope migration
# skips them and --retire-bare stays blocked.

@test "re-pins a meta-repo's channel-pinned CONSUMER stub to the tier v<M>-form" {
export GH_RELEASE_REFS="refs/tags/dev-lead/v3.2.0"
# .github's dev-lead stub is a consumer still on the bare tier channel.
GH_CONTENT_B64="$(devlead_stub_pinning dev-lead/ring0)"; export GH_CONTENT_B64
install_gh_stub
# --force so the bare-tier migration grace does not mark it already-compliant.
run env GH_TOKEN=x bash "$SCRIPT" --dry-run --force --repo .github --workflow dev-lead.yml
[ "$status" -eq 0 ]
# .github is a ring0 repo → v3 major line → @dev-lead/v3-ring0
echo "$output" | grep -qF '@dev-lead/v3-ring0'
echo "$output" | grep -qE 'Would open PR for \.github .* dev-lead.yml'
}

@test "leaves a meta-repo's local ./ SELF-HOST stub exempt (never re-pins it)" {
export GH_RELEASE_REFS="refs/tags/agent-shield/v2.0.0"
# .github HOSTS agent-shield's reusable — its stub uses a local ./ ref.
GH_CONTENT_B64="$(selfhost_stub agent-shield)"; export GH_CONTENT_B64
install_gh_stub
run env GH_TOKEN=x bash "$SCRIPT" --dry-run --force --repo .github --workflow agent-shield.yml
[ "$status" -eq 0 ]
echo "$output" | grep -qF '.github/agent-shield.yml (exempt)'
! echo "$output" | grep -qi 'Would open PR'
! echo "$output" | grep -qF '@agent-shield/v'
}
32 changes: 32 additions & 0 deletions test/scripts/lib/ring-pins.bats
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,35 @@ setup() {
# bare tier (no v<M>-) → not a v-form
! ring_vform_tier_aligned auto-rebase/ring1 auto-rebase TalkTerm
}

# ── meta-repo self-host vs channel-consumer discrimination (#704) ──────────────

@test "ring_stub_selfhosts: true for a local ./ self-host ref" {
# .github hosts agent-shield's reusable itself, so its own stub uses a local ref.
local stub="jobs:
agent-shield:
uses: ./.github/workflows/agent-shield-reusable.yml # local ref — always current
secrets: inherit"
run bash -c 'source "'"$REPO_ROOT"'/scripts/lib/ring-pins.sh"; ring_stub_selfhosts agent-shield <<<"$1"' _ "$stub"
[ "$status" -eq 0 ]
}

@test "ring_stub_selfhosts: false for a channel-pinned consumer ref" {
# .github does NOT host dev-lead (it lives in .github-private) — its stub is a
# channel consumer, so it must be re-pinned, not treated as self-host.
local stub="jobs:
dev-lead:
uses: petry-projects/.github-private/.github/workflows/dev-lead-reusable.yml@dev-lead/ring0
with:
agent_ref: dev-lead/ring0"
run bash -c 'source "'"$REPO_ROOT"'/scripts/lib/ring-pins.sh"; ring_stub_selfhosts dev-lead <<<"$1"' _ "$stub"
[ "$status" -ne 0 ]
}

@test "ring_stub_selfhosts: false when the stub does not reference the reusable at all" {
local stub="jobs:
something-else:
uses: ./.github/workflows/other-reusable.yml"
run bash -c 'source "'"$REPO_ROOT"'/scripts/lib/ring-pins.sh"; ring_stub_selfhosts dev-lead <<<"$1"' _ "$stub"
[ "$status" -ne 0 ]
}
Comment thread
don-petry marked this conversation as resolved.
Loading