ci: stop the chore block bumping shipped dependencies - #373
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both
/src/blocks ignoredependency-name: "*"and propose nothing, and their comment says bumpsthere "stay a deliberate manual act". They did not. The tests block reaches
src/throughProjectReference, Dependabot edits the src csproj on its way past, and that block carriesprefix: 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 changelogentry and no version proposed. It also truncated all eight lockfiles, exactly as the
/src/blockspredict — so the brokenness those blocks were disabled to avoid arrived anyway, under a different
label.
Why the existing guard passed
check-dependabot-scoping.pywas right about what it checked, and its own comment named thehole:
It required the 4 named rules.
src/Directory.Build.propsdeclares 15 packages, and onlySixLabors.Fontswas in both sets — leaving 14 reachable and bumpable.Discarding
"*"stays, and is right: requiring the tests block to repeat it would stop testdependencies 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 fromthe props file rather than listed — the same principle as
gen-third-party-notices.pyreading thelockfile.
Verified by sabotage in both directions, because a derived check has two ways to go quiet:
MISSINGitThe 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 andthe
/src/blocks' lockfile reasoning argues against it.dependency-report.ymlalready deliversthe information weekly, read-only, which is what those blocks say was actually wanted.
Closes C37.