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
131 changes: 66 additions & 65 deletions .github/workflows/upgrade-check.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
# Reusable consumer upgrade radar — things a consuming repo owns that its
# updater doesn't cover here, each in its own reviewed PR:
# flutter-sdk — the Flutter SDK pin (.fvmrc + example/.fvmrc) → latest stable.
# lockfiles — pubspec.lock files refreshed to latest in-range.
# composite-refs — the dependabot-core#6704 blind spot (opt-in): every
# whuppi/ci ref (workflow AND composite, kept uniform so the
# pin never splits) → latest release, plus third-party
# actions pinned inside composite action.yml → latest SHA.
# flutter-sdk — the Flutter SDK pin (.fvmrc + example/.fvmrc) → latest stable.
# lockfiles — pubspec.lock files refreshed to latest in-range.
# action-refs — ONE owner for EVERY `uses:` action ref (opt-in): whuppi/ci
# refs across .github (workflows AND composites) → latest
# release, kept uniform so the pin never splits; and every
# third-party action (workflows AND composites alike) → latest
# SHA via pinact.
#
# Dependabot still owns pub deps and third-party actions in workflow FILES; the
# composite-refs job owns only what Dependabot can't see (dependabot never reads
# `uses:` inside composite action.yml, and a whuppi/ci group bump splits the pin
# across workflow-vs-composite). A consumer opts in with `sweepActions: true`
# and passes CI_ACTIONS_TOKEN — a Workflows-scope PAT, because GITHUB_TOKEN
# can't write .github/workflows/ — and drops `whuppi/ci*` from its Dependabot
# github-actions group.
# Why one owner: Dependabot never reads `uses:` inside composite action.yml
# (dependabot-core#6704), so splitting action updates between Dependabot
# (workflows) and a sweep (composites) is the ugly half-and-half we refuse. The
# action-refs job owns ALL action refs — workflow and composite, whuppi/ci and
# third-party — in one PR. Dependabot is left owning ONLY pub deps (its one
# competency nothing else here covers). A consumer opts in with
# `sweepActions: true` + CI_ACTIONS_TOKEN (a Workflows-scope PAT — GITHUB_TOKEN
# can't write .github/workflows/) and drops the whole `github-actions` ecosystem
# from its Dependabot config.
#
# The shared tool/binary pins (fvm, Chrome, bore, zizmor, actionlint, pinact)
# reach consumers through a whuppi/ci release + the composite-refs whuppi/ci
# bump; whuppi/ci owns their versions via self-upgrade.yml.
# reach consumers through a whuppi/ci release + the action-refs whuppi/ci bump;
# whuppi/ci owns their versions via self-upgrade.yml.
name: Upgrade Check

on:
Expand All @@ -32,14 +35,14 @@ on:
required: false
default: 'dev'
sweepActions:
# Opt in to the composite-refs job. Off by default so a consumer that
# Opt in to the action-refs job. Off by default so a consumer that
# hasn't set up CI_ACTIONS_TOKEN keeps its old Dependabot-only shape.
type: boolean
required: false
default: false
secrets:
CI_ACTIONS_TOKEN:
# Workflows-scope PAT for the composite-refs job's push (GITHUB_TOKEN
# Workflows-scope PAT for the action-refs job's push (GITHUB_TOKEN
# can't write .github/workflows/). Only needed when sweepActions: true.
required: false

Expand Down Expand Up @@ -230,21 +233,19 @@ jobs:
gh pr edit "$EXISTING_PR" --title "chore: refresh lockfiles" --body "$PR_BODY"
fi

# ── Composite + workflow action refs (the dependabot-core#6704 gap) ──
# Opt-in (sweepActions: true). Two bumps Dependabot can't reach:
# 1. every whuppi/ci ref across .github — workflows AND composite
# action.yml — swept to the latest release, so the pin never splits
# (a Dependabot group bump moves only the workflow refs, and
# pin-availability then rejects the split);
# 2. third-party actions pinned inside composite action.yml (e.g.
# actions/cache frozen at an old SHA) → latest SHA, via pinact.
# Dependabot keeps pub + third-party actions in workflow FILES; this owns only
# the composite blind spot + the uniform whuppi/ci bump. The push needs
# CI_ACTIONS_TOKEN (Workflows-scope PAT) — GITHUB_TOKEN can't write
# .github/workflows/. github.token (contents:read) still authenticates
# pinact's SHA-resolving API reads.
composite-refs:
name: Composite action refs
# ── Action refs — ONE owner for every `uses:` ref (the #6704 fix) ──
# Opt-in (sweepActions: true). Bumps EVERY action ref in .github, workflow and
# composite alike, in one PR — no split between Dependabot and a sweep:
# 1. every whuppi/ci ref (workflows AND composite action.yml) → latest
# release, swept uniform so the pin never splits;
# 2. every third-party action (workflows AND composites) → latest SHA, via
# pinact (which reads composite action.yml, the dependabot-core#6704 blind
# spot).
# This leaves Dependabot owning ONLY pub deps. The push needs CI_ACTIONS_TOKEN
# (Workflows-scope PAT) — GITHUB_TOKEN can't write .github/workflows/.
# github.token (contents:read) still authenticates pinact's SHA-resolving reads.
action-refs:
name: Action refs
if: ${{ inputs.sweepActions }}
runs-on: ${{ inputs.runner }}
timeout-minutes: 15
Expand Down Expand Up @@ -278,14 +279,14 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
gh auth setup-git
gh label create upgrade-ci-refs --description "Auto: whuppi/ci + composite action ref bump" --color "1d76db" 2>/dev/null || true
EXISTING_PR=$(gh pr list --label upgrade-ci-refs --state open --json number --jq '.[0].number // empty')
gh label create upgrade-action-refs --description "Auto: every action uses: ref bump (workflows + composites)" --color "1d76db" 2>/dev/null || true
EXISTING_PR=$(gh pr list --label upgrade-action-refs --state open --json number --jq '.[0].number // empty')
echo "existing_pr=$EXISTING_PR" >> "$GITHUB_OUTPUT"
if [ -n "$EXISTING_PR" ]; then
git fetch origin chore/composite-refs 2>/dev/null || true
git checkout -B chore/composite-refs origin/chore/composite-refs 2>/dev/null || git checkout -b chore/composite-refs
git fetch origin chore/action-refs 2>/dev/null || true
git checkout -B chore/action-refs origin/chore/action-refs 2>/dev/null || git checkout -b chore/action-refs
else
git checkout -b chore/composite-refs
git checkout -b chore/action-refs
fi

- name: Sweep every whuppi/ci ref to the latest release
Expand Down Expand Up @@ -325,7 +326,7 @@ jobs:
echo "SWEPT_EOF"
} >> "$GITHUB_OUTPUT"

- name: Pin third-party actions inside composites (pinact)
- name: Pin every third-party action to the latest SHA (pinact)
id: pinact
env:
# Read-only API for resolving tag → SHA. The write is the push below.
Expand All @@ -336,22 +337,22 @@ jobs:
source .whuppi-ci/tool/versions.env
go install "github.com/suzuki-shunsuke/pinact/v4/cmd/pinact@v$PINACT_VERSION"
PATH="$(go env GOPATH)/bin:$PATH"
# Composite action.yml ONLYDependabot owns third-party actions in
# workflow files, so pinact stays out of .github/workflows here to
# avoid double-bumping. whuppi/ci refs are excluded (the sweep above
# keeps them as version tags, not SHAs).
comp=()
while IFS= read -r f; do comp+=("$f"); done \
< <(find .github/actions \( -name action.yml -o -name action.yaml \) 2>/dev/null || true)
if [ ${#comp[@]} -eq 0 ]; then
echo "No composite action.yml — nothing to pin."
# ALL action filesworkflows AND composite action.yml. One owner for
# every third-party ref, no split with Dependabot (which is left owning
# only pub). whuppi/ci refs are excluded the sweep above keeps them as
# version tags, not SHAs.
targets=()
while IFS= read -r f; do targets+=("$f"); done \
< <(find .github/workflows .github/actions \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null || true)
if [ ${#targets[@]} -eq 0 ]; then
echo "No workflow/composite files — nothing to pin."
else
pinact run --update --exclude '^whuppi/ci/' "${comp[@]}"
pinact run --update --exclude '^whuppi/ci/' "${targets[@]}"
fi
# Stage any composite that pinact rewrote.
# Stage any file pinact rewrote (workflows or composites).
changed=()
while IFS= read -r f; do changed+=("$f"); done \
< <(git diff --name-only -- '.github/actions/**/action.yml' '.github/actions/**/action.yaml' 2>/dev/null || true)
< <(git diff --name-only -- '.github/workflows' '.github/actions' 2>/dev/null || true)
{
echo "files<<PINNED_EOF"
printf '%s\n' "${changed[@]}"
Expand All @@ -371,28 +372,28 @@ jobs:
printf '%s\n%s\n' "$SWEPT_FILES" "$PINNED_FILES" | while IFS= read -r f; do
[ -n "$f" ] && git add -- "$f"
done
git commit -m "ci: bump whuppi/ci + composite action refs" || {
git commit -m "ci: bump all action refs (workflows + composites)" || {
echo "Refs already current — nothing to bump."
exit 0
}
if [ -n "$EXISTING_PR" ]; then
git push origin chore/composite-refs
git push origin chore/action-refs
else
git push --force origin chore/composite-refs
git push --force origin chore/action-refs
fi

PR_BODY="$(cat <<'EOF'
Bumps the refs Dependabot can't reach inside composite `action.yml`
(dependabot/dependabot-core#6704):
One owner for every `uses:` action ref in `.github` — workflow and
composite alike, no split with Dependabot:

- **whuppi/ci refs** across `.github` workflows AND vendored composite
actions — swept uniform to the latest whuppi/ci release, so the pin
never splits (a Dependabot group bump moves only the workflow refs,
and pin-availability rejects the split).
- **Third-party actions inside composites** (e.g. `actions/cache`)
pinned to the latest SHA via pinact.
- **whuppi/ci refs** across `.github` (workflows AND composite
`action.yml`) swept uniform to the latest whuppi/ci release, so the
pin never splits.
- **Every third-party action** (workflows AND composites) pinned to the
latest SHA via pinact — including the refs inside composite
`action.yml` that Dependabot can't read (dependabot-core#6704).

Dependabot still owns pub deps and third-party actions in workflow files.
Dependabot owns pub deps only.

Add `ready-to-test` for the full cross-target run, then merge once green.

Expand All @@ -401,8 +402,8 @@ jobs:
)"

if [ -z "$EXISTING_PR" ]; then
gh pr create --base "$BASE" --head chore/composite-refs \
--title "ci: bump whuppi/ci + composite action refs" --label upgrade-ci-refs --draft --body "$PR_BODY"
gh pr create --base "$BASE" --head chore/action-refs \
--title "ci: bump all action refs (workflows + composites)" --label upgrade-action-refs --draft --body "$PR_BODY"
else
gh pr edit "$EXISTING_PR" --title "ci: bump whuppi/ci + composite action refs" --body "$PR_BODY"
gh pr edit "$EXISTING_PR" --title "ci: bump all action refs (workflows + composites)" --body "$PR_BODY"
fi
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ Releases are cut from the top heading here by `self-release.yml`; consumers pin
an exact version and upgrade through grouped Dependabot PRs. Versioning rules
live in the README. Newest first.

## 2.4.0

- The opt-in composite sweep now owns **every** action `uses:` ref, not just the
composite blind spot — renamed `composite-refs` → `action-refs`. It sweeps
every whuppi/ci ref (workflows + composites, uniform) to the latest release AND
pins every third-party action (workflows AND composites) to the latest SHA via
`pinact`. No more split between Dependabot and a sweep across workflow-vs-composite.
- This leaves Dependabot owning **only pub deps**. Consumers drop the whole
`github-actions` ecosystem from `dependabot.yml` (not just `whuppi/ci*`), keep
`sweepActions: true` + `CI_ACTIONS_TOKEN`, and get one PR for all action bumps.
- The label/branch changed with the rename (`upgrade-action-refs` /
`chore/action-refs`); a consumer on 2.3.0's short-lived `composite-refs` shape
just re-points its wrapper.

## 2.3.0

- Reverted Renovate (added in 2.2.0). Deleted the reusable `renovate.yml`. The
Expand Down
29 changes: 15 additions & 14 deletions docs/UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,26 @@ consumer onboarding.
| Thing | Bumped by | You do |
|---|---|---|
| Tool/binary pins (`tool/versions.env`) | `self-upgrade.yml` daily PR | Review the hashes, merge, cut a release |
| Consumer pub deps + third-party actions in **workflow files** | consumer's **Dependabot** (ignoring `whuppi/ci*`) | Review + merge its grouped PRs |
| Every **whuppi/ci ref** (workflow + composite, uniform) + third-party actions **inside composites** | the `composite-refs` job in the reusable `upgrade-check.yml` (opt-in) | Review + merge the one bump PR it opens |
| Consumer **pub deps** (root + example) | consumer's **Dependabot** (pub ecosystem only) | Review + merge its grouped PRs |
| **Every action `uses:` ref** — whuppi/ci + third-party, workflows AND composites | the `action-refs` job in the reusable `upgrade-check.yml` (opt-in) | Review + merge the one bump PR it opens |
| Consumers' Flutter SDK + lockfiles | reusable `upgrade-check.yml` | Nothing here |

**The composite blind spot (`composite-refs`).** Dependabot's github-actions
**One owner for every action ref (`action-refs`).** Dependabot's github-actions
updater never reads `uses:` refs inside composite `action.yml`
([dependabot/dependabot-core#6704](https://github.com/dependabot/dependabot-core/issues/6704),
open) — a consumer's vendored `make-target/action.yml`, and any third-party action
pinned inside a composite, is invisible to it. A grouped whuppi/ci bump moves only
the workflow-level refs and splits the pin, which the `pin-availability` gate then
rejects. The opt-in `composite-refs` job owns exactly that gap: it sweeps every
whuppi/ci ref across `.github` (workflows AND composites) to the latest release —
uniform by construction — and pins third-party actions inside composites to the
latest SHA with `pinact`. Dependabot keeps pub deps + third-party actions in
workflow FILES; the two never overlap. A consumer opts in by calling the reusable
with `sweepActions: true`, passing `CI_ACTIONS_TOKEN`, and adding `whuppi/ci*` to
its Dependabot `ignore`. The token is a whuppi org secret (`CI_ACTIONS_TOKEN`, a
Workflows-scope PAT) — the sweep writes `.github/workflows/`, which `GITHUB_TOKEN`
can't.
pinned inside a composite, is invisible to it. Splitting action updates between
Dependabot (workflows) and a sweep (composites) is a half-and-half we refuse: it
also splits the whuppi/ci pin, which the `pin-availability` gate rejects. So the
opt-in `action-refs` job owns **all** of it — every `uses:` ref in `.github`,
workflow and composite alike: it sweeps every whuppi/ci ref to the latest release
(uniform by construction) and pins every third-party action to the latest SHA with
`pinact` (which reads composites). Dependabot is left owning **only pub deps** —
the one thing nothing else here covers. A consumer opts in by calling the reusable
with `sweepActions: true`, passing `CI_ACTIONS_TOKEN`, and dropping the whole
`github-actions` ecosystem from its Dependabot config. The token is a whuppi org
secret (`CI_ACTIONS_TOKEN`, a Workflows-scope PAT) — the sweep writes
`.github/workflows/`, which `GITHUB_TOKEN` can't.

A pin bump merged to `main` reaches nobody until a release is cut — merging
and releasing are separate, deliberate steps.
Expand Down