Skip to content

Adopt Dependabot: nothing proposes crossing this repo's dependency caps #283

Description

@gregoryfoster

Mirroring what notifier just shipped. Watcher is one uv table plus a
github-actions surface, with 21 of 23 runtime specifiers already capped — so
the policy is effectively in place here and only the thing that proposes
crossing the caps is missing.

Why

This repo caps its dependencies — nearly every specifier carries an upper
bound. That is deliberate and good, and it has one consequence that is easy to
miss: a cap makes new releases invisible to uv lock. Nothing proposes
crossing it. The bound sits there until a person remembers to look, and nobody
remembers. notifier hit this and fixed it in
CannObserv/notifier#33:
Dependabot is what turns "a version wall nobody crosses" into a reviewed,
CI-gated diff.

The first batch landed there last week —
notifier#45 is the triage
of all eight PRs, including one that mattered (a markdown renderer carrying
four CWE-400 quadratic-DoS fixes that had been sitting unreachable behind a
<4 cap for months).

What notifier ships

Two files, both small.

.github/dependabot.yml — the whole config, comments included, since each
one records a decision that cost something to learn:

# Proposes the bumps the 0.x cap policy makes invisible to `uv lock`.
# Every cap sits one minor below a wall by design: crossing it takes a
# reviewed diff, and this file is what produces that diff instead of a memory.
version: 2
updates:
  - package-ecosystem: "uv"
    directories:
      - "/"
    schedule:
      interval: "weekly"
    # Touch a specifier only when the new release falls outside it. Floors in
    # these tables mean "oldest supported" and must not ratchet on every bump;
    # the default `auto` resolves to `widen` for libraries, and `widen` is
    # unsupported for uv (dependabot-core#15290).
    versioning-strategy: increase-if-necessary
    groups:
      # Dev tooling carries most of the churn (ruff alone ships minors roughly
      # monthly); one grouped PR keeps the bot readable. Runtime dependencies
      # stay individual PRs — those deserve their own review.
      dev-tools:
        dependency-type: "development"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

A guard test (tests/ci/test_dependabot.py in notifier). Worth the twenty
lines: a mistyped ecosystem name or a bad directories entry stops producing
PRs without erroring anywhere a person looks. The bot just goes quiet, and
quiet reads like "no updates available". Assert the ecosystems, the directory
list, versioning-strategy, and the group.

Two blockers to clear first — notifier had neither

notifier's CI uses no secrets, no cloud auth and no private index, so its
Dependabot PRs went green on the first try. This repo differs in exactly the
way that matters, twice. Both apply to every ecosystem, including
github-actions
, because ci.yml is the gate on any Dependabot PR.

1. Dependabot runs have their own variable store

ci.yml authenticates to GCP with vars.GCP_WIF_PROVIDER and each job
requests id-token: write. Dependabot-triggered runs do not read the
Actions secret/variable store — they read the separate Dependabot store
(Settings → Secrets and variables → Dependabot). Until GCP_WIF_PROVIDER
is mirrored there with this repo in its access list, every Dependabot PR is
red at the auth step.

Confirm before writing any config, and while you are there confirm that OIDC
(id-token: write) is actually granted on Dependabot-triggered runs in this
org — if it is not, the wheelhouse cannot be synced for those PRs at all and
the gate needs rethinking (a workflow_run path, or accepting a manual re-run
per PR). A red bot is worse than no bot: red PRs get ignored, and then the
real one gets ignored too.

2. The private wheelhouse

co-core and co-core-aio are declared runtime dependencies that resolve
only from find-links = ["./.wheelhouse"], a directory that holds one
.gitkeep in git and is populated by scripts/sync_wheelhouse.py from
gs://co-gcs-pypi before any uv command.

Dependabot's uv updater checks the repo out and resolves the whole graph in its
own container. It will not run that script and has no GCS credentials, so it
should be expected to fail to resolve and error on every uv update. That is
a Dependabot-side failure, visible under Insights → Dependency graph →
Dependabot, not a red PR.

Options, roughly in order of effort: serve the GCS bucket as a PEP 503 index
over HTTPS and declare it as a registries: entry with a Dependabot secret;
or leave the uv ecosystem out and take the github-actions half now. ignore:
does not help — resolution still needs the package even when it is not being
updated.

Suggested order

  1. Mirror GCP_WIF_PROVIDER into the Dependabot variable store; confirm a
    Dependabot-triggered run can authenticate.
  2. Add .github/dependabot.yml with the github-actions entry only. It
    needs no package resolution, so it is the honest test of blocker 1 on its
    own. This repo pins actions/checkout@v5 and astral-sh/setup-uv@v5; both
    are two majors behind, so expect PRs immediately.
  3. Add the guard test.
  4. Only then decide about the uv ecosystem, with blocker 2 answered.

Repo-specific notes

Dependabot will not move the notifier-client pin

[tool.uv.sources]
notifier-client = { git = "ssh://git@github-notifier/CannObserv/notifier.git", subdirectory = "clients/python", rev = "v0.2.1" }

A tool.uv.sources git rev is not something the uv updater bumps, so this pin
stays wherever it is set no matter how the config is written. Two things follow:

  • The pin is behind. notifier's SDK is at 0.3.1; this repo is pinned to
    v0.2.1. In between: 0.3.0 documented AuthErrorDetail and the widened
    return types from notifier#22, and 0.3.1 dropped python-dateutil from the
    client's dependencies and regenerated against a newer OpenAPI generator.
  • Upstream has not tagged those releases. notifier carries only v0.2.0
    and v0.2.1 as tags, so there is currently no v0.3.1 to point this rev at
    even if you wanted to. Raised upstream in
    notifier#49, which is
    about what the SDK's version is supposed to mean; tagging is the part that
    blocks this repo.

Neither is a reason to delay the config — it is a reason not to expect the bot
to cover this dependency. Whatever cadence you settle on for the notifier
client stays manual.

The ci.yml step that rewrites this SSH source to HTTPS is also worth keeping
in mind: a Dependabot-triggered run has to clone that source too, so it is a
second thing to confirm works under the Dependabot token, alongside blocker 1.

One uncapped runtime specifier

html5lib>=1.1 is the only runtime dependency without an upper bound. Worth
capping in the same pass so the config and the policy it serves agree.

versioning-strategy matters here

Floors like co-core[extract]>=0.10,<0.11 mean "oldest supported".
increase-if-necessary touches a specifier only when the new release falls
outside it; the default auto would ratchet the floor on every bump, and for
uv it resolves to widen, which is unsupported (dependabot-core#15290).

What the first batch looks like

From notifier's, so you can budget for it:

  • Expect roughly one PR per capped dependency that has moved, all at once.
    notifier got eight. Six were mergeable after reading the changelog; one
    needed a service restart and a smoke check; one needed real work.
  • The grouped dev-tools PR is the one that needs work. Linter and
    code-generator majors change output, and the bump alone lands red. In
    notifier's case ruff 0.16 began formatting Python code blocks inside
    Markdown by default, and an OpenAPI generator bump rewrote every generated
    model. Both were fixed by pushing commits onto the Dependabot branch —
    allowed, and it stops the bot rebasing under you.
  • Merge one at a time. Every PR that touches the root table rewrites
    uv.lock, so merging one invalidates the rest. Comment @dependabot rebase
    and let it catch up; Dependabot may also close and re-open a grouped PR under
    a new number when the group's contents change mid-flight.
  • Read the changelog, not the diff. The valuable finding in notifier's
    batch was a security fix five minors back that the version number did not
    advertise. The rest were routine.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions