feat(vdev): automate release upgrade guide from breaking fragments#25877
feat(vdev): automate release upgrade guide from breaking fragments#25877pront wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 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".
| 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." | ||
| ); |
There was a problem hiding this comment.
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 👍 / 👎.
| let s_pos = body | ||
| .find(SUMMARY_HDR) | ||
| .ok_or_else(|| anyhow!("missing `## Summary` section"))?; |
There was a problem hiding this comment.
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 👍 / 👎.
1e45fa9 to
ed3ed50
Compare
There was a problem hiding this comment.
💡 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".
| Event::Start(Tag::Heading { level, .. }) => { | ||
| // Close any open section — this heading ends it. | ||
| if let Some((sec, start)) = current.take() { |
There was a problem hiding this comment.
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 👍 / 👎.
| let joined_len: usize = lines | ||
| .iter() | ||
| .map(|l| l.len() + 1) // +1 for the '\n' each line was split on | ||
| .sum(); |
There was a problem hiding this comment.
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 👍 / 👎.
| if line.trim_start().starts_with("```") { | ||
| in_fence = !in_fence; |
There was a problem hiding this comment.
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.
ed3ed50 to
d37120b
Compare
There was a problem hiding this comment.
💡 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".
| 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()); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 👍 / 👎.
Motivation
vdev changelog new <type> <slug>scaffolder that fills in the filename, the required structure, and theauthors:line (auto-detected fromgit config github.user,gh api user, or ausers.noreply.github.comemail). Works for every fragment type — no more copying an existing fragment as a template.website/content/en/highlights/<date>-<v>-upgrade-guide.mdfrom*.breaking.mdfragments. No more hand-writing the upgrade guide every minor release.What changed
*.breaking.mdfragments follow a small schema — H1 title (with optional Hugo-style{#anchor}),## Summarysection (lands in the release changelog list),## Migrationsection (feeds the upgrade guide), trailingauthors:line. Non-breaking fragments are unchanged.vdev check changelog-fragments(already CI-wired) enforces the schema and rejects scaffold placeholders (TODOtitle,TODO_your_gh_handle). Header matching is fence-aware.vdev release generate-cueparses the new schema, uses only the## Summaryas the changelog description, and emits the upgrade-guide markdown when there is at least one breaking fragment.vdev release generate-cue --version <v>so the changelog pipeline can be exercised without running the fullrelease prepareflow.Forward-only — no
*.breaking.mdfragments inchangelog.d/on master today.How did you test this PR?
cargo clippy --workspace --all-targets --all-features -- -D warningsclean.breaking+ 1fixfragment, ranvdev 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
Is this a breaking change?
Does this PR include user facing changes?
no-changeloglabel to this PR.