Skip to content

Add per-entry RST directory reader to AChangelog (PR 2)#4501

Draft
Copilot wants to merge 2 commits into
mainfrom
copilot/add-per-entry-rst-directory-reader
Draft

Add per-entry RST directory reader to AChangelog (PR 2)#4501
Copilot wants to merge 2 commits into
mainfrom
copilot/add-per-entry-rst-directory-reader

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 11, 2026

Part of the migration from a monolithic changelogs/current.yaml to a per-entry layout (changelogs/current/<section>/<area>__<slug>.rst) that eliminates merge conflicts. This PR is purely additive — no existing methods are modified.

New constants

CHANGELOG_CURRENT_DIR_PATH = "changelogs/current"
CHANGELOG_ENTRY_GLOB = "*/*.rst"
ENTRY_SEPARATOR = "__"

New classmethod: AChangelog.get_data_from_entries

@classmethod
def get_data_from_entries(
        cls,
        yaml_path: pathlib.Path,   # slim current.yaml (holds date: Pending)
        entry_dir: pathlib.Path,   # changelogs/current/
) -> ChangelogDict:
  • Reads date from yaml_path via the same utils.from_yaml / error path as get_data()
  • Globs entry_dir with */*.rst; path.parent.name → section, path.stemarea__slug
  • Validates stems have exactly one __; raises ChangelogParseError naming the offending file otherwise
  • Groups entries by section, sorted deterministically; assembles and returns a ChangelogDict
  • Section names are not validated against sections.yaml here — that's deferred to PR 4

Tests

Seven integration tests (using tmp_path) cover: happy path across multiple sections, arbitrary/unknown section names (accepted), missing separator, multiple separators, missing current.yaml, empty entry directory, and stable ordering across repeated calls.

Original prompt

Toolshed PR 2 — Per-entry RST directory reader in AChangelog

This is the second PR in the implementation plan for per-entry changelog layout support (see issue #4498, plan at plans/per-entry-changelog-4498.md, investigation PR #4499). PR 1 (typing relaxation) is in flight in parallel — see #4498/#4499 for the relaxed ChangelogSourceDict / ChangelogChangeSectionsDict shape; rebase if/when PR 1 lands first.

Background

Envoy is migrating from a single monolithic changelogs/current.yaml to a per-entry layout to eliminate constant merge conflicts:

changelogs/current/<section>/<area>__<slug>.rst
  • <section>: must match a key in changelogs/sections.yaml (e.g. bug_fixes, new_features, behavior_changes, …).
  • <area>: arbitrary low-cardinality identifier (e.g. oauth2, jwt_authn); must not contain __.
  • <slug>: arbitrary short description; must not contain __.
  • Separator: double underscore (__) cleanly splits area from slug; both sides may use - and _ otherwise.
  • File contents: pure RST, no YAML frontmatter.

Example: changelogs/current/bug_fixes/oauth2__foo_fixed-critical-vuln.rst

The slim current.yaml continues to exist but holds only date: Pending (or a real date once stamped by write_date).

Required changes

File: py/envoy.base.utils/envoy/base/utils/abstract/project/changelog.py

1. Add module-level constants (around lines 27–34, alongside existing CHANGELOG_CURRENT_PATH)

CHANGELOG_CURRENT_DIR_PATH = "changelogs/current"
CHANGELOG_ENTRY_GLOB = "*/*.rst"
ENTRY_SEPARATOR = "__"

Use existing naming conventions / module style.

2. Add new classmethod AChangelog.get_data_from_entries

Signature (adjust to match existing style — pathlib.Path is already used in the file):

@classmethod
def get_data_from_entries(
    cls,
    yaml_path: pathlib.Path,
    entry_dir: pathlib.Path,
) -> "ChangelogDict":
    ...

Behaviour:

  1. Read date from yaml_path — the slim current.yaml that still holds date: Pending (or a stamped date). Use the same YAML loader pattern as the existing get_data() (line ~135) so quirks (e.g. envoy.base.utils' yaml utils) are preserved. If the file is missing, raise the same kind of error get_data() raises today.
  2. Walk entry_dir using CHANGELOG_ENTRY_GLOB (i.e. one subdirectory per section, *.rst files within). Use pathlib.Path.glob or rglob consistently with the rest of the codebase.
  3. For each .rst file, parse and validate:
    • path.parent.name is the section name (no validation against sections.yaml here — that's PR 4's checker job; this method just groups by parent dir name).
    • path.stem must contain exactly one ENTRY_SEPARATOR (__). Split into area, slug = path.stem.split(ENTRY_SEPARATOR, 1). If the count of __ in path.stem is not exactly 1, raise a clear ValueError (or the existing project exception type — check what get_data() raises) naming the offending file.
    • Read the file contents as text; treat it as the RST body of the change.
  4. Build the ChangelogChangeDict for each entry. Inspect the existing ChangelogChangeDict definition in typing.py to populate the right keys (typically area + change text; if the dict has more keys today they become absent/empty per the new layout — only populate what's required and keep optional keys absent).
  5. Group by section into a mapping {section_name: [ChangelogChangeDict, ...]}. Sort entries within a section deterministically (e.g. by (area, slug)) so output is stable across runs.
  6. Assemble the final ChangelogDict with the date from step 1 plus the section groupings. Cast to ChangelogDict / ChangelogSourceDict as get_data() does today.
  7. Return the dict.

3. Do NOT modify the existing get_data() or data property in this PR

  • get_data() (lines ~133–149) and the data async property (lines ~163–168) stay untouched.
  • Dispatching between the old YAML path and the new directory path is PR 3's job (write_version / write_current / write_date / changes_for_commit). This PR is purely additive.

4. Tests

Add unit tests in py/envoy.base.utils/tests/test_abstract_project_changelogs.py covering:

  • Happy path: a temp directory with current.yaml (date: Pending) plus a few <section>/<area>__<slug>.rst files across multiple sections → returns a correct ChangelogDict.
  • Unknown / arbitrary section name (e.g. weird_section/foo__bar.rst) — should be accepted by this reader (validation deferred to PR 4) and grouped under that key.
  • Missing __ in stem (e.g. bug_fixes/no_separator.rst) → raises with a message that names the file.
  • Multiple __ in stem (e.g. bug_fixes/a__b__c.rst) → raises (the spec says exactly one separator; both area and slug must not contain __).
  • Missing current.yaml → raises in the same way the existing get_data() does for a missing file.
  • Em...

This pull request was created from Copilot chat.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 11, 2026

Deploy Preview for nifty-bassi-e26446 ready!

Name Link
🔨 Latest commit 858449d
🔍 Latest deploy log https://app.netlify.com/projects/nifty-bassi-e26446/deploys/6a020470decb6700074510a2
😎 Deploy Preview https://deploy-preview-4501--nifty-bassi-e26446.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

- Add CHANGELOG_CURRENT_DIR_PATH, CHANGELOG_ENTRY_GLOB, ENTRY_SEPARATOR constants
- Add AChangelog.get_data_from_entries() classmethod that reads date from
  current.yaml and walks <section>/<area>__<slug>.rst entries
- Add unit tests covering happy path, arbitrary sections, missing/multiple
  separators, missing yaml, empty dir, and stable ordering

Agent-Logs-Url: https://github.com/envoyproxy/toolshed/sessions/6f4aed80-8caf-4e9e-9155-6446a7a7d9bd

Co-authored-by: phlax <454682+phlax@users.noreply.github.com>
Copilot AI changed the title [WIP] Add per-entry RST directory reader in AChangelog Add per-entry RST directory reader to AChangelog (PR 2) May 11, 2026
Copilot finished work on behalf of phlax May 11, 2026 16:34
Copilot AI requested a review from phlax May 11, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants