Skip to content

ci: stop the chore block bumping shipped dependencies - #373

Merged
Ank-KhoaHo merged 1 commit into
mainfrom
ci/scope-dependabot-to-its-own-projects
Aug 24, 2026
Merged

ci: stop the chore block bumping shipped dependencies#373
Ank-KhoaHo merged 1 commit into
mainfrom
ci/scope-dependabot-to-its-own-projects

Conversation

@Ank-KhoaHo

Copy link
Copy Markdown
Owner

Both /src/ blocks ignore dependency-name: "*" and propose nothing, and their comment says bumps
there "stay a deliberate manual act". They did not. The tests block reaches src/ through
ProjectReference, Dependabot edits the src csproj on its way past, and that block carries
prefix: chore — hidden and non-bumping.

So the only automated path that touched a shipped dependency was the one whose own header says
"these do not ship, so they stay out of the public changelog."

What it cost

#366, titled "Bump the test-dependencies group with 10 updates", bumped seven shipped runtime
dependencies
: OfficeIMO 3.2.2 → 3.2.6 across eight packages, AngleSharp 1.7.1 → 1.7.2, PdfPig
0.1.15 → 0.1.16. OfficeIMO is the engine behind every DOCX, XLSX, PPTX and PDF path here.

Under chore: a consumer restores four patch versions of different rendering code with no changelog
entry and no version proposed. It also truncated all eight lockfiles, exactly as the /src/ blocks
predict — so the brokenness those blocks were disabled to avoid arrived anyway, under a different
label.

Why the existing guard passed

check-dependabot-scoping.py was right about what it checked, and its own comment named the
hole
:

The NAMED rules still have to be repeated. A wildcard in src/ stops Dependabot proposing bumps
rooted there; it does nothing about a bump proposed from the tests or root block that edits a src
csproj on its way past.

It required the 4 named rules. src/Directory.Build.props declares 15 packages, and only
SixLabors.Fonts was in both sets — leaving 14 reachable and bumpable.

Discarding "*" stays, and is right: requiring the tests block to repeat it would stop test
dependencies updating at all, for a reason that has nothing to do with them.

The fix

All fifteen are ignored in both blocks that reach src/, and the requirement is derived from
the props file rather than listed — the same principle as gen-third-party-notices.py reading the
lockfile.

Verified by sabotage in both directions, because a derived check has two ways to go quiet:

sabotage result
delete one shipped ignore 1 error, naming it
add a sixteenth package to the props required set grows to 19; tests block reported MISSING it

The second is the one that matters — a hand-written list passes that case, which is how four rules
came to cover one of fifteen packages. The script also refuses to run if the props file is missing
or parses to zero references, rather than quietly requiring less.

Not in scope

Whether shipped bumps should be automated under build: instead. That is a separate decision and
the /src/ blocks' lockfile reasoning argues against it. dependency-report.yml already delivers
the information weekly, read-only, which is what those blocks say was actually wanted.

Closes C37.

@Ank-KhoaHo
Ank-KhoaHo merged commit f05bc93 into main Aug 24, 2026
16 checks passed
@Ank-KhoaHo
Ank-KhoaHo deleted the ci/scope-dependabot-to-its-own-projects branch August 24, 2026 13:13
Ank-KhoaHo added a commit that referenced this pull request Aug 24, 2026
…t config (#374)

**#373 shipped a `.github/dependabot.yml` that Dependabot refuses to
parse.** It appended the
fifteen shipped packages to two ignore lists that already named
`SixLabors.Fonts`, so both gained a
duplicate:

```
The property '#/updates/2/ignore/18/dependency-name' is a duplicate.
```

Dependabot stops parsing the **whole file** on that, so every update it
would raise is disabled —
**security updates included**. `main` has been in that state since #373
merged.

Caught by Dependabot's own API check appearing on #366, not by anything
in this repo.

## Why nothing here saw it

Three separate blind spots, all pointing the same way:

- `check-dependabot-scoping.py` reduces each block's ignore list to a
**set**, so a duplicate is
  gone before it is examined
- `yaml.safe_load` accepts duplicate list items happily — they are list
entries, not map keys
- the config check that objects is Dependabot's, and it runs on a PR
touching the file, which
  #373's own checks did not show before I merged it

## Fixed

`SixLabors.Fonts` is no longer a standalone entry in either block; the
derived list carries it. In
the root block that entry had an `update-types:
["version-update:semver-major"]` line under it —
removing only the name left the line orphaned and broke the YAML
outright, which is worth recording
because it is the second way to get this wrong in one edit.

That is a deliberate widening: the root block ignored only SixLabors
majors, the derived entry
ignores it entirely. Ignoring more is always safe, and `src/` pins it to
an exact version anyway.

## Guarded

`check-dependabot-scoping.py` now counts each ignore list as a **list**
rather than a set, using
this file's own line patterns.

**Deliberately not via PyYAML.** The first version of this check
imported it inside a
`try/except ImportError` — and no workflow here installs PyYAML, so in
CI it would have skipped
itself and reported success. A check that silently does nothing is worse
than no check.

Sabotage-verified three ways, each duplicating a name that already
exists in its own block:

| duplicated | caught |
|---|---|
| `Ank.DocToolkit` | yes |
| `OfficeIMO.Word` | yes |
| `Microsoft.Extensions.Hosting` | yes |

My first two sabotage attempts were ineffective — I added a name to a
block that did not already
have it, so no duplicate existed and the check passed correctly. Worth
stating: an ineffective
sabotage looks exactly like a guard that does not work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant