Skip to content

feat(vdev): automate release upgrade guide from breaking fragments#25877

Open
pront wants to merge 1 commit into
masterfrom
pront-breaking-fragment-schema
Open

feat(vdev): automate release upgrade guide from breaking fragments#25877
pront wants to merge 1 commit into
masterfrom
pront-breaking-fragment-schema

Conversation

@pront

@pront pront commented Jul 16, 2026

Copy link
Copy Markdown
Member

Motivation

  • Easier for devs to write changelog fragments. New vdev changelog new <type> <slug> scaffolder that fills in the filename, the required structure, and the authors: line (auto-detected from git config github.user, gh api user, or a users.noreply.github.com email). Works for every fragment type — no more copying an existing fragment as a template.
  • Easier for maintainers to produce breaking guides. The release process now auto-generates website/content/en/highlights/<date>-<v>-upgrade-guide.md from *.breaking.md fragments. No more hand-writing the upgrade guide every minor release.

What changed

  • *.breaking.md fragments follow a small schema — H1 title (with optional Hugo-style {#anchor}), ## Summary section (lands in the release changelog list), ## Migration section (feeds the upgrade guide), trailing authors: line. Non-breaking fragments are unchanged.
  • vdev check changelog-fragments (already CI-wired) enforces the schema and rejects scaffold placeholders (TODO title, TODO_your_gh_handle). Header matching is fence-aware.
  • vdev release generate-cue parses the new schema, uses only the ## Summary as the changelog description, and emits the upgrade-guide markdown when there is at least one breaking fragment.
  • Exposes vdev release generate-cue --version <v> so the changelog pipeline can be exercised without running the full release prepare flow.

Forward-only — no *.breaking.md fragments in changelog.d/ on master today.

How did you test this PR?

  • Unit tests (96 total): schema parser, TODO/placeholder rejection, slug validation & traversal, fence-aware header matching, upgrade-guide filename, rendered highlights markdown shape, heading bumping.
  • cargo clippy --workspace --all-targets --all-features -- -D warnings clean.
  • End-to-end: scaffolded 3 breaking + 1 fix fragment, ran vdev release generate-cue --version 0.58.0, verified the CUE (only Summary in the changelog list, no metadata/migration leaked) and the highlights markdown (correct frontmatter, TOC, per-fragment anchors, and heading levels matching the existing hand-written upgrade guides).

Change Type

  • Bug fix
  • New feature
  • Dependencies
  • Non-functional (chore, refactoring, docs)
  • Performance

Is this a breaking change?

  • Yes
  • No

Does this PR include user facing changes?

  • Yes. Please add a changelog fragment based on our guidelines.
  • No. A maintainer will apply the no-changelog label to this PR.

@github-actions github-actions Bot added the domain: vdev Anything related to the vdev tooling label Jul 16, 2026
@datadog-vectordotdev

datadog-vectordotdev Bot commented Jul 16, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: d37120b | Docs | Give us feedback!

@pront pront changed the title feat(vdev): formalize breaking changelog fragment schema feat(vdev): structured breaking changelog fragments Jul 16, 2026
@pront pront changed the title feat(vdev): structured breaking changelog fragments feat(vdev): automate release upgrade guide from breaking fragments Jul 16, 2026
@pront pront added the no-changelog Changes in this PR do not need user-facing explanations in the release changelog label Jul 17, 2026
@pront
pront marked this pull request as ready for review July 17, 2026 14:38
@pront
pront requested a review from a team as a code owner July 17, 2026 14:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4879156100

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +143 to +146
if names.split_whitespace().any(|n| n == TODO_HANDLE) {
bail!(
"invalid fragment contents for '{filename}': the scaffolder placeholder '{TODO_HANDLE}' must be replaced with a real GitHub handle."
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject body TODOs from scaffolded fragments

When vdev changelog new fix ... detects a GitHub handle (or the author line is manually replaced), the generated fragment still contains TODO one-item description..., but this placeholder check only rejects TODO_your_gh_handle. Non-breaking fragments have no other TODO validation, and breaking fragments only reject TODO in the title, so CI can pass and generate-cue can publish placeholder Summary/Migration/body text in the release notes. Please reject scaffolded TODOs in the fragment content as well.

Useful? React with 👍 / 👎.

Comment on lines +543 to +545
let s_pos = body
.find(SUMMARY_HDR)
.ok_or_else(|| anyhow!("missing `## Summary` section"))?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use fence-aware parsing for breaking sections

When a breaking fragment has a fenced Markdown/config example before the real ## Summary (or a fenced ## Migration inside the summary), vdev check changelog-fragments accepts it because find_header ignores fenced headings, but this raw substring search grabs the fenced header first. The release then publishes the wrong changelog summary/upgrade-guide body even though CI passed; please share the fence-aware header scan with the release parser.

Useful? React with 👍 / 👎.

@pront
pront force-pushed the pront-breaking-fragment-schema branch from 1e45fa9 to ed3ed50 Compare July 17, 2026 15:18

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed3ed50c1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vdev/src/commands/changelog/mod.rs Outdated
Comment on lines +44 to +46
Event::Start(Tag::Heading { level, .. }) => {
// Close any open section — this heading ends it.
if let Some((sec, start)) = current.take() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep H3 subsections inside migration bodies

When a breaking fragment's ## Migration contains subsection headings (for example the scaffolded ### Old/### New before/after examples), this branch closes the current section for every heading level before verifying that it is a top-level ## Summary/## Migration header. The checker still accepts fragments with prose before the first H3, but generate-cue only sees that prose and drops the rest of the migration from the upgrade guide; only H2 section headings should terminate a section.

Useful? React with 👍 / 👎.

Comment on lines +519 to +522
let joined_len: usize = lines
.iter()
.map(|l| l.len() + 1) // +1 for the '\n' each line was split on
.sum();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve CRLF changelog bodies when splitting authors

When a changelog fragment is committed with CRLF line endings, raw.lines() strips the \r characters but this length calculation only adds back one byte for \n. The resulting slice ends too early by one byte per line, so generate-cue can truncate non-breaking descriptions or drop the tail of a breaking fragment's Migration section even though the checker accepts the file; the previous join-based path normalized CRLF instead of slicing into the original buffer.

Useful? React with 👍 / 👎.

Comment on lines +554 to +555
if line.trim_start().starts_with("```") {
in_fence = !in_fence;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize tilde fences before bumping headings

When a migration section uses a valid ~~~ fenced code block, this only toggles fence state for backtick fences, so lines like # comment inside the block are treated as Markdown headings and rewritten in the generated upgrade guide. The breaking-fragment parser/checker uses CommonMark and will accept those tilde fences, so CI can pass while migration examples are silently corrupted; please handle both fence markers before changing heading levels.

Useful? React with 👍 / 👎.

Automates the release upgrade guide. Previously
website/content/en/highlights/<date>-<v>-upgrade-guide.md was hand-
written for every minor release.

This PR:

- Defines a small schema for `*.breaking.md` fragments — H1 title
  (with optional Hugo-style `{#anchor}`) followed by `## Summary`
  and `## Migration` sections, then a trailing `authors:` line.
- Adds `vdev changelog new <type> <slug>` scaffolder that writes a
  prefilled template and git-adds it. Slug validated (blocks path
  traversal); author auto-fills from `git config github.user` /
  `gh api user` / a `users.noreply.github.com` email; falls back
  to a `TODO_your_gh_handle` placeholder the checker rejects.
- Extends `vdev check changelog-fragments` (already CI-wired) to
  enforce the schema, reject scaffolder placeholders, and validate
  cross-fragment anchor uniqueness (loads every `*.breaking.md` in
  changelog.d/). Sees added AND modified fragments.
- Extends `vdev release generate-cue` to parse `*.breaking.md`,
  feed only the `## Summary` into the changelog list, and emit
  `website/content/en/highlights/<date>-<v>-upgrade-guide.md`.
  Both output files written via .tmp + atomic rename; CUE rolled
  back if highlights write fails.
- Exposes `vdev release generate-cue --version <v>` so the pipeline
  can be exercised without running full `release prepare`. Fragment
  retirement (`git rm`) is a separate step invoked by
  `release prepare`.
- Shared anchor validation lives in `commands::changelog`
  (kebab-case check + reserved names for `vector-breaking-changes`
  and `vector-upgrade-guide`). Used by both the checker and the
  generator.

The parser is deliberately dumb — three string splits over
`\n## Summary\n` and `\n## Migration\n` plus the `authors:` trailer.
CRLF checkouts normalize to LF up front. Migration content passes
through verbatim (any sub-headings the author wrote — `### Old`,
`### New`, whatever — render at whatever depth they declared).
No pulldown-cmark, no heading bumping, no HTML sanitization —
PR review handles content quality.

Forward-only — no `*.breaking.md` fragments in changelog.d/ on
master today.
@pront
pront force-pushed the pront-breaking-fragment-schema branch from ed3ed50 to d37120b Compare July 18, 2026 00:23

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d37120b0c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +725 to +728
writeln!(out, "### {} {{#{}}}\n", b.title, b.anchor).unwrap();
// Migration content passes through verbatim — any sub-headings the author wrote
// (e.g. `### Old` / `### New`) render at whatever depth they declared.
out.push_str(b.migration.trim());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Nest migration headings under each generated change

When a breaking fragment uses the scaffolded ### Old / ### New subsections, this pass-through emits them at the same level as the generated ### <change> heading, so the highlight page’s TOC/document outline treats “Old” and “New” as sibling upgrade-guide entries rather than subsections of that breaking change. Please bump migration headings while rendering under each generated change, or change the generated change heading level so scaffolded subsections remain nested.

Useful? React with 👍 / 👎.

if after_title.matches(MIGRATION_MARKER).count() != 1 {
bail!("exactly one `## Migration` section is required");
}
let s_pos = after_title.find(SUMMARY_MARKER).unwrap();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject prose before the Summary section

When a hand-written breaking fragment puts any prose between the H1 and ## Summary (for example carrying over the old free-form breaking text before adding the new sections), this parser finds the Summary later but never checks the skipped prefix. The checker then passes while generate-cue drops that prose from both the release CUE and the upgrade guide; please reject non-whitespace before the Summary header or include it in one of the rendered sections.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain: vdev Anything related to the vdev tooling no-changelog Changes in this PR do not need user-facing explanations in the release changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant