diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 1c855ccd7d..d37b645ce6 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -29,6 +29,11 @@ "repo": "github/gh-aw-actions/setup", "version": "v0.65.6", "sha": "31130b20a8fd3ef263acbe2091267c0aace07e09" + }, + "github/gh-aw-actions/setup@v0.67.0": { + "repo": "github/gh-aw-actions/setup", + "version": "v0.67.0", + "sha": "cde65c546c2b0f6d3f3a9492a04e6687887c4fe8" } } } diff --git a/.github/skills/editorial-scoring/SKILL.md b/.github/skills/editorial-scoring/SKILL.md index a592f9cd79..6bf6717b37 100644 --- a/.github/skills/editorial-scoring/SKILL.md +++ b/.github/skills/editorial-scoring/SKILL.md @@ -61,6 +61,7 @@ This is how foundational but initially niche work can still earn a good score. - Build or infrastructure churn - Refactoring with no user-visible effect - VMR sync noise, dependency updates, or repo automation +- Product-boundary mismatch — repo-adjacent IDE/editor/design-time tooling work when the notes are for a different product surface - Highly specialized implementation details that read like internal jargon - Features that only matter after several stacked conditions are true (for example: uses single-file publish **and** cares deeply about startup **and** is willing to do extra training/tuning) - PRs with very thin descriptions where the only concrete signal is an internal runtime/tooling term like cDAC diff --git a/.github/skills/generate-changes/SKILL.md b/.github/skills/generate-changes/SKILL.md index 80ad551a98..145011b805 100644 --- a/.github/skills/generate-changes/SKILL.md +++ b/.github/skills/generate-changes/SKILL.md @@ -2,7 +2,7 @@ name: generate-changes description: > Generate `changes.json` for a .NET release milestone by selecting the correct - VMR base/head refs and running `release-notes-gen generate changes`. Handles + VMR base/head refs and running `release-notes generate changes`. Handles preview-only multi-branch targeting (`main` vs release branches vs tags) and emits the authoritative manifest of what shipped. DO NOT USE FOR: API verification/diffs (use api-diff), feature scoring (use generate-features), @@ -17,7 +17,7 @@ This is the **VMR-aware data acquisition stage** of the release notes pipeline: 1. Determine which milestone(s) are active 2. Resolve the correct `--base` and `--head` refs -3. Run `release-notes-gen generate changes` +3. Run `release-notes generate changes` 4. Write `changes.json` into the correct `release-notes/` folder ## When to use @@ -90,7 +90,7 @@ For each active milestone: ### 4. Generate the file ```bash -release-notes-gen generate changes \ +release-notes generate changes \ --base \ --head \ --version "" \ @@ -103,7 +103,7 @@ Examples: ```bash # Preview milestone -release-notes-gen generate changes ~/git/dotnet \ +release-notes generate changes ~/git/dotnet \ --base v11.0.0-preview.3.26210.100 \ --head origin/release/11.0.1xx-preview4 \ --version "11.0.0-preview.4" \ @@ -111,7 +111,7 @@ release-notes-gen generate changes ~/git/dotnet \ --output release-notes/11.0/preview/preview4/changes.json # GA/patch milestone -release-notes-gen generate changes ~/git/dotnet \ +release-notes generate changes ~/git/dotnet \ --base v10.0.7 \ --head v10.0.8 \ --version "10.0.8" \ diff --git a/.github/skills/generate-features/SKILL.md b/.github/skills/generate-features/SKILL.md index d11bfe8c50..04675f7e77 100644 --- a/.github/skills/generate-features/SKILL.md +++ b/.github/skills/generate-features/SKILL.md @@ -37,6 +37,8 @@ The only additions are **optional enrichment fields** on change entries, such as - `score_reason` (`string`) — short explanation of the score - `score_breakdown` (`object`) — optional per-dimension scoring details - `breaking_changes` (`bool`) — mark changes that users may need to react to, even when they are not headline items +- `reverted_by` (`array`) — optional PR URLs or refs that later backed out this change +- `reverts` (`array`) — optional change IDs or PR URLs that this entry reverts or partially reverts This schema is intentionally loose and can grow as the workflow learns what it needs. @@ -65,7 +67,36 @@ Apply the **80/20 rule** from that document: prefer features that make sense to Do **not** invent features from memory, roadmaps, or PR titles found elsewhere. Everything in `features.json` must trace back to `changes.json`. -### 2. Score what shipped +### 2. Resolve revert state before scoring + +Do a quick mechanical revert pass before assigning any positive scores. This step +exists because the original PR may appear in the current `changes.json` while the +revert lands in a later preview and therefore does **not** appear in the same file. + +Start with the obvious cases: + +```bash +jq -r '.changes[] | select(.title | test("(?i)^(partial(ly)?\\s+)?revert\\b|\\bback out\\b")) | [.repo, .title, .url] | @tsv' changes.json +``` + +Then, for each candidate you might promote into the draft, search the source repo +for later merged PRs that explicitly revert or back out that PR number or URL: + +```bash +gh search prs --repo dotnet/ --state merged \ + "\"This reverts https://github.com/dotnet//pull/\" OR \"revert \" OR \"back out \"" \ + --json number,title,mergedAt,url +``` + +When a revert is found: + +- annotate the original entry with `reverted_by` +- annotate the revert entry with `reverts`, when both are present in the file +- set the original score to `0` unless you can verify the shipped build still contains the feature + +Do **not** promote an item to section-worthy status until this pass is complete. + +### 3. Score what shipped Look for signals such as: @@ -85,14 +116,30 @@ Down-rank or exclude: - infra and dependency churn - test-only changes - internal refactors with no user-facing impact +- items that violate the product-boundary rule in `editorial-rules.md` - reverts and partial work that did not survive into the build - items that mostly require insider knowledge to understand why they matter -### 3. Use API evidence to refine the score +### 4. Use API evidence to refine the score If a change depends on public APIs, use `api-diff` / `dotnet-inspect` to confirm the API exists in the actual build. Missing or reverted APIs should be scored down or excluded. -### 4. Write `features.json` +### 5. Merge incrementally when `features.json` already exists + +If the milestone branch already has a `features.json`, do **not** throw that +work away just because `changes.json` was regenerated. For the full rerun +workflow, follow [`update-existing-branch`](../update-existing-branch/SKILL.md). + +Use the existing file as the editorial baseline: + +- preserve prior scores and annotations for unchanged entries +- score only the newly added or materially changed IDs +- revisit older scores only when new evidence or review feedback justifies it +- keep useful human edits instead of flattening them back to a machine pass + +This should feel like a **merge** operation, not a full restart. + +### 6. Write `features.json` The output typically lives next to `changes.json`: diff --git a/.github/skills/release-notes/SKILL.md b/.github/skills/release-notes/SKILL.md index a0728c2dfb..34d360eb10 100644 --- a/.github/skills/release-notes/SKILL.md +++ b/.github/skills/release-notes/SKILL.md @@ -1,7 +1,7 @@ --- name: release-notes -description: Generate and maintain .NET release notes from `features.json`. Uses `generate-changes` for authoritative shipped-change data, `generate-features` for scoring/triage, `editorial-scoring` for the shared rubric, `api-diff`/`dotnet-inspect` for API verification, and a multi-model `review-release-notes` pass for final editorial QA. -compatibility: Requires GitHub MCP server or gh CLI for cross-repo queries. Pairs with the generate-changes, generate-features, editorial-scoring, api-diff, and review-release-notes skills. Claude Opus 4.6 is the default workflow model; the preferred final reviewer pair is Claude Opus 4.6 + GPT-5.4 for broader editorial feedback. +description: Generate and maintain .NET release notes from `features.json`. Uses `generate-changes` for authoritative shipped-change data, `generate-features` for scoring/triage, `update-existing-branch` for incremental reruns on populated branches, `editorial-scoring` for the shared rubric, `api-diff`/`dotnet-inspect` for API verification, and a multi-model `review-release-notes` pass for final editorial QA. +compatibility: Requires GitHub MCP server or gh CLI for cross-repo queries. Pairs with the generate-changes, generate-features, update-existing-branch, editorial-scoring, api-diff, and review-release-notes skills. Claude Opus 4.6 is the default workflow model; the preferred final reviewer pair is Claude Opus 4.6 + GPT-5.4 for broader editorial feedback. --- # .NET Release Notes @@ -13,16 +13,26 @@ This skill is the **editorial writing stage** of the pipeline. It turns a scored ## How it works 1. `generate-changes` diffs `source-manifest.json` between VMR refs to produce `changes.json` -2. `generate-features` reads `changes.json` and emits `features.json` with optional scores using the shared `editorial-scoring` rubric -3. `api-diff` / `dotnet-inspect` verifies public APIs and catches missed reverts -4. `release-notes` writes curated markdown using the higher-value entries from `features.json` -5. `review-release-notes` runs a final multi-model editorial QA pass against the scoring rubric and examples -6. Output is one PR per release milestone in dotnet/core, maintained incrementally +2. `generate-features` reads `changes.json`, resolves revert/backout relationships, and emits `features.json` with optional scores using the shared `editorial-scoring` rubric +3. `update-existing-branch` handles incremental reruns when a milestone branch already exists, merging deltas instead of restarting from scratch +4. `api-diff` / `dotnet-inspect` verifies public APIs and confirms suspect features still exist in the shipped build +5. `release-notes` writes curated markdown using the higher-value entries from `features.json` +6. `review-release-notes` runs a final multi-model editorial QA pass against the scoring rubric and examples +7. Output is one PR per release milestone in dotnet/core, maintained incrementally + +## Existing-branch reruns + +When the milestone branch already exists and contains drafted markdown, invoke +[`update-existing-branch`](../update-existing-branch/SKILL.md). That shared +skill is the canonical playbook for refreshing `changes.json`, merging the delta +into `features.json`, integrating new material into existing sections, and +handling review comments without clobbering human edits. ## Reference documents - [quality-bar.md](references/quality-bar.md) — what good release notes look like - [vmr-structure.md](references/vmr-structure.md) — VMR branches, tags, source-manifest.json +- [../update-existing-branch/SKILL.md](../update-existing-branch/SKILL.md) — how to refresh a populated milestone branch incrementally - [changes-schema.md](references/changes-schema.md) — the shared `changes.json` / `features.json` schema - [../editorial-scoring/SKILL.md](../editorial-scoring/SKILL.md) — the reusable scoring rubric and cut guidance - [feature-scoring.md](references/feature-scoring.md) — how to score and cut features diff --git a/.github/skills/release-notes/references/api-verification.md b/.github/skills/release-notes/references/api-verification.md index 4d2a480b49..3aee81934f 100644 --- a/.github/skills/release-notes/references/api-verification.md +++ b/.github/skills/release-notes/references/api-verification.md @@ -30,10 +30,10 @@ When writing release notes for Preview N, the locally installed SDK is often Pre ### Step 1: Generate build metadata -`release-notes-gen generate build-metadata` reads the VMR's version info and queries the nightly NuGet feed to find the correct package versions: +`release-notes generate build-metadata` reads the VMR's version info and queries the nightly NuGet feed to find the correct package versions: ```bash -release-notes-gen generate build-metadata ~/git/dotnet \ +release-notes generate build-metadata ~/git/dotnet \ --base v11.0.0-preview.2.26159.112 \ --head origin/release/11.0.1xx-preview3 \ --output build-metadata.json @@ -122,7 +122,7 @@ If `dotnet-inspect` can't find a type: 1. **Check your package version first** — are you querying a Preview N-1 package while writing Preview N notes? Find the correct version (see above). 2. **Named differently** — search with a broader pattern (`find "*Zstd*"` instead of the exact name). -3. **Reverted** — check `changes.json` for a revert PR. If the API was added and then reverted in the same preview, it didn't ship. Don't document it. +3. **Reverted or backed out** — first scan `changes.json` for revert titles, then search GitHub for a later merged PR that references reverting the original PR number or URL. The revert may live in the next preview and therefore be absent from the current `changes.json`. If the API or behavior is gone in the final build, don't document the original feature. 4. **Internal** — the type may not be public. Don't document internal types in release notes. 5. **Read the PR tests** — the PR's test files are ground truth. Tests compile and run against the actual API surface. Derive code samples from test assertions rather than guessing type names. diff --git a/.github/skills/release-notes/references/changes-schema.md b/.github/skills/release-notes/references/changes-schema.md index 4168fe9ac2..a311f3db81 100644 --- a/.github/skills/release-notes/references/changes-schema.md +++ b/.github/skills/release-notes/references/changes-schema.md @@ -1,6 +1,6 @@ # Shared Schema for `changes.json` and `features.json` -Reference for the `changes.json` file produced by `release-notes-gen generate changes`, and for the derived `features.json` file used by downstream editorial skills. One file per release milestone. +Reference for the `changes.json` file produced by `release-notes generate changes`, and for the derived `features.json` file used by downstream editorial skills. One file per release milestone. ## Overview @@ -83,6 +83,8 @@ writing stage to keep long-running features consistent across the release. - `score_reason` (`string`) — optional short explanation for the score - `score_breakdown` (`object`) — optional structured scoring details - `breaking_changes` (`bool`) — optional flag for changes users may need to react to, even if they are not headline features +- `reverted_by` (`array`) — optional list of PR URLs or refs that later reverted or backed out this change +- `reverts` (`array`) — optional list of change IDs or PR URLs this entry reverts or partially reverts The `product` field is derived from the repo-level [component mapping](component-mapping.md). Infra repos like `arcade` and `symreader` have no `product` field. The `repo` field always matches the VMR manifest path. @@ -100,7 +102,7 @@ The `commit` field is the VMR codeflow commit in `dotnet/dotnet` that synced thi ## Conventions -- **No nulls** — required fields are always present. Optional fields (`product`, `labels`, `local_repo_commit`, `score`, `score_reason`, `score_breakdown`, `breaking_changes`) may be absent. Use `""` for missing strings, `0` for missing integers. +- **No nulls** — required fields are always present. Optional fields (`product`, `labels`, `local_repo_commit`, `score`, `score_reason`, `score_breakdown`, `breaking_changes`, `reverted_by`, `reverts`) may be absent. Use `""` for missing strings, `0` for missing integers. - **Naming** — `snake_case_lower` for JSON fields, `kebab-case-lower` for file names and repo slugs. - **Public URLs only** — every URL must resolve publicly. - **Commit URLs use `.diff` form** — for machine consumption. @@ -119,6 +121,15 @@ That means a change can be: In practice, a `score` around `0-4` with `breaking_changes: true` usually means **one line in a "Breaking changes" section**, not a full feature writeup. +## Revert annotations + +`changes.json` is the raw shipped-change manifest. During editorial triage, +`features.json` may add `reverted_by` and `reverts` when the agent finds that a +candidate feature was later backed out or partially reverted. + +These annotations exist so downstream skills can suppress the original headline +without losing the provenance that explains why the item was cut. + ## Example ```json diff --git a/.github/skills/release-notes/references/component-mapping.md b/.github/skills/release-notes/references/component-mapping.md index 70204ae09b..1dba1d66df 100644 --- a/.github/skills/release-notes/references/component-mapping.md +++ b/.github/skills/release-notes/references/component-mapping.md @@ -36,6 +36,7 @@ The `runtime` manifest entry covers both Libraries and Runtime. When writing mar - **Razor → ASP.NET Core** — `dotnet/razor` PRs go in `aspnetcore.md` - **Templating → SDK** — `dotnet/templating` PRs go in `sdk.md` - **Roslyn** — covers both C# and Visual Basic. Check PR labels/titles to determine language. Produce `csharp.md` (and `visualbasic.md` if VB-specific features exist). +- **Apply the product-boundary rule** — Razor editor code actions, language-server behavior, and other IDE-only experiences are usually tooling stories, not ASP.NET Core product notes. See `editorial-rules.md`. ### Infrastructure components (skip for release notes) diff --git a/.github/skills/release-notes/references/editorial-rules.md b/.github/skills/release-notes/references/editorial-rules.md index 126418088f..2913fbe905 100644 --- a/.github/skills/release-notes/references/editorial-rules.md +++ b/.github/skills/release-notes/references/editorial-rules.md @@ -64,6 +64,7 @@ Tone, attribution, and content guidelines for .NET release notes. - **Shared audience filter** — apply the 80/20 rule from `editorial-scoring`; don't redefine a competing threshold here. Keep narrower items only when the broader audience can still see why they matter. - **The two-sentence test** — if you can only write two sentences about a feature, it's probably an engineering fix, not a feature. Cut it. A community contribution or breaking change can lift a borderline entry, but "fixed an internal bug that happened to be visible" is not a feature. - **Headlines should convey value** — a heading like "GC regions on macOS" doesn't tell the reader whether this is good or bad. Prefer headings that hint at the benefit: "GC regions enabled on macOS" or "Server GC memory model now available on macOS." +- **Product-boundary rule** — exclude higher-level IDE, editor, or design-time tooling features from product notes unless the release is specifically about that tooling surface. For example, a Razor editor code action should not be presented as an ASP.NET Core feature just because it is adjacent to the web stack. - **TODO for borderline entries** — when a feature might deserve inclusion but you lack data to justify it (benchmark numbers, real-world impact, user demand), keep the entry but add an HTML `` comment asking for the missing information. This is better than silently including a vague claim or silently cutting something that might matter. The TODO should state what's needed and link to the PR where the data might live. - **Breaking changes are separate from hype** — a breaking change can be important even when it is not exciting. Keep the score honest; use `breaking_changes: true` to preserve a short callout instead of inflating the item into a headline feature. - **Clusters can be stronger than the parts** — several related low-score items can justify one section when together they tell a clear story. Keep the individual scores honest, then merge them into one writeup instead of emitting several weak mini-features. Good examples include a group of "Unsafe evolution" changes or multiple runtime entries prefixed with `[browser]`. @@ -90,7 +91,7 @@ Thank you [@username](https://github.com/username) for this contribution! ### Community contributors section -At the bottom of each component's notes, list ALL external contributors — not just those with documented features. Use the `community-contribution` label to identify them. +At the bottom of each component's notes, list ALL external contributors — not just those with documented features. Any community contributor with **one or more merged PRs** in the milestone should get a mention exactly once in the **Community contributors** section, even if none of their work was promoted into a feature writeup. Use the `community-contribution` label as a strong signal, but do not rely on it exclusively if the merged PR history shows a clear external contribution. **Vet the list** — the `community-contribution` label is sometimes wrong. Exclude usernames containing `-msft`, `-microsoft`, or other Microsoft suffixes. When in doubt about whether someone is a Microsoft employee, leave them out of the community list. diff --git a/.github/skills/release-notes/references/feature-scoring.md b/.github/skills/release-notes/references/feature-scoring.md index 83b7f3353e..2bd23853a9 100644 --- a/.github/skills/release-notes/references/feature-scoring.md +++ b/.github/skills/release-notes/references/feature-scoring.md @@ -25,6 +25,7 @@ matter during .NET release-note triage. - Test-only or infra-only work - Mechanical refactors with no user-facing impact - Churn in dependencies or tooling internals +- **Product-boundary mismatch** — apply the product-boundary rule from `editorial-rules.md`; score these to cut-level unless the release is explicitly about that tooling surface - Reverts, partial implementations, or APIs that do not appear in the actual build - Changes too small or obscure to justify external attention - **Stacked audience gates** — if the reader has to care about A, then B, then be willing to do C, the addressable audience shrinks at each step and the score should usually drop hard diff --git a/.github/skills/release-notes/references/quality-bar.md b/.github/skills/release-notes/references/quality-bar.md index 5920c05076..bd40edcd10 100644 --- a/.github/skills/release-notes/references/quality-bar.md +++ b/.github/skills/release-notes/references/quality-bar.md @@ -6,7 +6,7 @@ What good .NET release notes look like. This is the north star — when in doubt ### changes.json — comprehensive and mechanical -Every PR that shipped gets an entry. No editorial judgment — if the `release-notes-gen generate changes` tool found it in the source-manifest diff, it goes in. This is the machine-readable record of what shipped. +Every PR that shipped gets an entry. No editorial judgment — if the `release-notes generate changes` tool found it in the source-manifest diff, it goes in. This is the machine-readable record of what shipped. Quality criteria: diff --git a/.github/skills/release-notes/references/vmr-structure.md b/.github/skills/release-notes/references/vmr-structure.md index c583885f66..4292fd55df 100644 --- a/.github/skills/release-notes/references/vmr-structure.md +++ b/.github/skills/release-notes/references/vmr-structure.md @@ -36,7 +36,7 @@ By comparing `source-manifest.json` at two release points, you get: 3. **Which components didn't change** — these get minimal stubs, no investigation needed 4. **Source repo URLs** — for querying PRs via the GitHub compare API -The `release-notes-gen generate changes` tool automates this: it fetches the manifest at both refs, diffs them, queries GitHub for PRs in each commit range, and outputs `changes.json`. +The `release-notes generate changes` tool automates this: it fetches the manifest at both refs, diffs them, queries GitHub for PRs in each commit range, and outputs `changes.json`. ## Branch naming diff --git a/.github/skills/review-release-notes/SKILL.md b/.github/skills/review-release-notes/SKILL.md index 6cdfda1b79..9af819e106 100644 --- a/.github/skills/review-release-notes/SKILL.md +++ b/.github/skills/review-release-notes/SKILL.md @@ -66,6 +66,7 @@ Compare the draft against the component examples: - **Under-explaining** — a real `8+` feature is present but buried or not framed clearly - **API inventory mode** — the draft starts mirroring `api-diff` instead of telling a user story - **Technical novelty bias** — clever implementation details outrank practical user value +- **Missed revert** — a promoted feature was later backed out or does not appear in the actual build ## Multi-model review pattern @@ -94,13 +95,14 @@ specific checks instead: 2. **Which sections fail the 80/20 reader-value test and should be cut, grouped, or demoted?** 3. **Which sentences infer feelings or outcomes (`trust`, `confidence`, `easier`, `better`) instead of stating the concrete change?** 4. **Which sections drift into API-inventory mode instead of teaching a user-facing story?** -5. **Which code samples or examples are weak, confusing, or unsupported by the text?** -6. **Which links, issue/PR references, or formatting details still violate house style?** -7. **What is the single highest-value rewrite still needed in the draft?** -8. **Is the wording conventional, or is it inventing non-standard phrasing or terms?** -9. **Are the subject and its adjective or adverb paired in a familiar way?** -10. **Would this phrasing seem normal in release notes for another developer platform?** -11. **If `release-notes/features.json` lists this feature, does the section begin with the standard preview blockquote?** +5. **Which promoted items were later reverted, backed out, or are missing from the actual build/package set? Cite the revert PR or verification gap.** +6. **Which code samples or examples are weak, confusing, or unsupported by the text?** +7. **Which links, issue/PR references, or formatting details still violate house style?** +8. **What is the single highest-value rewrite still needed in the draft?** +9. **Is the wording conventional, or is it inventing non-standard phrasing or terms?** +10. **Are the subject and its adjective or adverb paired in a familiar way?** +11. **Would this phrasing seem normal in release notes for another developer platform?** +12. **If `release-notes/features.json` lists this feature, does the section begin with the standard preview blockquote?** Ask reviewers to answer with file + heading + issue + suggested rewrite. This produces actionable review instead of general taste feedback. diff --git a/.github/skills/update-distro-packages/SKILL.md b/.github/skills/update-distro-packages/SKILL.md index 7f151acca7..7075b46401 100644 --- a/.github/skills/update-distro-packages/SKILL.md +++ b/.github/skills/update-distro-packages/SKILL.md @@ -15,7 +15,7 @@ Create and maintain per-distro JSON files in `release-notes/{version}/distros/`. ## Directory structure -``` +```text release-notes/{version}/distros/ ├── dependencies.json # distro-agnostic dependency list (what .NET needs) ├── index.json # lists all per-distro file names @@ -34,24 +34,24 @@ release-notes/{version}/distros/ ## Prerequisites -The `release-notes-gen` tool must be installed for markdown generation and package availability queries. The public `dotnet-release` tool is now for browsing release data and CVEs. +The `release-notes` tool must be installed for markdown generation and package availability queries. The public `dotnet-release` tool is now for browsing release data and CVEs. ```bash # Check if already installed -release-notes-gen --version +release-notes --version ``` If not installed: ```bash -dotnet tool install -g ReleaseNotes.Gen \ +dotnet tool install -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json ``` If already installed, update to latest: ```bash -dotnet tool update -g ReleaseNotes.Gen \ +dotnet tool update -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json ``` @@ -214,7 +214,7 @@ Confirm every Linux distro in `supported-os.json` has a corresponding file. Regenerate `dotnet-dependencies.md` from the JSON files: ```bash -release-notes-gen generate dotnet-dependencies {version} release-notes +release-notes generate dotnet-dependencies {version} release-notes ``` This produces `release-notes/{version}/dotnet-dependencies.md` with copy-pasteable install commands for each distro and release. Never hand-edit this file — it is generated from the JSON. @@ -246,7 +246,7 @@ Requires `PKGS_ORG_TOKEN` to be set. ```bash export PKGS_ORG_TOKEN= -release-notes-gen query distro-packages --dotnet-version {version} --output /tmp/distro-packages.json +release-notes query distro-packages --dotnet-version {version} --output /tmp/distro-packages.json ``` This queries pkgs.org and supplemental feeds (Ubuntu backports via Launchpad, Homebrew, NixOS) and writes a JSON file with package availability per distro. @@ -400,8 +400,8 @@ Show the user a summary of which distros+releases have packages and from which f After any JSON changes, regenerate both markdown files: ```bash -release-notes-gen generate dotnet-dependencies {version} release-notes -release-notes-gen generate dotnet-packages {version} release-notes +release-notes generate dotnet-dependencies {version} release-notes +release-notes generate dotnet-packages {version} release-notes ``` - `dotnet-dependencies.md` — what OS packages .NET requires (from dependency data) @@ -424,7 +424,7 @@ When packages come from a non-builtin feed, the `install_command` field tells us Feed name: `backports` -``` +```bash sudo add-apt-repository ppa:dotnet/backports && sudo apt-get update ``` @@ -434,7 +434,7 @@ This PPA provides .NET packages for older Ubuntu LTS releases that don't carry . Feed name: `microsoft` -``` +```bash wget https://packages.microsoft.com/config/{distro}/{version}/packages-microsoft-prod.deb -O /tmp/packages-microsoft-prod.deb && sudo dpkg -i /tmp/packages-microsoft-prod.deb && rm /tmp/packages-microsoft-prod.deb ``` diff --git a/.github/skills/update-existing-branch/SKILL.md b/.github/skills/update-existing-branch/SKILL.md new file mode 100644 index 0000000000..5dc6e5cbe6 --- /dev/null +++ b/.github/skills/update-existing-branch/SKILL.md @@ -0,0 +1,127 @@ +--- +name: update-existing-branch +description: > + Refresh an existing .NET release-notes milestone branch or PR incrementally. + Checks whether the VMR ref moved, regenerates `changes.json` only when needed, + merges the delta into `features.json`, integrates new material into existing + markdown clusters, and responds to review feedback. USE FOR: reruns on a + populated release-notes branch. DO NOT USE FOR: first-pass generation of a new + milestone (use generate-changes, generate-features, and release-notes). +--- + +# Update Existing Branch + +Use this skill when the milestone branch already exists and contains drafted +markdown, `features.json`, reviewer comments, or human edits. + +This is the **incremental rerun** stage of the release-notes pipeline. It is the +branch-maintenance equivalent of what `editorial-scoring` is for scoring: the +canonical place to describe how follow-up runs should behave. + +## Purpose + +Treat the existing branch as the **working baseline**, not as a blank slate. + +The goal is to: + +1. refresh shipped-change data only when the preview actually moved forward +2. score and write the **delta**, not the whole release again +3. preserve human edits and reviewer-driven rewrites +4. integrate new material into the current story cleanly + +## Inputs + +Read these before making changes: + +1. the current milestone branch and PR +2. the existing `changes.json`, `features.json`, and markdown files +3. `build-metadata.json`, if present +4. unresolved PR review comments and discussion threads + +## Process + +### 1. Check whether the preview moved forward + +Determine whether later `dotnet/dotnet` codeflow commits landed for the same +preview branch or tag since the last run. + +If the relevant VMR ref moved forward, regenerate `changes.json`. If it did not, +keep the current file and focus on editorial fixes and comment responses. + +Typical signals: + +- later commits on `dotnet/dotnet` for `release/.0.1xx-previewN` +- a newer release-branch head than the one captured in `build-metadata.json` +- new source-manifest commits for a component already discussed in the draft + +### 2. Compute a delta from the existing files + +If you refreshed `changes.json`, compare the old and new files by stable `id`. + +Classify changes as: + +- **added** — new shipped items not seen in the last run +- **changed** — same `id`, but new evidence, labels, revert state, or reviewer context changes the treatment +- **removed or superseded** — no longer relevant to the current build story, or later evidence shows the original interpretation was wrong +- **unchanged** — keep the prior editorial work + +Do **not** treat a refreshed `changes.json` as permission to restart the whole +release from zero. + +### 3. Merge into the existing `features.json` + +When `features.json` already exists, use it as the editorial baseline and merge +the delta into it. + +Preserve prior annotations for unchanged entries, including: + +- `score` +- `score_reason` +- `score_breakdown` +- `breaking_changes` +- `reverted_by` / `reverts` +- useful human notes or hand-tuned annotations + +Only rescore: + +- newly added entries +- materially changed entries +- earlier entries that reviewers explicitly questioned or that new evidence disproved + +This should feel like a **delta merge**, not a full rescore. + +### 4. Update markdown in place + +Use the current draft as the starting point. Prefer **integration** over +duplication. + +Examples: + +- add a new GC item into an existing **Garbage Collection Performance Improvements** section +- extend an existing **Unsafe Evolution** block instead of creating a second heading +- move a newly demoted item into **Bug fixes** instead of deleting the story without explanation + +Keep the existing structure when it still works. Add a new top-level section +only when the delta introduces a genuinely new story. + +### 5. Treat review comments as required inputs + +Unresolved PR comments are part of the spec for the next run. + +- read them before rewriting anything +- answer factual questions with evidence from `changes.json`, VMR refs, API verification, or the final build +- update the markdown when the reviewer is right +- if the reviewer is directionally right but the current build still contains the feature, explain the milestone and branch context clearly + +### 6. Default outcome + +For an existing release-notes branch, the normal loop is: + +1. refresh `changes.json` only if the preview moved forward +2. merge the delta into `features.json` +3. update the existing markdown in place +4. respond to comments and questions +5. push a follow-up commit to the same PR + +This keeps the branch stable for reviewers and avoids throwing away already +curated editorial work. diff --git a/.github/skills/update-os-packages/SKILL.md b/.github/skills/update-os-packages/SKILL.md index 3f1fcea9d8..2d26fc46df 100644 --- a/.github/skills/update-os-packages/SKILL.md +++ b/.github/skills/update-os-packages/SKILL.md @@ -2,7 +2,7 @@ name: update-os-packages description: > Audit and update os-packages.json/md files that document required Linux - packages for each .NET release. Uses the release-notes-gen tool to verify + packages for each .NET release. Uses the release-notes tool to verify package names against distro archives and regenerate markdown. USE FOR: adding packages for new distro versions, fixing incorrect package names, periodic package audits. DO NOT USE FOR: supported-os.json changes (use @@ -26,15 +26,15 @@ The scope of `os-packages.json` is broader than `supported-os.json`. It includes ## Prerequisites -The `release-notes-gen` tool must be installed. The public `dotnet-release` tool is now for browsing release data and CVEs. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). +The `release-notes` tool must be installed. The public `dotnet-release` tool is now for browsing release data and CVEs. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). ```bash # GitHub Packages requires authentication — use a GitHub token (PAT or GITHUB_TOKEN) -dotnet tool install -g ReleaseNotes.Gen \ +dotnet tool install -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json # Verify -release-notes-gen --help +release-notes --help ``` > **Note:** GitHub Packages requires authentication even for public repositories. If you get a 401 error, configure credentials for the source: @@ -63,17 +63,17 @@ The user provides: Run the verify command for each .NET version to audit: ```bash -release-notes-gen verify os-packages release-notes +release-notes verify os-packages release-notes ``` Examples: ```bash # Check 10.0 against local files -release-notes-gen verify os-packages 10.0 release-notes +release-notes verify os-packages 10.0 release-notes # Check against live data on GitHub (no local clone needed) -release-notes-gen verify os-packages 10.0 +release-notes verify os-packages 10.0 ``` **Interpret the exit code:** @@ -164,7 +164,7 @@ Delete the release object from the `releases` array for that distribution. After updating the JSON, regenerate the markdown file: ```bash -release-notes-gen generate os-packages release-notes +release-notes generate os-packages release-notes ``` This overwrites `os-packages.md` with content derived from the updated JSON. @@ -186,7 +186,7 @@ CI runs markdownlint via super-linter. If linting fails, fix the generator or Ma 1. Run verify again to confirm issues are resolved: ```bash - release-notes-gen verify os-packages release-notes + release-notes verify os-packages release-notes ``` Expect exit code 0 (or only skipped distros remaining). diff --git a/.github/skills/update-os-packages/references/verify-output-example.md b/.github/skills/update-os-packages/references/verify-output-example.md index b7a9cbd520..37954a141b 100644 --- a/.github/skills/update-os-packages/references/verify-output-example.md +++ b/.github/skills/update-os-packages/references/verify-output-example.md @@ -1,6 +1,6 @@ # Verify Output Example -Example output from `release-notes-gen verify os-packages 10.0 release-notes`: +Example output from `release-notes verify os-packages 10.0 release-notes`: ````markdown # .NET 10.0 — OS Packages Verification diff --git a/.github/skills/update-release-graph/SKILL.md b/.github/skills/update-release-graph/SKILL.md index 057eea3005..ca83d8ce93 100644 --- a/.github/skills/update-release-graph/SKILL.md +++ b/.github/skills/update-release-graph/SKILL.md @@ -15,7 +15,7 @@ description: > # Update Release Graph -Regenerate the HAL+JSON information graph in `release-notes/`. The graph is a set of interconnected JSON files using [HAL](https://stateless.group/hal_specification.html) `_links` and `_embedded` properties. It is **generated** from source data — never hand-edit the output files. +Regenerate the HAL+JSON information graph in `release-notes/`. The graph is a set of interconnected JSON files using [HAL](https://datatracker.ietf.org/doc/draft-kelly-json-hal/) `_links` and `_embedded` properties. It is **generated** from source data — never hand-edit the output files. ## Architecture @@ -31,7 +31,7 @@ Regenerate the HAL+JSON information graph in `release-notes/`. The graph is a se ### Generated graph (outputs — do not hand-edit) -``` +```text release-notes/ ├── index.json ← root: all major versions ├── llms.json ← AI entry point: latest patches per supported version @@ -60,7 +60,7 @@ release-notes/ ### Graph generators -The `release-notes-gen` tool includes four graph generation commands: +The `release-notes` tool includes four graph generation commands: | Command | Generates | From | |---------|-----------|------| @@ -71,8 +71,8 @@ The `release-notes-gen` tool includes four graph generation commands: All accept the same arguments: -``` -release-notes-gen generate [output-dir] [--url-root ] +```bash +release-notes generate [output-dir] [--url-root ] ``` ## When to use @@ -85,16 +85,16 @@ release-notes-gen generate [output-dir] [--url-root ] ## Prerequisites -### release-notes-gen +### release-notes -The `release-notes-gen` tool handles both graph generation and legacy file operations. The public `dotnet-release` tool is for navigating release data and CVEs. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). +The `release-notes` tool handles both graph generation and legacy file operations. The public `dotnet-release` tool is for navigating release data and CVEs. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). ```bash -dotnet tool install -g ReleaseNotes.Gen \ +dotnet tool install -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json # Verify — should show graph generation commands -release-notes-gen --help +release-notes --help ``` > **Note:** GitHub Packages requires authentication even for public repositories. If you get a 401 error, configure credentials for the source: @@ -156,27 +156,27 @@ The simplest approach is `generate indexes` which runs all three generators in s ```bash # Generate all graph files in one shot -release-notes-gen generate indexes release-notes +release-notes generate indexes release-notes ``` Or run each generator individually (order matters: version-index → timeline-index → llms-index): ```bash -release-notes-gen generate version-index release-notes -release-notes-gen generate timeline-index release-notes -release-notes-gen generate llms-index release-notes +release-notes generate version-index release-notes +release-notes generate timeline-index release-notes +release-notes generate llms-index release-notes ``` **Custom URL root** (for PR review before merging to release-index): ```bash -release-notes-gen generate indexes release-notes --url-root https://raw.githubusercontent.com/dotnet/core/ +release-notes generate indexes release-notes --url-root https://raw.githubusercontent.com/dotnet/core/ ``` **Separate output directory** (to inspect output without overwriting source): ```bash -release-notes-gen generate indexes release-notes /tmp/graph-output +release-notes generate indexes release-notes /tmp/graph-output ``` The default URL root is `https://raw.githubusercontent.com/dotnet/core/refs/heads/release-index/release-notes/`. @@ -185,27 +185,28 @@ The default URL root is `https://raw.githubusercontent.com/dotnet/core/refs/head ```bash # Regenerate releases-index.json from releases.json files -release-notes-gen generate releases-index release-notes +release-notes generate releases-index release-notes # Regenerate releases.md -release-notes-gen generate releases release-notes +release-notes generate releases release-notes ``` ### 4. Validate ```bash # Verify release links and hashes (can take minutes — do not cancel) -release-notes-gen verify releases release-notes +release-notes verify releases release-notes # Or for a specific version -release-notes-gen verify releases {ver} release-notes +release-notes verify releases {ver} release-notes # Skip hash verification for faster iteration -release-notes-gen verify releases release-notes --skip-hash +release-notes verify releases release-notes --skip-hash # Lint generated markdown npx markdownlint --config .github/linters/.markdown-lint.yml release-notes/releases.md ``` **Exit codes for verify:** + - `0` — no issues - `2` — issues found (report written to stdout as markdown) @@ -290,7 +291,7 @@ Create the `release.json` in the patch directory. ### 2. Regenerate -Same as the patch release process — run `release-notes-gen generate indexes release-notes` → legacy files. +Same as the patch release process — run `release-notes generate indexes release-notes` → legacy files. ### 3. Verify the new version appears @@ -303,12 +304,14 @@ Same as the patch release process — run `release-notes-gen generate indexes re ### 1. Update source data Edit `release-notes/{ver}/_manifest.json`: + - Set `"support_phase": "eol"` - Set `"supported": false` (if present) -### 2. Regenerate +### 2. Regenerate outputs Run all generators. The tools will: + - Set `supported: false` in root index - Remove from `llms.json` `supported_major_releases` and `_embedded.patches` - Update `support_phase` in major index @@ -328,6 +331,7 @@ The `_links` section contains reference links that are merged into the generated Contains the full release data for a single patch: component versions, download URLs, file hashes. This is a subset of the corresponding entry in `releases.json`. The generators use `release.json` to: + - Build patch detail indexes with embedded runtime/SDK data - Generate per-RID download files - Compute SDK feature band information @@ -335,6 +339,7 @@ The generators use `release.json` to: ### Preview releases Preview/RC releases are stored in a different directory structure: + - `{ver}/preview/preview1/` for preview.1 - `{ver}/preview/rc1/` for RC1 @@ -375,8 +380,8 @@ Immutable files use only `prev-*` links (no `next`). Mutable files use `latest-* - Never hand-edit generated files (`index.json`, `manifest.json`, `llms.json`, downloads files, timeline indexes) - `_manifest.json` (with underscore prefix) is the source; `manifest.json` (without prefix) is the generated output - Similarly, `_llms.json` is optional source overrides; `llms.json` is the generated output -- `releases-index.json` (legacy flat format) is generated by `release-notes-gen generate releases-index` -- `releases.md` is generated by `release-notes-gen generate releases` +- `releases-index.json` (legacy flat format) is generated by `release-notes generate releases-index` +- `releases.md` is generated by `release-notes generate releases` - The generators are idempotent — running them on unchanged source data produces identical output - When running individually, order matters: `version-index` → `timeline-index` → `llms-index`; `generate indexes` handles this automatically - All `_links.*.href` values are absolute URLs; the base URL is controlled by `--url-root` @@ -389,10 +394,10 @@ Immutable files use only `prev-*` links (no `next`). Mutable files use `latest-* |---------|------------| | Hand-editing `index.json` or other generated files | Edit source data and re-run the generators | | Hand-editing `manifest.json` | Edit `_manifest.json` and re-run `VersionIndex` | -| Hand-editing `releases-index.json` | Run `release-notes-gen generate releases-index release-notes` | -| Hand-editing `releases.md` | Run `release-notes-gen generate releases release-notes` | +| Hand-editing `releases-index.json` | Run `release-notes generate releases-index release-notes` | +| Hand-editing `releases.md` | Run `release-notes generate releases release-notes` | | Running generators in wrong order | Use `generate indexes` (handles order automatically) or run: version-index → timeline-index → llms-index | | Missing `_manifest.json` for a version | The generators warn but fall back to `releases.json`; create `_manifest.json` for accurate lifecycle data | | Missing `release.json` for a patch | Patch detail index will be incomplete; ensure every patch has a `release.json` | -| Editing source data without regenerating | Always run `release-notes-gen generate indexes release-notes` after changing source files | +| Editing source data without regenerating | Always run `release-notes generate indexes release-notes` after changing source files | | Forgetting `--url-root` for PR review | Links will use the default release-index branch URL; pass `--url-root` with a commit SHA for verifiable links in PRs | diff --git a/.github/skills/update-supported-os/SKILL.md b/.github/skills/update-supported-os/SKILL.md index b6c4c4556f..dc6b0db3e8 100644 --- a/.github/skills/update-supported-os/SKILL.md +++ b/.github/skills/update-supported-os/SKILL.md @@ -2,7 +2,7 @@ name: update-supported-os description: > Audit and update supported-os.json/md files to reflect current OS version - support. Uses the release-notes-gen tool for automated verification against + support. Uses the release-notes tool for automated verification against upstream lifecycle data and markdown regeneration. USE FOR: adding new OS versions, moving EOL versions to unsupported, periodic support matrix audits. DO NOT USE FOR: os-packages.json changes (use update-os-packages skill), @@ -23,17 +23,17 @@ Audit and update `supported-os.json` files in this repository. These files decla The following tools must be installed: -### release-notes-gen +### release-notes -The `release-notes-gen` tool is used to verify and generate supported OS files. The public `dotnet-release` tool is now for browsing release data and CVEs. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). +The `release-notes` tool is used to verify and generate supported OS files. The public `dotnet-release` tool is now for browsing release data and CVEs. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). ```bash # GitHub Packages requires authentication — use a GitHub token (PAT or GITHUB_TOKEN) -dotnet tool install -g ReleaseNotes.Gen \ +dotnet tool install -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json # Verify -release-notes-gen --help +release-notes --help ``` > **Note:** GitHub Packages requires authentication even for public repositories. If you get a 401 error, configure credentials for the source: @@ -75,17 +75,17 @@ The user provides: Run the verify command for each .NET version to audit: ```bash -release-notes-gen verify supported-os release-notes +release-notes verify supported-os release-notes ``` Examples: ```bash # Check 10.0 against local files -release-notes-gen verify supported-os 10.0 release-notes +release-notes verify supported-os 10.0 release-notes # Check against live data on GitHub (no local clone needed) -release-notes-gen verify supported-os 10.0 +release-notes verify supported-os 10.0 ``` **Interpret the exit code:** @@ -145,7 +145,7 @@ For each confirmed change, edit `release-notes//supported-os.json`: After updating the JSON, regenerate the markdown file: ```bash -release-notes-gen generate supported-os release-notes +release-notes generate supported-os release-notes ``` This overwrites `supported-os.md` with content derived from the updated JSON. @@ -171,7 +171,7 @@ Check if any newly added distro versions need entries in `os-packages.json`. If 1. Run verify again to confirm issues are resolved: ```bash - release-notes-gen verify supported-os release-notes + release-notes verify supported-os release-notes ``` Remaining items are acceptable if they are: diff --git a/.github/skills/verify-releases/SKILL.md b/.github/skills/verify-releases/SKILL.md index 759f43fcee..79429fb545 100644 --- a/.github/skills/verify-releases/SKILL.md +++ b/.github/skills/verify-releases/SKILL.md @@ -2,7 +2,7 @@ name: verify-releases description: > Validate releases and release links: URL liveness, file hashes, CDN - latest.version files, and aka.ms redirect targets. Uses release-notes-gen verify + latest.version files, and aka.ms redirect targets. Uses release-notes verify and generate commands against the local release-notes directory. USE FOR: validate the latest release, validate release links, validating that all download links return HTTP 200, verifying SHA512 hashes match downloaded @@ -16,21 +16,21 @@ description: > # Verify Releases -Validate .NET release data in `release-notes/` using the `release-notes-gen` CLI tool. This skill checks that download URLs are live, file hashes match, CDN latest.version files are current, and aka.ms redirects resolve correctly. +Validate .NET release data in `release-notes/` using the `release-notes` CLI tool. This skill checks that download URLs are live, file hashes match, CDN latest.version files are current, and aka.ms redirects resolve correctly. ## Prerequisites -### release-notes-gen +### release-notes -The `release-notes-gen` tool is published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). The public `dotnet-release` tool is now for browsing release data and CVEs. +The `release-notes` tool is published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages). The public `dotnet-release` tool is now for browsing release data and CVEs. ```bash # Install -dotnet tool install -g ReleaseNotes.Gen \ +dotnet tool install -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json # Verify — must show "verify releases" in usage output -release-notes-gen +release-notes ``` > **Note:** GitHub Packages requires authentication even for public repositories. If you get a 401 error, configure credentials: @@ -46,7 +46,7 @@ release-notes-gen **Version check:** If the tool usage output does not include `verify releases` in its command list, the installed version is too old. Update with: ```bash -dotnet tool update -g ReleaseNotes.Gen \ +dotnet tool update -g release-notes \ --add-source https://nuget.pkg.github.com/richlander/index.json ``` @@ -57,7 +57,7 @@ dotnet tool update -g ReleaseNotes.Gen \ Downloads every binary and verifies SHA512 hashes against `releases.json`. This is the most thorough check and takes several minutes. ```bash -release-notes-gen verify releases release-notes +release-notes verify releases release-notes ``` ### Verify all supported versions (quick — skip hashes) @@ -65,20 +65,20 @@ release-notes-gen verify releases release-notes Checks URL liveness, CDN latest.version, and aka.ms redirects only. Much faster — typically under 30 seconds. ```bash -release-notes-gen verify releases release-notes --skip-hash +release-notes verify releases release-notes --skip-hash ``` ### Verify a specific major version ```bash -release-notes-gen verify releases 10.0 release-notes -release-notes-gen verify releases 10.0 release-notes --skip-hash +release-notes verify releases 10.0 release-notes +release-notes verify releases 10.0 release-notes --skip-hash ``` ### Verify a specific patch release ```bash -release-notes-gen verify releases 10.0.5 release-notes +release-notes verify releases 10.0.5 release-notes ``` ## What gets verified @@ -112,13 +112,13 @@ release-notes-gen verify releases 10.0.5 release-notes ### 1. Check tool version -Confirm `release-notes-gen` is installed and has the `verify releases` command: +Confirm `release-notes` is installed and has the `verify releases` command: ```bash -release-notes-gen +release-notes ``` -The usage output must include `release-notes-gen verify releases [version] [path] [--skip-hash]`. If it does not, update the tool (see Prerequisites). +The usage output must include `release-notes verify releases [version] [path] [--skip-hash]`. If it does not, update the tool (see Prerequisites). ### 2. Run verification @@ -126,13 +126,13 @@ For a standard validation (recommended for release sign-off): ```bash cd ~/git/core -release-notes-gen verify releases release-notes +release-notes verify releases release-notes ``` For a quick check during development: ```bash -release-notes-gen verify releases release-notes --skip-hash +release-notes verify releases release-notes --skip-hash ``` ### 3. Interpret results @@ -156,8 +156,8 @@ Present a summary table with per-version results: ## .NET Release Link Verification Report **Date:** YYYY-MM-DD -**Tool:** `release-notes-gen` vX.Y.Z -**Command:** `release-notes-gen verify releases release-notes` +**Tool:** `release-notes` vX.Y.Z +**Command:** `release-notes verify releases release-notes` | Version | Latest Release | Download URLs | SHA512 Hashes | CDN latest.version | aka.ms | Status | |---------|---------------|---------------|---------------|-------------------|--------|--------| @@ -177,10 +177,10 @@ After verifying releases, you may also want to regenerate the legacy index and m ```bash # Regenerate releases-index.json -release-notes-gen generate releases-index release-notes +release-notes generate releases-index release-notes # Regenerate releases.md -release-notes-gen generate releases release-notes +release-notes generate releases release-notes # Lint the generated markdown npx markdownlint --config .github/linters/.markdown-lint.yml release-notes/releases.md diff --git a/.github/skills/verify-releases/references/verify-output-example.md b/.github/skills/verify-releases/references/verify-output-example.md index 37ee9a21a0..4264179465 100644 --- a/.github/skills/verify-releases/references/verify-output-example.md +++ b/.github/skills/verify-releases/references/verify-output-example.md @@ -2,9 +2,9 @@ ## Full verification (with hashes) -Command: `release-notes-gen verify releases release-notes` +Command: `release-notes verify releases release-notes` -``` +```text Verifying release links for all supported versions in /Users/rich/git/core/release-notes... Checking .NET 11.0 (11.0.0-preview.2)... 82 URLs to check @@ -64,9 +64,9 @@ Exit code: `0` ## Quick verification (skip hashes) -Command: `release-notes-gen verify releases release-notes --skip-hash` +Command: `release-notes verify releases release-notes --skip-hash` -``` +```text Verifying release links for all supported versions in /Users/rich/git/core/release-notes... Checking .NET 11.0 (11.0.0-preview.2)... 82 URLs to check @@ -115,8 +115,8 @@ Exit code: `0` ## .NET Release Link Verification Report **Date:** 2026-03-27 -**Tool:** `release-notes-gen` v1.1.0 -**Command:** `release-notes-gen verify releases release-notes` +**Tool:** `release-notes` v1.1.0 +**Command:** `release-notes verify releases release-notes` | Version | Latest Release | Download URLs | SHA512 Hashes | CDN latest.version | aka.ms | Status | |---------|---------------|---------------|---------------|-------------------|--------|--------| diff --git a/.github/workflows/release-notes.lock.yml b/.github/workflows/release-notes.lock.yml index ea9478791a..b95125b81c 100644 --- a/.github/workflows/release-notes.lock.yml +++ b/.github/workflows/release-notes.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"76ca2ecd08c26c9b040306bd58ae8f204c075abf017dde36d4d5585adba2b398","compiler_version":"v0.67.1","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"f759ecfe0bc16a7872aee85ce9a248452ac68ea9a587c5cdeaac78b83a07e2f0","compiler_version":"v0.67.0","strict":true,"agent_id":"copilot"} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.67.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.67.0). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -45,7 +45,7 @@ # - actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 # - actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 # - actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 -# - github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 +# - github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 name: "Write Release Notes" "on": @@ -111,7 +111,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 + uses: github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -124,7 +124,7 @@ jobs: GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'auto' }} GH_AW_INFO_VERSION: "latest" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.67.1" + GH_AW_INFO_CLI_VERSION: "v0.67.0" GH_AW_INFO_WORKFLOW_NAME: "Write Release Notes" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -170,7 +170,7 @@ jobs: - name: Check compile-agentic version uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - GH_AW_COMPILED_VERSION: "v0.67.1" + GH_AW_COMPILED_VERSION: "v0.67.0" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -193,20 +193,20 @@ jobs: run: | bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh { - cat << 'GH_AW_PROMPT_a5a3647fd2134272_EOF' + cat << 'GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF' - GH_AW_PROMPT_a5a3647fd2134272_EOF + GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_a5a3647fd2134272_EOF' + cat << 'GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF' Tools: add_comment(max:20), create_pull_request(max:5), push_to_pull_request_branch(max:5), missing_tool, missing_data, noop - GH_AW_PROMPT_a5a3647fd2134272_EOF + GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_a5a3647fd2134272_EOF' + cat << 'GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF' The following GitHub context information is available for this workflow: @@ -236,12 +236,12 @@ jobs: {{/if}} - GH_AW_PROMPT_a5a3647fd2134272_EOF + GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_a5a3647fd2134272_EOF' + cat << 'GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF' {{#runtime-import .github/workflows/release-notes.md}} - GH_AW_PROMPT_a5a3647fd2134272_EOF + GH_AW_PROMPT_8ea76c6c2bd34ce4_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -306,8 +306,6 @@ jobs: path: | /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/github_rate_limits.jsonl - if-no-files-found: ignore retention-days: 1 agent: @@ -338,7 +336,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 + uses: github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -367,14 +365,13 @@ jobs: env: REPO_NAME: ${{ github.repository }} SERVER_URL: ${{ github.server_url }} - GITHUB_TOKEN: ${{ github.token }} run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - name: Checkout PR branch id: checkout-pr @@ -407,18 +404,18 @@ jobs: const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.25.13 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.13 ghcr.io/github/gh-aw-firewall/squid:0.25.13 ghcr.io/github/gh-aw-mcpg:v0.2.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.25.13 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.13 ghcr.io/github/gh-aw-firewall/squid:0.25.13 ghcr.io/github/gh-aw-mcpg:v0.2.12 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_525ef00cdc19702d_EOF' - {"add_comment":{"max":20,"target":"*"},"create_pull_request":{"draft":true,"labels":["area-release-notes","automation"],"max":5,"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[release-notes] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","labels":["area-release-notes","automation"],"max":5,"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[release-notes] "},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_525ef00cdc19702d_EOF + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_cecb8fd51e1ed2a6_EOF' + {"add_comment":{"max":20,"target":"*"},"create_pull_request":{"draft":true,"labels":["area-release-notes","automation"],"max":5,"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[release-notes] "},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","labels":["area-release-notes","automation"],"max":5,"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"title_prefix":"[release-notes] "}} + GH_AW_SAFE_OUTPUTS_CONFIG_cecb8fd51e1ed2a6_EOF - name: Write Safe Outputs Tools run: | - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_22fffe885d0e0f91_EOF' + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_598f5ec1e315399a_EOF' { "description_suffixes": { "add_comment": " CONSTRAINTS: Maximum 20 comment(s) can be added. Target: *.", @@ -428,8 +425,8 @@ jobs: "repo_params": {}, "dynamic_tools": [] } - GH_AW_SAFE_OUTPUTS_TOOLS_META_22fffe885d0e0f91_EOF - cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_dfa689149e75ef27_EOF' + GH_AW_SAFE_OUTPUTS_TOOLS_META_598f5ec1e315399a_EOF + cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_86fc92ebb11567ca_EOF' { "add_comment": { "defaultMax": 1, @@ -561,25 +558,9 @@ jobs: "issueOrPRNumber": true } } - }, - "report_incomplete": { - "defaultMax": 5, - "fields": { - "details": { - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 1024 - } - } } } - GH_AW_SAFE_OUTPUTS_VALIDATION_dfa689149e75ef27_EOF + GH_AW_SAFE_OUTPUTS_VALIDATION_86fc92ebb11567ca_EOF node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs - name: Generate Safe Outputs MCP Server Config id: safe-outputs-config @@ -646,10 +627,10 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.12' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_6d59452c92065982_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh + cat << GH_AW_MCP_CONFIG_a8a9a0a91f91831a_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { "github": { @@ -690,7 +671,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_6d59452c92065982_EOF + GH_AW_MCP_CONFIG_a8a9a0a91f91831a_EOF - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -722,7 +703,7 @@ jobs: # --allow-tool shell(jq) # --allow-tool shell(ls) # --allow-tool shell(pwd) - # --allow-tool shell(release-notes-gen) + # --allow-tool shell(release-notes) # --allow-tool shell(sort) # --allow-tool shell(tail) # --allow-tool shell(uniq) @@ -735,7 +716,7 @@ jobs: touch /tmp/gh-aw/agent-step-summary.md # shellcheck disable=SC1003 sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains '*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.13 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(dotnet:*)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(git:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(release-notes-gen)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(dotnet:*)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(git:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(release-notes)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ case(needs.pre_activation.outputs.copilot_pat_number == '0', secrets.COPILOT_PAT_0, needs.pre_activation.outputs.copilot_pat_number == '1', secrets.COPILOT_PAT_1, needs.pre_activation.outputs.copilot_pat_number == '2', secrets.COPILOT_PAT_2, needs.pre_activation.outputs.copilot_pat_number == '3', secrets.COPILOT_PAT_3, needs.pre_activation.outputs.copilot_pat_number == '4', secrets.COPILOT_PAT_4, needs.pre_activation.outputs.copilot_pat_number == '5', secrets.COPILOT_PAT_5, needs.pre_activation.outputs.copilot_pat_number == '6', secrets.COPILOT_PAT_6, needs.pre_activation.outputs.copilot_pat_number == '7', secrets.COPILOT_PAT_7, needs.pre_activation.outputs.copilot_pat_number == '8', secrets.COPILOT_PAT_8, needs.pre_activation.outputs.copilot_pat_number == '9', secrets.COPILOT_PAT_9, secrets.COPILOT_GITHUB_TOKEN) }} @@ -744,7 +725,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.67.1 + GH_AW_VERSION: v0.67.0 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -767,14 +748,13 @@ jobs: env: REPO_NAME: ${{ github.repository }} SERVER_URL: ${{ github.server_url }} - GITHUB_TOKEN: ${{ github.token }} run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - name: Copy Copilot session state files to logs if: always() @@ -905,7 +885,6 @@ jobs: /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent-stdio.log /tmp/gh-aw/agent/ - /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/safeoutputs.jsonl /tmp/gh-aw/agent_output.json /tmp/gh-aw/aw-*.patch @@ -939,14 +918,13 @@ jobs: group: "gh-aw-conclusion-release-notes" cancel-in-progress: false outputs: - incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 + uses: github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -996,20 +974,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); await main(); - - name: Record Incomplete - id: report_incomplete - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" - GH_AW_WORKFLOW_NAME: "Write Release Notes" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); - await main(); - name: Handle Agent Failure id: handle_agent_failure if: always() @@ -1053,7 +1017,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 + uses: github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1155,7 +1119,7 @@ jobs: COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.67.1 + GH_AW_VERSION: v0.67.0 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1200,7 +1164,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 + uses: github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1274,7 +1238,7 @@ jobs: steps: - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1 + uses: github/gh-aw-actions/setup@cde65c546c2b0f6d3f3a9492a04e6687887c4fe8 # v0.67.0 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1338,7 +1302,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":20,\"target\":\"*\"},\"create_pull_request\":{\"draft\":true,\"labels\":[\"area-release-notes\",\"automation\"],\"max\":5,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[release-notes] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"labels\":[\"area-release-notes\",\"automation\"],\"max\":5,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[release-notes] \"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":20,\"target\":\"*\"},\"create_pull_request\":{\"draft\":true,\"labels\":[\"area-release-notes\",\"automation\"],\"max\":5,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[release-notes] \"},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"labels\":[\"area-release-notes\",\"automation\"],\"max\":5,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[release-notes] \"}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-notes.md b/.github/workflows/release-notes.md index 3f8874b6c4..1fde6b92b6 100644 --- a/.github/workflows/release-notes.md +++ b/.github/workflows/release-notes.md @@ -31,7 +31,7 @@ tools: toolsets: [issues, pull_requests, repos, search] bash: - dotnet - - release-notes-gen + - release-notes - git - jq timeout-minutes: 120 @@ -100,13 +100,13 @@ You maintain release notes for .NET preview, RC, and GA releases in this reposit Your outputs are pull requests — one per active milestone — each containing: -1. **`changes.json`** — a comprehensive manifest of all PRs/commits that shipped, generated by `release-notes-gen generate changes` +1. **`changes.json`** — a comprehensive manifest of all PRs/commits that shipped, generated by `release-notes generate changes` 2. **`features.json`** — a scored derivative of `changes.json` used to rank what is worth documenting 3. **Markdown release notes** — curated editorial content covering high-value features ## Your principles -- **High fidelity** — only document what actually ships. The VMR (`dotnet/dotnet`) and its `src/source-manifest.json` are the source of truth. Trust `release-notes-gen generate changes` output. +- **High fidelity** — only document what actually ships. The VMR (`dotnet/dotnet`) and its `src/source-manifest.json` are the source of truth. Trust `release-notes generate changes` output. - **High value** — bias toward features users care about. Skip infra, test-only, and internal refactoring. - **Never document non-shipping features** — if it's not in `changes.json`, it didn't ship. - **Use scoring as guidance, not law** — `features.json` helps prioritize, but humans and editorial judgment still decide what makes the cut. @@ -205,7 +205,7 @@ Always regenerate — the content may have changed since the previous run. ```bash mkdir -p release-notes/11.0/preview/preview4 -release-notes-gen generate changes /tmp/dotnet \ +release-notes generate changes /tmp/dotnet \ --base v11.0.0-preview.3.26210.100 \ --head main \ --version "11.0.0-preview.4" \ @@ -215,13 +215,47 @@ release-notes-gen generate changes /tmp/dotnet \ #### b. Generate or refresh features.json +Before you assign scores, do a mechanical revert audit. The goal is to catch +features that landed and were later backed out, including reverts that live +outside the current milestone diff. + +Start by scanning `changes.json` for explicit revert titles: + +```bash +jq -r '.changes[] | select(.title | test("(?i)^(partial(ly)?\\s+)?revert\\b|\\bback out\\b")) | [.repo, .title, .url] | @tsv' \ + release-notes/11.0/preview/preview4/changes.json +``` + +Then, for each section-worthy candidate, search the source repo for later merged +PRs that say they revert or back out the original PR. Do this even if the later +PR is in Preview N+1 and therefore absent from the current `changes.json`: + +```bash +gh search prs --repo dotnet/ --state merged \ + "\"This reverts https://github.com/dotnet//pull/\" OR \"revert \" OR \"back out \"" \ + --json number,title,mergedAt,url +``` + +If a merged revert exists, record that evidence in `features.json` with optional +`reverted_by` / `reverts` fields and score the original item to `0` unless the +shipped build clearly still contains it. + Use `changes.json` as the source of truth and write a sibling `features.json` that preserves the same schema while adding optional scoring metadata: - keep the same `id` and `commits{}` values - assign higher scores to externally meaningful, user-visible changes - down-rank infra, churn, test-only work, and anything that appears reverted +- apply the product-boundary rule from `editorial-rules.md` - preserve any useful human annotations if the file already exists +If `features.json` already exists on the branch, treat it as the editorial +baseline and do a **delta merge**: + +- compare the old and new `changes.json` entries by `id` +- score only newly added or materially changed entries +- preserve previous scores and notes for unchanged entries +- avoid rescoring the whole release unless review feedback or new evidence says the earlier cut was wrong + #### c. Re-check the cut with the reader rubric Before finalizing the candidate list, ask: @@ -244,14 +278,19 @@ git log --oneline --author!=github-actions origin/release-notes/11.0-preview4 Identify which markdown files humans have edited. For those files, diff them to understand what changed. Do NOT overwrite human-edited sections. Only add new sections or update sections the agent previously wrote that no human has touched. +If the branch already has drafted markdown, that content is the **baseline** for the next run. Follow the shared `update-existing-branch` playbook: refresh `changes.json` only if the preview moved forward, merge the delta into `features.json`, preserve the current structure, address unresolved feedback, and update only the sections affected by new evidence or newly shipped changes. + #### e. Write or update markdown Using `features.json`, `changes.json`, and the reference documents: - Route changes to output files via `product` field and component-mapping.md - For each component: identify which PRs are worth writing about +- On a populated branch, start by editing the existing markdown files rather than drafting replacements from zero +- integrate new material into existing clusters and sections when it fits the current story (for example, extend an existing performance or GC heading instead of creating a duplicate one) - Write feature descriptions following `format-template.md` and `editorial-rules.md` - If `release-notes/features.json` exists, consult it before writing. For matching long-running features, use the established feature name and start the section with the standard preview blockquote from the sidecar file. +- Include a **Community contributors** section that mentions every external contributor with at least one merged PR in the milestone, even if their change only appears in bug fixes or was not promoted into a top-level feature section. - Components with no noteworthy changes get a minimal stub #### f. Ask for what you can't generate