Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
60 changes: 60 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Keep every action pin current — including the shared osv-scan lane's.
#
# DESTINATION: .github/dependabot.yml — NOT .github/workflows/. This is repo
# configuration, not a workflow.
#
# WHY DEPENDABOT AND NOT A BESPOKE RE-PIN BOT (ci-workflows#10)
# -------------------------------------------------------------
# A caller's `uses: ...@<sha>` resolves the reusable workflow AT THAT COMMIT, so
# a stale pin means the repo runs an old scanner with old rules. The 2026-08-03
# census (caller-pins.yml) measured 43 of 58 callers behind the template, and
# nothing makes a stale caller go red on its own.
#
# The fix decomposes the way OpenTofu does: the template is the DESIRED STATE,
# the caller-pins census is the PLAN (an ancestry diff of actual vs desired),
# and something has to be the APPLY. Dependabot's `github-actions` ecosystem is
# that applier, chosen over a broker-credentialed bot because:
#
# - the engine is GitHub-maintained — nothing bespoke to keep working;
# - no new credential: a custom actor would need org-wide contents:write PLUS
# the `workflows` permission (GitHub rejects workflow-file pushes without
# it), minted from a broker entry that would make ci-workflows' main able to
# reach every repo's contents — a posture change SHA pins exist to avoid;
# - it covers consumers the census cannot see: repos that call the lane from
# inside a combined workflow (infra's _infra-test.yml) rather than a
# byte-identical deps.yml, and every OTHER action pin (checkout,
# setup-node, setup-deno) going stale the same way.
#
# Dependabot converges callers onto the head of each action's default branch,
# which is what the template pin IS at bump time. The census stays on as the
# independent verifier that convergence actually happened — Dependabot proposes,
# a human merges, the Monday census proves. Once the fleet reads current, flip
# caller-pins' `fail-on-lag` to true and the loop is closed: plan must be empty.
#
# ADOPTING THIS FILE
# ------------------
# Repo has no .github/dependabot.yml: copy this file byte-identical.
# Repo already has one (other ecosystems — npm, cargo, pip): APPEND the single
# `github-actions` entry below to its `updates:` list instead. Byte-identity is
# the norm, not a law; a merged config is correct, a clobbered one is not.
#
# The `ci` commit-message prefix is load-bearing: semantic-PR gates in this org
# reject `deps:` (claude-box did, during the rollout), and `ci:` is the accurate
# conventional-commit type for CI configuration.
#
# The single `actions` group collapses each week's bumps into ONE PR per repo —
# 58 repos on a weekly schedule is otherwise a noise machine nobody reads,
# which is the required-baseline.yml failure with extra steps.
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
groups:
actions:
patterns:
- "*"
commit-message:
prefix: "ci"
39 changes: 26 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,19 +200,32 @@ The same shape, with one step that exists because skipping it is what

1. Edit `.github/workflows/osv-scan.yml`
2. Merge, and re-pin `templates/deps.yml` to the merge commit
3. **Re-pin every caller** — dispatch `caller-pins.yml` to get the list

Step 3 is not bookkeeping. `uses: …@<sha>` resolves the reusable workflow *at that
commit*, so a caller's pin decides which scanner that repo actually runs: a lane
improvement that stops at step 2 is merged and deployed to nobody. And unlike the
canonical-script gate above, **nothing makes a stale caller go red on its own** — it
keeps scanning happily under the old rules — which is why this one has to be run rather
than waited for.

`caller-pins.yml` compares by **ancestry** (`git merge-base --is-ancestor`), not
existence. `self-test`'s `template-pins` job already proves pins name real commits, and
that is a different question: a stale pin *is* a real commit. `62990dd` resolves
perfectly and is four commits behind.
3. **Let the callers converge, then verify.** Dependabot (`templates/dependabot.yml`,
vendored per repo as `.github/dependabot.yml`) opens each caller's re-pin PR on its
weekly cycle; a human merges. The Monday `caller-pins.yml` census proves the fleet
actually converged.

Step 3 matters because `uses: …@<sha>` resolves the reusable workflow *at that commit*:
a caller's pin decides which scanner that repo actually runs, a lane improvement that
stops at step 2 is merged and deployed to nobody, and — unlike the canonical-script
gate above — **nothing makes a stale caller go red on its own.** The 2026-08-03 census
measured 43 of 58 callers behind for exactly this reason (ci-workflows#10).

The division of labour is deliberate, and it is the OpenTofu shape: the template is the
**desired state**, the census is the **plan** (an ancestry diff — `merge-base
--is-ancestor` — because a stale pin *is* a real commit, so existence checks cannot see
it; `62990dd` resolves perfectly and is four behind), Dependabot is the **apply**, and
merging stays human. Once the fleet reads current, flip `caller-pins`' `fail-on-lag`
to `true`: from then on the plan must be empty, and a caller that lags a week reds the
Monday lane instead of drifting silently.

Why Dependabot rather than a broker-credentialed re-pin bot: the engine is
GitHub-maintained (nothing bespoke to keep working); a custom actor would need
org-wide `contents:write` *plus* the `workflows` permission — GitHub rejects
workflow-file pushes without it — minted from an entry that would make this repo's
`main` a lever over every repo's contents; and Dependabot also covers what the census
cannot see: consumers that call the lane from inside a combined workflow (infra's
`_infra-test.yml`) and every other action pin going stale the same way.

**Two copies, currently in step.** `infra` and `front-desk-scheduler` both carry
`c530b86a…`, byte-identical to canonical as of adoption — so this gate was introduced
Expand Down
60 changes: 60 additions & 0 deletions templates/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Keep every action pin current — including the shared osv-scan lane's.
#
# DESTINATION: .github/dependabot.yml — NOT .github/workflows/. This is repo
# configuration, not a workflow.
#
# WHY DEPENDABOT AND NOT A BESPOKE RE-PIN BOT (ci-workflows#10)
# -------------------------------------------------------------
# A caller's `uses: ...@<sha>` resolves the reusable workflow AT THAT COMMIT, so
# a stale pin means the repo runs an old scanner with old rules. The 2026-08-03
# census (caller-pins.yml) measured 43 of 58 callers behind the template, and
# nothing makes a stale caller go red on its own.
#
# The fix decomposes the way OpenTofu does: the template is the DESIRED STATE,
# the caller-pins census is the PLAN (an ancestry diff of actual vs desired),
# and something has to be the APPLY. Dependabot's `github-actions` ecosystem is
# that applier, chosen over a broker-credentialed bot because:
#
# - the engine is GitHub-maintained — nothing bespoke to keep working;
# - no new credential: a custom actor would need org-wide contents:write PLUS
# the `workflows` permission (GitHub rejects workflow-file pushes without
# it), minted from a broker entry that would make ci-workflows' main able to
# reach every repo's contents — a posture change SHA pins exist to avoid;
# - it covers consumers the census cannot see: repos that call the lane from
# inside a combined workflow (infra's _infra-test.yml) rather than a
# byte-identical deps.yml, and every OTHER action pin (checkout,
# setup-node, setup-deno) going stale the same way.
#
# Dependabot converges callers onto the head of each action's default branch,
# which is what the template pin IS at bump time. The census stays on as the
# independent verifier that convergence actually happened — Dependabot proposes,
# a human merges, the Monday census proves. Once the fleet reads current, flip
# caller-pins' `fail-on-lag` to true and the loop is closed: plan must be empty.
#
# ADOPTING THIS FILE
# ------------------
# Repo has no .github/dependabot.yml: copy this file byte-identical.
# Repo already has one (other ecosystems — npm, cargo, pip): APPEND the single
# `github-actions` entry below to its `updates:` list instead. Byte-identity is
# the norm, not a law; a merged config is correct, a clobbered one is not.
#
# The `ci` commit-message prefix is load-bearing: semantic-PR gates in this org
# reject `deps:` (claude-box did, during the rollout), and `ci:` is the accurate
# conventional-commit type for CI configuration.
#
# The single `actions` group collapses each week's bumps into ONE PR per repo —
# 58 repos on a weekly schedule is otherwise a noise machine nobody reads,
# which is the required-baseline.yml failure with extra steps.
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
groups:
actions:
patterns:
- "*"
commit-message:
prefix: "ci"
Loading