Per-template classifiers used to triage the wrapper-sweep work that
follows each of the foundational reusable PRs filed against
standards:
| Classifier | Reusable PR | Template |
|---|---|---|
|
#187 |
|
|
#190 |
|
|
#192 |
|
|
#193 |
|
-
Paginate
gh api /search/codefor the template across the estate. -
Group results by blob SHA; fetch each unique blob exactly once.
-
Classify each blob (job-set match / line-count band / language matrix).
-
Emit per-repo TSV:
<repo>\t<sha>\t<class>\t<reason>\t<lines>\t<details>.
The expensive step (blob fetch) is cached in $BLOBS_DIR, so reruns
are fast.
# 1. Paginate the search (one-time per template):
gh api --paginate -X GET '/search/code' \
-f q='filename:mirror.yml path:.github/workflows org:hyperpolymath' \
--jq '.items[] | {repo: .repository.name, sha: .sha}' \
> /tmp/mirror-full.json
# 2. Run the classifier:
BLOBS_DIR=/tmp/mirror-blobs \
./classify-mirror.sh /tmp/mirror-full.json > /tmp/mirror-classification.tsv
# 3. Summarise:
awk -F'\t' '{print $3}' /tmp/mirror-classification.tsv | sort | uniq -c | sort -rngh api /search/code undercounts monorepo-nested workflow files in
three compounding ways:
-
Layer 1 —
path:filter is a prefix match.path:.github/workflowsexcludes paths likea2ml/bindings/deno/.github/workflows/mirror.ymloutright. -
Layer 2 — even broad queries are org-scope-truncated. Removing the
path:filter and runningfilename:codeql.yml org:hyperpolymathreturns ~190 paths, but per-repo enumeration of justdeveloper-ecosystemfinds 170 codeql.yml files. The endpoint silently caps results once it hits internal limits. Empirically validated againstscorecard.yml: broad query saw 152 paths, all top-level; per-repo enumeration revealed 626 nested copies the broad query missed entirely. -
Layer 3 — nested workflows do not execute. GitHub Actions only runs workflows from the repo-root
.github/workflows/directory. Nested copies are inert vendored templates or stale leftover. Security-driven campaigns (e.g. propagating a missing guardrail) do NOT close additional attack surface via nested wrappers. Single-source-of-truth campaigns still benefit.
For accurate counts, use list-workflow-paths.sh (this directory),
which walks gh repo list and queries each repo’s Git Tree API
directly — bypassing Layers 1 and 2.
./list-workflow-paths.sh codeql.yml \
> /tmp/drift-survey/codeql-all-tuples.tsv
# Top-level vs nested split:
awk -F'\t' '{print $4}' /tmp/drift-survey/codeql-all-tuples.tsv \
| sort | uniq -cOutput format: <repo>\t<path>\t<blob-sha>\t<top-level|nested>.
The legacy gh api /search/code query is still useful as a quick first
look, but list-workflow-paths.sh is the source of truth for any
sweep-planning decision.
-
TRIVIAL/TRIVIAL_CURRENT/TRIVIAL_DEFAULT— canonical match; pure mechanical wrapper conversion. -
MISSING_*/SLIM_*/OLDER_*— propagation lag; same wrapper as TRIVIAL upgrades the workflow body on first run after merge. -
SINGLE_NON_DEFAULT/MULTI_LANGUAGE— requires one or more input overrides at the call site. -
NEEDS_REVIEW— extra jobs, line count out of any known band, or custom workflow body. Per-repo diff required before wrapper.