[docs] Unify changelog + release-notes generation: stable rollups and local skia links#4177
Merged
Merged
Conversation
The unified-versioning refactor (#4171) started passing --nugetDiffPrerelease=true to docs-api-diff-past so the 4.x line — which ships only as previews/rcs until it stabilises — would get changelogs. But the loop still emitted a folder for *every* enumerated version, so every intermediate preview now produced its own directory: the API Diff run filled bot/api-diff (PR #4173) with 4.147.0-preview.{1,2,3}, 4.148.0-rc.1.2 and 4.150.0-preview.1.1 dirs across every tracked package (~2,300 added files). Historically changelogs/ held stable versions only. Collapse the feed to one entry per release line (the numeric version core with the prerelease label stripped: 4.148.0-rc.1.2 -> 4.148.0; a genuine 4-part stable like 1.49.2.1 keeps its fourth digit) and emit a single rollup changelog per line, diffed against the line's representative package — the newest stable if it shipped, otherwise the newest prerelease. This mirrors the release-notes pages, which are stable-named rollups of the previews in between. A line is emitted when it shipped stable, or when it is an active dev line (preview-only, not superseded, and newer than the last stable, e.g. 4.148/4.150). Abandoned preview-only lines (superseded in versions.json, e.g. 4.147) and old never-shipped previews are skipped; their changes are absorbed into the successor's cumulative diff. Baselines are unchanged: versions.json compare_to overrides win, otherwise diff against the previous emitted line. For SkiaSharp this regenerates to the stable set plus the two active lines: it adds 3.119.4 (the shipped stable, previously missing) and the 4.148.0/4.150.0 rollups, and prunes the superseded preview-only 3.0.0 and 3.119.3 directories that predated versions.json. RunBreakingAndFullDiff (published overload) now takes the changelog folder name separately from the diffed package version, since they differ while a line is still in preview. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
📦 Try the packages from this PRWarning Do not run these scripts without first reviewing the code in this PR. Step 1 — Download the packages bash / macOS / Linux: curl -fsSL https://raw.githubusercontent.com/mono/SkiaSharp/main/scripts/get-skiasharp-pr.sh | bash -s -- 4177PowerShell / Windows: iex "& { $(irm https://raw.githubusercontent.com/mono/SkiaSharp/main/scripts/get-skiasharp-pr.ps1) } 4177"Step 2 — Add the local NuGet source dotnet nuget add source ~/.skiasharp/hives/pr-4177/packages --name skiasharp-pr-4177More options
Or download manually from Azure Pipelines — look for the Remove the source when you're done: dotnet nuget remove source skiasharp-pr-4177 |
The "Update Release Notes" agentic workflow
(.github/workflows/update-release-notes.md, timeout-minutes: 15) timed out on
its first post-merge run. The cause was the per-PR "effort" badge: it ran one
network `git fetch refs/pull/N/head` per PR to recover pre-squash commit
detail. For the 4.148 release that is 162 serial fetches, each paying full
proxied-TLS setup (~4s) behind the workflow's Squid firewall — ~11 min plus an
unshallow, blowing the 15-min cap. On timeout `safe_outputs` is skipped, so no
PR is ever opened and the cold author-cache never warms: the cold start cannot
self-heal.
The effort value (commit count + working days) earned none of that cost: it is
not rendered in the notes and the model never surfaced it. Everything actually
shown — title, author, number, body, companion skia link — already comes from a
single local `git log`.
Changes:
* Remove the effort computation and its rendering entirely
(_get_pr_effort_from_git, compute_pr_effort, _fetch_skia_pr_effort,
add_pr_effort). This alone takes a cold full `--all` from a 15-min timeout
to ~35s.
* Keep the companion `skia: mono/skia#N` hint, parsed locally from the PR
body, and add the `mono/skia#N` shorthand that milestone bumps use.
* Resolve the companion link for submodule-bump PRs that don't spell it out,
purely locally. The bump and its target skia SHA are read from the
superproject tree (`git rev-parse <commit>:externals/skia` vs its parent);
the bumped skia commit's own subject ((#N) / "Merge pull request #N") is then
read from the submodule. The only network is one batched, blobless, shallow
`git fetch` of just the bumped SHAs; already-present commits are skipped, so
`--all` fetches each skia commit at most once (a handful per run, not 162).
For 4.148 this lifts companion-link coverage of skia-bumping PRs from 18 to 22
of 23. The single remaining gap is an anomalous backwards gitlink (PR #4081
resets the submodule to mono/skia's scaffold root, which has no PR); its real
companion, skia #242, is already linked via the forward bump. The other
historical misses are skia commits pushed directly to mono/skia with no PR at
all, so there is nothing to link.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
📖 Documentation Preview The documentation for this PR has been deployed and is available at: 🔗 View Staging Site This preview will be updated automatically when you push new commits to this PR. This comment is automatically updated by the documentation staging workflow. |
mattleibow
added a commit
that referenced
this pull request
Jun 17, 2026
A version's "released" and "unreleased" states are orthogonal: a version
can be on NuGet as a shipped prerelease (e.g. 4.150.0-preview.1) AND have
newer, not-yet-released commits on its head branch (main). The original
two-file model gave each of those its own page — a full-rollup released
`{version}.md` and a small-delta `{version}-unreleased.md` — and they
coexisted while the version was in flight.
A five-PR refactor stack (#4174, #4177, #4179) collapsed this into a
single page per version:
* #4174 added `--all` + `cleanup_stale_unreleased`, which deleted
`{v}-unreleased.md` whenever `{v}.md` existed — killing the
coexisting in-flight delta page.
* #4179 (R2) forced `main`/`.x` heads to honor `versions.json`
`compare_to`, turning the small "what may ship next" delta into a
full rollup.
* #4179 (R1) remapped prerelease branches to `-unreleased` and
deferred them, collapsing released + unreleased onto one file.
Restore the original delta semantics while keeping the genuinely-good
additions from the stack (versions.json supersession + compare_to on the
RELEASED rollup pages, preview-milestone rollup, contributor credit,
local skia links, `--all` idempotency):
* `_page_filename`: versioned branch (incl. -rc/-preview) -> released
`{version}.md`; `main`/`.x` head -> `{version}-unreleased.md`.
* `determine_diff_range` (main + servicing `.x`): drop the versions.json
`compare_to` override on heads so the unreleased page is again the
small delta from the last release to head, not a rollup. `compare_to`
still drives the released rollup pages produced by versioned branches.
* `cmd_all`: process main + servicing + ALL canonical versioned branches;
no deferral (distinct filenames never collide).
* `cleanup_stale_unreleased`: delete `{v}-unreleased.md` only once the
line advances to a strictly-higher version in the same minor — keeps
the in-flight released+unreleased pair, still prunes obsolete deltas
(e.g. 3.119.4-unreleased once 3.119.5 appears).
* `_write_page`: an unreleased head with an empty delta emits no page
(and prunes any stale one) — nothing to ship next.
Net effect on the 4.x line: 4.148.0.md / 4.150.0.md are full released
rollups (with preview milestones + supersede banners), 4.150.0-unreleased.md
is the small `release/4.150.0-preview.1..main` delta, and 4.148.0 has no
unreleased page because release/4.148.x has zero commits past the rc.
Verified by running the script for real (`--all`): it emitted exactly
those pages and listed only the changed ones under "Files to polish".
Keep the page model where it belongs — in the SCRIPT. The script owns ALL
file creation, naming, diff ranges, supersession, and cleanup; the AI/skill
only polishes the prose of the files the script lists. The detailed model
now lives in the script's module + function docstrings, and SKILL.md is
trimmed to a clear "division of responsibility" instead of duplicating
(and drifting from) the naming rules.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mattleibow
added a commit
that referenced
this pull request
Jun 17, 2026
A version's "released" and "unreleased" states are orthogonal: a version
can be on NuGet as a shipped prerelease (e.g. 4.150.0-preview.1) AND have
newer, not-yet-released commits on its head branch (main). The original
two-file model gave each of those its own page — a full-rollup released
`{version}.md` and a small-delta `{version}-unreleased.md` — and they
coexisted while the version was in flight.
A five-PR refactor stack (#4174, #4177, #4179) collapsed this into a
single page per version:
* #4174 added `--all` + `cleanup_stale_unreleased`, which deleted
`{v}-unreleased.md` whenever `{v}.md` existed — killing the
coexisting in-flight delta page.
* #4179 (R2) forced `main`/`.x` heads to honor `versions.json`
`compare_to`, turning the small "what may ship next" delta into a
full rollup.
* #4179 (R1) remapped prerelease branches to `-unreleased` and
deferred them, collapsing released + unreleased onto one file.
Restore the original delta semantics while keeping the genuinely-good
additions from the stack (versions.json supersession + compare_to on the
RELEASED rollup pages, preview-milestone rollup, contributor credit,
local skia links, `--all` idempotency):
* `_page_filename`: versioned branch (incl. -rc/-preview) -> released
`{version}.md`; `main`/`.x` head -> `{version}-unreleased.md`.
* `determine_diff_range` (main + servicing `.x`): drop the versions.json
`compare_to` override on heads so the unreleased page is again the
small delta from the last release to head, not a rollup. `compare_to`
still drives the released rollup pages produced by versioned branches.
* `cmd_all`: process main + servicing + ALL canonical versioned branches;
no deferral (distinct filenames never collide).
* `cleanup_stale_unreleased`: delete `{v}-unreleased.md` only once the
line advances to a strictly-higher version in the same minor — keeps
the in-flight released+unreleased pair, still prunes obsolete deltas
(e.g. 3.119.4-unreleased once 3.119.5 appears).
* `_write_page`: an unreleased head with an empty delta emits no page
(and prunes any stale one) — nothing to ship next.
Net effect on the 4.x line: 4.148.0.md / 4.150.0.md are full released
rollups (with preview milestones + supersede banners), 4.150.0-unreleased.md
is the small `release/4.150.0-preview.1..main` delta, and 4.148.0 has no
unreleased page because release/4.148.x has zero commits past the rc.
Verified by running the script for real (`--all`): it emitted exactly
those pages and listed only the changed ones under "Files to polish".
Keep the page model where it belongs — in the SCRIPT. The script owns ALL
file creation, naming, diff ranges, supersession, and cleanup; the AI/skill
only polishes the prose of the files the script lists. The detailed model
now lives in the script's module + function docstrings, and SKILL.md is
trimmed to a clear "division of responsibility" instead of duplicating
(and drifting from) the naming rules.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mattleibow
added a commit
that referenced
this pull request
Jun 17, 2026
A version's "released" and "unreleased" states are orthogonal: a version
can be on NuGet as a shipped prerelease (e.g. 4.150.0-preview.1) AND have
newer, not-yet-released commits on its head branch (main). The original
two-file model gave each of those its own page — a full-rollup released
`{version}.md` and a small-delta `{version}-unreleased.md` — and they
coexisted while the version was in flight.
A five-PR refactor stack (#4174, #4177, #4179) collapsed this into a
single page per version:
* #4174 added `--all` + `cleanup_stale_unreleased`, which deleted
`{v}-unreleased.md` whenever `{v}.md` existed — killing the
coexisting in-flight delta page.
* #4179 (R2) forced `main`/`.x` heads to honor `versions.json`
`compare_to`, turning the small "what may ship next" delta into a
full rollup.
* #4179 (R1) remapped prerelease branches to `-unreleased` and
deferred them, collapsing released + unreleased onto one file.
Restore the original delta semantics while keeping the genuinely-good
additions from the stack (versions.json supersession + compare_to on the
RELEASED rollup pages, preview-milestone rollup, contributor credit,
local skia links, `--all` idempotency):
* `_page_filename`: versioned branch (incl. -rc/-preview) -> released
`{version}.md`; `main`/`.x` head -> `{version}-unreleased.md`.
* `determine_diff_range` (main + servicing `.x`): drop the versions.json
`compare_to` override on heads so the unreleased page is again the
small delta from the last release to head, not a rollup. `compare_to`
still drives the released rollup pages produced by versioned branches.
* `cmd_all`: process main + servicing + ALL canonical versioned branches;
no deferral (distinct filenames never collide).
* `cleanup_stale_unreleased`: delete `{v}-unreleased.md` only once the
line advances to a strictly-higher version in the same minor — keeps
the in-flight released+unreleased pair, still prunes obsolete deltas
(e.g. 3.119.4-unreleased once 3.119.5 appears).
* `_write_page`: an unreleased head with an empty delta emits no page
(and prunes any stale one) — nothing to ship next.
Net effect on the 4.x line: 4.148.0.md / 4.150.0.md are full released
rollups (with preview milestones + supersede banners), 4.150.0-unreleased.md
is the small `release/4.150.0-preview.1..main` delta, and 4.148.0 has no
unreleased page because release/4.148.x has zero commits past the rc.
Verified by running the script for real (`--all`): it emitted exactly
those pages and listed only the changed ones under "Files to polish".
Keep the page model where it belongs — in the SCRIPT. The script owns ALL
file creation, naming, diff ranges, supersession, and cleanup; the AI/skill
only polishes the prose of the files the script lists. The detailed model
now lives in the script's module + function docstrings, and SKILL.md is
trimmed to a clear "division of responsibility" instead of duplicating
(and drifting from) the naming rules.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mattleibow
added a commit
that referenced
this pull request
Jun 17, 2026
A version's "released" and "unreleased" states are orthogonal: a version
can be on NuGet as a shipped prerelease (e.g. 4.150.0-preview.1) AND have
newer, not-yet-released commits on its head branch (main). The original
two-file model gave each of those its own page — a full-rollup released
`{version}.md` and a small-delta `{version}-unreleased.md` — and they
coexisted while the version was in flight.
A five-PR refactor stack (#4174, #4177, #4179) collapsed this into a
single page per version:
* #4174 added `--all` + `cleanup_stale_unreleased`, which deleted
`{v}-unreleased.md` whenever `{v}.md` existed — killing the
coexisting in-flight delta page.
* #4179 (R2) forced `main`/`.x` heads to honor `versions.json`
`compare_to`, turning the small "what may ship next" delta into a
full rollup.
* #4179 (R1) remapped prerelease branches to `-unreleased` and
deferred them, collapsing released + unreleased onto one file.
Restore the original delta semantics while keeping the genuinely-good
additions from the stack (versions.json supersession + compare_to on the
RELEASED rollup pages, preview-milestone rollup, contributor credit,
local skia links, `--all` idempotency):
* `_page_filename`: versioned branch (incl. -rc/-preview) -> released
`{version}.md`; `main`/`.x` head -> `{version}-unreleased.md`.
* `determine_diff_range` (main + servicing `.x`): drop the versions.json
`compare_to` override on heads so the unreleased page is again the
small delta from the last release to head, not a rollup. `compare_to`
still drives the released rollup pages produced by versioned branches.
* `cmd_all`: process main + servicing + ALL canonical versioned branches;
no deferral (distinct filenames never collide).
* `cleanup_stale_unreleased`: delete `{v}-unreleased.md` only once the
line advances to a strictly-higher version in the same minor — keeps
the in-flight released+unreleased pair, still prunes obsolete deltas
(e.g. 3.119.4-unreleased once 3.119.5 appears).
* `_write_page`: an unreleased head with an empty delta emits no page
(and prunes any stale one) — nothing to ship next.
Bucket PRs per preview so the AI can summarize each milestone directly.
A released rollup spans several prerelease tags; a single flat PR list
left the AI guessing which work shipped in which preview. The script now
partitions the PRs into per-preview buckets via git ancestry — each PR
under the EARLIEST milestone tag whose range contains its commit, i.e.
the preview it first shipped in (`bucket_prs_by_milestone`). The buckets
exhaustively cover the range (every PR lands in exactly one), so they ARE
the full list — no separate flat list to drift. The AI renders one
`## Preview N` section per bucket and merges them for the Highlights.
Verified: 4.148.0.md partitions its 162 PRs as RC1 35 + P3 37 + P2 27 +
P1 63 = 162 (no leftover); 4.150.0.md as Preview 1 = 2; the
4.150.0-unreleased.md delta has no previews and stays a flat 7-PR list.
Keep the page model where it belongs — in the SCRIPT. The script owns ALL
file creation, naming, diff ranges, supersession, cleanup, and PR
bucketing; the AI/skill only polishes prose and, on any anomaly (missing
or unexpected page, bad data), STOPS and reports rather than editing the
script or working around it. The model lives in the script's module +
function docstrings; SKILL.md is trimmed to a "division of responsibility"
plus how to consume the pre-bucketed PR data, instead of duplicating (and
drifting from) the naming rules.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Two related fixes to the shared
versions.json-driven docs pipeline (release notes + API changelogs). Combined here since we're iterating on both together.1. API changelogs: one folder per release line, not per preview (
docs.cake)The unified-versioning refactor (#4171) started passing
--nugetDiffPrerelease=truetodocs-api-diff-pastso the 4.x line — which ships only as previews/rcs until it stabilises — would get changelogs. But the loop still emitted a folder for every enumerated version, so every intermediate preview produced its own directory. The last API Diff run filledbot/api-diff(#4173) with4.147.0-preview.{1,2,3},4.148.0-rc.1.2and4.150.0-preview.1.1dirs across every tracked package (~2,300 added files). Historicallychangelogs/held stable versions only.Fix: collapse the feed to one entry per release line (numeric version core, prerelease label stripped:
4.148.0-rc.1.2→4.148.0; a genuine 4-part stable like1.49.2.1keeps its fourth digit) and emit a single rollup changelog per line, diffed against the line's representative package — newest stable if it shipped, otherwise newest prerelease. This mirrors the release-notes pages.A line is emitted when it shipped stable, or when it's an active dev line (preview-only, not superseded, newer than the last stable — e.g.
4.148,4.150). Abandoned preview-only lines (superseded inversions.json, e.g.4.147) and old never-shipped previews are skipped; their changes are absorbed into the successor's cumulative diff. Baselines unchanged:compare_tooverrides win, otherwise the previous emitted line.RunBreakingAndFullDiff(published overload) now takes the changelog folder name separately from the diffed package version, since they differ while a line is in preview.Effect for SkiaSharp:
3.119.4(shipped stable, previously missing),4.148.0,4.150.03.0.0,3.119.3(predatedversions.json)1.53.1.1,1.68.2.1) preservedOnce merged, the API Diff workflow regenerates #4173 with the collapsed set. Validated: Cake dryrun compiles clean; emit rule simulated against the live feed produces exactly the table above (53 cores, zero preview-named dirs).
2. Release notes: drop per-PR effort metric, resolve skia links locally (
generate-release-notes.py)The
--allrun was timing out: the dropped "effort" metric and the companion-skia-PR link resolution each issued one network fetch per PR (162 serial round-trips).git rev-parse <commit>:externals/skiavs parent, batch-fetch only the bumped SHAs (blobless + shallow) intoexternals/skia, and read each skia commit subject to parse the companionmono/skia#NPR. No per-PR network calls.Result: companion links for 22/23 skia-bumping PRs in 4.148 (the one miss is an anomalous backwards gitlink with no PR), and a cold full
--allrun drops to ~35s.Supersedes #4176 (release-notes change folded into this PR).
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com