Skip to content

Sort the public API tracking files, with a script that keeps them that way - #3245

Merged
mgravell merged 1 commit into
mainfrom
marc/public-api-sort
Sep 17, 2026
Merged

mgravell merged 1 commit into
mainfrom
marc/public-api-sort

Conversation

@mgravell

Copy link
Copy Markdown
Collaborator

Adds eng/public-api.py and runs it. The diff is large but the content is unchanged - verified as a pure reorder, see below.

Previously pushed straight to main as 213f3c3 and reverted in 17e16f1: a 3000-line reorder that conflicts with open PRs wants review first, whatever the mechanical verification says. Nothing is wrong with the content.

Why a script

The analyzer does not care about order and its code fix appends, so these files accrete in whatever order entries were added and drift out of any order they are put into. RESPite's 236-line shipped file was unsorted too, so this is general drift rather than one neglected file - nothing maintains order between runs. The script is what does, run at a moment somebody is already editing them.

eng/public-api.py              # sort in place, promote nothing  (default)
eng/public-api.py --promote    # the release step: fold unshipped into shipped, and sort
eng/public-api.py --check      # write nothing; exit 1 if anything is unsorted  (for CI)

Promotion is opt-in on purpose. "Shipped" means released and frozen, so folding unshipped in mid-cycle would freeze API that has not shipped and then cannot change without it looking like a break. Promotion belongs to the release: ship the package, then promote. This PR runs with no --promote, since unshipped is empty after 3.2.15 - so it is purely the sort.

The sort key is the point

It strips the modifier (and any [SERxxx] annotation) rather than sorting the raw line, so a type's surface stays in one block. Sorting raw groups by modifier instead:

RedisValue.* (68 entries) ConfigurationOptions.* (117)
before 7 blocks 10 blocks
sorting the raw line 4 blocks 4 blocks
this 1 block 1 block

A 3000-line reorder that still cannot show a type in one place would not be worth doing.

Verification

  • Pure reorder: identical entry multiset and identical entry counts in all three files, before and after. Checked by hashing the sorted content on each side rather than by reading the diff.
  • Clean full solution build, and a /p:RunAnalyzers=true build of the library - which is what actually exercises RS0016/RS0017 on every target framework, since analyzers otherwise run on one TFM only.
  • Idempotent: re-running reports "nothing to do".
  • Blank lines are dropped as part of sorting; there were two stray ones mid-file.

Cost worth weighing before merging

This conflicts with the open PRs that touch PublicAPI.Shipped.txt - #2107, #2221, #2547, #2838 (which appends 132 lines), #2959, #3125. Each resolves by re-adding its entries and re-running the script, but that is real work for whoever owns them, and some are old enough that a conflict may finish them off.

If that is too disruptive now, the alternative moment is the v4 break, when those branches need rebasing anyway and the churn is already priced in. The script is useful either way.

Not included

A CI step running --check. Without one this will drift again, but it is a separate decision - say the word and it is one line in the workflow.

The analyzer does not care about order and its code fix appends, so these files
accrete in whatever order entries were added and drift out of any order they are
put into - RESPite's 236-line shipped file was unsorted too, so this is drift
rather than one file having been neglected. Nothing keeps them sorted between
runs, so the script is what does, at a moment somebody is already editing them.

Sorting and promotion are separate, and promotion is opt-in: "shipped" means
released and frozen, so folding unshipped in mid-cycle would freeze API that has
not shipped and cannot then change without it looking like a break. Default is
sort-only; --promote is the release step; --check writes nothing and exits 1,
for CI if we want it later.

The sort key strips the modifier (and any [SERxxx] annotation) rather than
sorting the raw line. That is the whole point: sorting raw groups by modifier
instead, so a type's surface splits across abstract/override/static/virtual
blocks and a bare one. RedisValue went from 7 blocks to 1, ConfigurationOptions
from 10 to 1; sorting raw would have left both at 4, which is a 3000-line
reorder that still cannot show a type in one place.

Run here with no --promote, since unshipped is empty after 3.2.15 - so this is
purely the sort. Verified it is a pure reorder: same entry multiset and same
counts in all three files before and after, plus a clean full build and a
RunAnalyzers=true sweep, which is what actually exercises RS0016/RS0017 on every
target framework. Re-running is a no-op.
@mgravell
mgravell merged commit 45d67f3 into main Sep 17, 2026
6 checks passed
@mgravell
mgravell deleted the marc/public-api-sort branch September 17, 2026 16:00
mgravell added a commit that referenced this pull request Sep 17, 2026
One conflict, in PublicAPI.Unshipped.txt, and it is the promotion rather than
anything this branch did: main moved the five RedisValue.EqualityComparer
entries into Shipped when 3.2.15 went out, while this branch still carried them
alongside its own 76. Resolved by keeping this branch's entries and dropping
those five - they are in Shipped now, and confirmed present there before
removing them, since a duplicate across the two files is what RS0025 rejects.

Unshipped is then sorted with eng/public-api.py, which arrived in the same
merge. That is the case it exists for: this file is append-only across branches,
so it is where API additions collide, and sorting it is what stops the next one
landing in the same place.

Verified with a clean full-solution build, a RunAnalyzers=true sweep of the
library - the thing that actually exercises RS0016/RS0017 per target framework -
and --check reporting nothing left to do.
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