ci(labels): add a labeler config audit against the canonical label set - #529
Conversation
A dangling labeler reference never fails. actions/labeler applies labels through the issues API, which creates a label that does not exist instead of erroring, so a labeler.yml naming a deleted legacy label silently recreates it on the next matching pull request. An org-wide label cleanup then partially undoes itself with nothing in any log explaining why. That is how 'enhancement*' returned to z-shell/zi after the #467 cleanup deleted it. Add a read-only audit reporting labeler.yml keys absent from lib/labels.yml, with the canonical replacement where legacy_migrations records one. A live run across 93 repositories finds 162 non-canonical references in 23 repositories. Read-only by design: which labels land on a repository's pull requests is a per-repository editorial call, not a uniform substitution, so the script reports and the owning repository edits. Covers both actions/labeler schemas, since v4 and v5 are both in use across the org and only top-level keys are label names in either. Treats a missing config as absence rather than a finding, and lets any non-404 API error surface rather than reporting the repository as clean. Add 11 unit tests running against an injected fixture client with no live API calls, plus a workflow that runs them and self-audits this repository.
|
Correction to the framing in this PR description, recorded here rather than silently amended. The description says a drifted
This does not change the code or the tests. The audit reports config drift against the canonical label set, which is the right thing for it to report either way: an inert |
|
Second correction, and this one retracts the motivating claim in the description above. The description says a drifted
Full detail and retraction in #527. What this means for this PRThe code and tests are unaffected, and I am not proposing changes to them. The audit reports What changes is the justification. This is configuration-drift detection and hygiene, not a guard against an active regression. I would rather you evaluate it on that basis than on the stronger claim I originally made for it. The value that survives is still real: these configs name labels that no longer exist, they carry Docusaurus-template leftovers into repositories with no such paths, and nothing else in CI would notice. It becomes materially more important if |
Addresses the durable part of #527: making labeler drift detectable instead of silent.
The failure mode
A dangling labeler reference never fails.
actions/labelerapplies labels through the issues API, which creates a label that does not exist rather than erroring. So alabeler.ymlnaming a deleted legacy label silently recreates it on the next matching pull request, and an org-wide label cleanup partially undoes itself with nothing in any log to explain why.This is not hypothetical.
enhancement ✨returned toz-shell/ziafter #467 deleted it org-wide, becausezi'slabeler.ymlmaps that exact name to*.zshandlib/**.What this adds
scripts/labeler-config-audit.rb, a read-only audit reportinglabeler.ymlkeys that are absent fromlib/labels.yml, annotated with the canonical replacement wherelegacy_migrationsrecords one.A live run across all 93 repositories finds 162 non-canonical references in 23 repositories:
documentation 📝type:docsenhancement ✨type:maintenancemaintenance 📈type:maintenanceci 🤖area:cidependencies 📦area:dependenciesi18n 🌐annex 🌀area:annexplugin ⚙️area:pluginpackage 📦area:packageannex 💠plugin 🧿Of those 23 repositories, 21 are in label-sync scope and 2 are forks.
Note this counts something slightly wider than "references that would recreate a label". 162 is every reference to a non-canonical name; the subset naming a label that does not currently exist, and would therefore be created on the next matching pull request, was 139 across the 21 in-scope repositories. The wider definition is the right one for a gate, because a reference to a non-canonical label that happens to exist today is still drift.
Design notes
Tests
11 unit tests against an injected fixture client, no live API calls, following the
scripts/test-repo-settings-audit.rbpattern. They cover both schemas, missing configs, the.yamlextension, legacy keys resolving to a replacement, unmapped keys reporting a null replacement rather than a guess, invalid YAML, exit codes, and the 404-versus-other-error distinction.The workflow also self-audits this repository. That step is a no-op today, since
z-shell/.githubhas no labeler config, and exists so that adding one cannot introduce drift unnoticed.Uses no non-default gems:
base64stopped being a default gem in Ruby 3.4, so decoding goes through coreunpack1("m").Not included
Rewriting the 23
labeler.ymlfiles, which is per-repository work across separate repositories, and deciding the three unmapped names (i18n 🌐,annex 💠,plugin 🧿). Both remain open on #527.Related: #467, #524, #527