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
135 changes: 8 additions & 127 deletions .github/workflows/upgrade-check.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
# Reusable consumer upgrade radar — the things a consuming repo owns that
# Dependabot can't bump, each in its own reviewed PR:
# 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.
# whuppi-ci-refs — every `whuppi/ci/...@vX.Y.Z` ref across .github (workflows
# AND vendored composite action.yml) → the latest whuppi/ci
# release, in one sweep. Dependabot never touches refs inside
# composite actions (dependabot/dependabot-core#6704), so a
# grouped bump splits the pin and the pin-availability gate
# rejects it — this keeps every ref on one version.
# The shared tool/binary pins (fvm, Chrome, bore, zizmor, actionlint) that
# whuppi/ci bakes into its actions reach this repo the same way — through the
# whuppi/ci ref bump above; whuppi/ci owns their versions via self-upgrade.yml.
# whuppi/ci refs (workflows AND composite action.yml) are bumped by Renovate
# (renovate.yml) — it reads composite action.yml, which Dependabot can't
# (dependabot/dependabot-core#6704). The shared tool/binary pins (fvm, Chrome,
# bore, zizmor, actionlint) reach consumers through a whuppi/ci release +
# Renovate bumping the whuppi/ci refs; whuppi/ci owns their versions via
# self-upgrade.yml.
name: Upgrade Check

on:
Expand Down Expand Up @@ -211,119 +208,3 @@ jobs:
else
gh pr edit "$EXISTING_PR" --title "chore: refresh lockfiles" --body "$PR_BODY"
fi

# ── whuppi/ci refs (workflows + vendored composite actions) ──────
# Dependabot can't bump whuppi/ci refs that live inside composite action.yml
# files (dependabot/dependabot-core#6704), so a grouped bump moves only the
# workflow-level refs and splits the pin — which pin-availability then rejects.
# This sweeps EVERY versioned whuppi/ci ref, workflow and composite alike, to
# the latest release in one PR. Consumers drop whuppi/ci from their Dependabot
# group; this owns it instead.
whuppi-ci-refs:
name: whuppi/ci refs
runs-on: ${{ inputs.runner }}
timeout-minutes: 15
permissions:
contents: write # pushes the chore/whuppi-ci-refs branch
pull-requests: write # opens / edits the PR
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.branch }}
persist-credentials: false

- name: Prepare branch
id: branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
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 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')
echo "existing_pr=$EXISTING_PR" >> "$GITHUB_OUTPUT"
if [ -n "$EXISTING_PR" ]; then
git fetch origin chore/whuppi-ci-refs 2>/dev/null || true
git checkout -B chore/whuppi-ci-refs origin/chore/whuppi-ci-refs 2>/dev/null || git checkout -b chore/whuppi-ci-refs
else
git checkout -b chore/whuppi-ci-refs
fi

- name: Sweep every whuppi/ci ref to the latest release
id: sweep
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
latest="$(gh api repos/whuppi/ci/releases/latest --jq '.tag_name' 2>/dev/null || true)"
# latest is interpolated into the sed below — reject anything but an
# exact vX.Y.Z tag, or a crafted release name becomes sed injection.
if ! printf '%s' "$latest" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::implausible whuppi/ci release tag: '$latest'" >&2
exit 1
fi
# Every .github file pinning a whuppi/ci ref to a version tag —
# workflows AND composite action.yml. `@main` refs (intentionally
# floating) carry no @v and are left untouched.
# Fill the array the bash-3.2-portable way (macOS ships that bash).
files=()
while IFS= read -r f; do files+=("$f"); done \
< <(grep -rlE 'whuppi/ci/[^@[:space:]]*@v[0-9.]+' .github || true)
if [ ${#files[@]} -eq 0 ]; then
echo "No versioned whuppi/ci refs found — nothing to sweep."
exit 0
fi
for f in "${files[@]}"; do
sed -i.bak -E "s#(whuppi/ci/[^@[:space:]]*)@v[0-9.]+#\1@${latest}#g" "$f"
rm -f "$f.bak"
done
# Hand the exact rewritten paths to the commit step so it stages only
# those, matching the sibling jobs' targeted add (never a blanket add).
{
echo "files<<SWEPT_EOF"
printf '%s\n' "${files[@]}"
echo "SWEPT_EOF"
} >> "$GITHUB_OUTPUT"
echo "swept ${#files[@]} file(s) → $latest"

- name: Commit + open or update the PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EXISTING_PR: ${{ steps.branch.outputs.existing_pr }}
BASE: ${{ inputs.branch }}
SWEPT_FILES: ${{ steps.sweep.outputs.files }}
run: |
# Stage only the files the sweep rewrote — never a blanket `git add`.
printf '%s\n' "$SWEPT_FILES" | while IFS= read -r f; do
[ -n "$f" ] && git add -- "$f"
done
git commit -m "ci: bump whuppi/ci refs to latest" || {
echo "whuppi/ci refs already current — nothing to bump."
exit 0
}
if [ -n "$EXISTING_PR" ]; then
git push origin chore/whuppi-ci-refs
else
git push --force origin chore/whuppi-ci-refs
fi

PR_BODY="$(cat <<'EOF'
Bumps every `whuppi/ci/...@vX.Y.Z` reference across `.github` — workflows
AND vendored composite actions — to the latest whuppi/ci release, in one
sweep, so the pin stays uniform. Dependabot can't do this: it never
touches refs inside composite `action.yml` (dependabot/dependabot-core#6704),
so its grouped bump splits the pin and pin-availability rejects it.

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

_Auto-generated by upgrade-check.yml_
EOF
)"

if [ -z "$EXISTING_PR" ]; then
gh pr create --base "$BASE" --head chore/whuppi-ci-refs \
--title "ci: bump whuppi/ci refs to latest" --label upgrade-ci-refs --draft --body "$PR_BODY"
else
gh pr edit "$EXISTING_PR" --title "ci: bump whuppi/ci refs to latest" --body "$PR_BODY"
fi
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ 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.2.0

- Added a reusable `renovate.yml` — self-hosted Renovate that each consumer calls
from a thin wrapper (same shape as `upgrade-check.yml`), running against the
calling repo. It reads composite `action.yml`
([dependabot-core#6704](https://github.com/dependabot/dependabot-core/issues/6704)
blind spot), so it keeps whuppi/ci refs uniform and bumps third-party actions
hidden in composites. Needs a `RENOVATE_TOKEN` org secret (Contents + Workflows +
Pull-requests + Issues: write) — Renovate must write `.github/workflows/`, which
`GITHUB_TOKEN` can't.
- `secrets.sh` gained an `org` scope for org-wide secrets (`set org/KEY`).
- Removed the `whuppi-ci-refs` job from `upgrade-check.yml`. Renovate replaces it:
the sweep needed a Workflows-scope token `GITHUB_TOKEN` couldn't provide, and
Renovate reads composites natively. Consumers migrate to the `renovate.yml`
wrapper and drop their Dependabot `github-actions` + `pub` config.

## 2.1.0

- Added a `whuppi-ci-refs` job to the reusable `upgrade-check.yml`. It sweeps
Expand Down
27 changes: 13 additions & 14 deletions docs/UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@ 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 |
| Third-party action SHAs (workflow files) | Dependabot (7-day cooldown) | Review, merge, cut a release |
| Consumers' Flutter SDK + lockfiles | reusable `upgrade-check.yml` in each consumer | Nothing here |
| Consumers' whuppi/ci pins | reusable `upgrade-check.yml` → `whuppi-ci-refs` job — sweeps every ref (workflow **and** composite) to the latest release | Cut the release; consumers sweep to it automatically |
| Consumer deps + every action `uses:` ref — pub, **and workflows AND composite `action.yml`** (third-party + whuppi/ci) | **Renovate** (`renovate.yml` reusable, one thin wrapper per consumer) | Review + merge the bump PRs it opens |
| Consumers' Flutter SDK + lockfiles, pre-Renovate | reusable `upgrade-check.yml` — for consumers not yet migrated to Renovate | Nothing here |

**Why the whuppi/ci sweep isn't Dependabot's job.** Dependabot's github-actions
updater never touches `uses:` refs inside composite `action.yml`
**Why Renovate, not Dependabot.** 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` pins the whuppi/ci
capability refs there, so a grouped Dependabot bump moves only the workflow-level
refs and splits the pinwhich the `pin-availability` gate then rejects. The
`whuppi-ci-refs` job `sed`-sweeps every versioned whuppi/ci ref, workflow and
composite alike, to the latest release, keeping the pin uniform by construction.
Consumers therefore drop `whuppi/ci*` from their Dependabot `github-actions`
group. (The same #6704 gap applies to any third-party SHA we ever pin inside our
OWN composite actions — keep third-party pins in workflow files where Dependabot
can see them.)
open) — a consumer's vendored `make-target/action.yml`, and any third-party action
pinned inside a composite, is invisible to it. A grouped bump moves only the
workflow-level refs and splits the pin, which the `pin-availability` gate then
rejects. Renovate reads composite `action.yml`, so it keeps every whuppi/ci ref
uniform (workflow and composite alike) and bumps the third-party actions hidden in
composites. Each consumer runs `renovate.yml` (a thin wrapper calling the reusable,
same shape as this one) and drops its Dependabot `github-actions` + `pub` config
once migrated. The token is a whuppi org secret (`RENOVATE_TOKEN`); Renovate must
write `.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