Skip to content

feat(changelog): PR 3a — per-entry changelog read-path dispatch#4521

Closed
Copilot wants to merge 3 commits into
mainfrom
copilot/implement-pr-3a-read-path
Closed

feat(changelog): PR 3a — per-entry changelog read-path dispatch#4521
Copilot wants to merge 3 commits into
mainfrom
copilot/implement-pr-3a-read-path

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 14, 2026

Implements the read-path half of Toolshed PR 3 from the per-entry changelog plan (#4498). Adds layout detection and dispatches AChangelog.data to get_data_from_entries when the entries layout is active; legacy YAML layout is fully preserved.

Layout detection

AChangelogs._entries_layout (@property, not cached): single is_dir() check against changelogs/current/. No config flag, no version pin — presence of the directory is the sole signal.

AChangelogs.current_dir_path / rel_current_dir_path: mirrors the existing current_path / rel_current_path pair; consumed by the dispatch below and by PR 3b (write-path).

Dispatch in AChangelog.data

AChangelog._is_current (@property): self.version == self.project.changelogs.current.

data now dispatches:

@async_property(cache=True)
async def data(self) -> typing.ChangelogDict:
    changelogs = self.project.changelogs
    if changelogs._entries_layout and self._is_current:
        parsed = await self.project.execute(
            self.get_data_from_entries,
            self.path,
            changelogs.current_dir_path)
    else:
        parsed = await self.project.execute(self.get_data, self.path)
    return changelogs.validate_sections(parsed, self.path)

_entries_layout is evaluated first so legacy repos short-circuit before touching _is_current. Historical <version>.yaml files always use the legacy reader. validate_sections runs on both branches.

Tests

  • test_abstract_changelogs_entries_layout — parametrized [True, False], asserts is_dir() call and no caching.
  • test_abstract_changelogs_current_dir_path / rel_current_dir_path — mirrors existing path property tests.
  • test_abstract_changelog__is_current — parametrized [True, False].
  • test_abstract_changelog_data — replaced with parametrized [entries_layout × is_current] covering all four dispatch combinations; asserts correct function reference and args passed to project.execute, and that validate_sections is called in every case.
  • test_abstract_changelog_data_unknown_section (existing integration test) passes unchanged.
Original prompt

Context

Implements PR 3a of the per-entry changelog plan (see #4498, #4499, and plans/per-entry-changelog-4498.md). This is the read-path half of "Toolshed PR 3"; the write-path half (write_version / write_current / write_date / changes_for_commit) lands separately as PR 3b.

Dependencies landed:

Backward-compatibility principle (non-negotiable)

Detection is a single is_dir() check against <project_path>/changelogs/current. No config flag, no version pin, no deprecation timeline. Both layouts must be supported indefinitely so that:

  • Stable release branches (release/v1.xx) cut before the migration keep working unchanged.
  • Cherry-picks to legacy branches keep building.
  • Toolshed wheels can be upgraded on legacy branches without breakage.

If changelogs/current/ is a directory → entries layout. Otherwise → legacy layout. Implementation must be strict: the entries path must never run on a repo without current/, and the legacy path must never look at current/.

What this PR does

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

1. Layout-detection helper on AChangelogs

Add a property:

@property
def _entries_layout(self) -> bool:
    """Whether the project uses the per-entry changelog layout.

    Detected by the presence of the `changelogs/current/` directory.
    A single `is_dir()` check; no caching beyond what `pathlib` does.
    """
    return self.project.path.joinpath(CHANGELOG_CURRENT_DIR_PATH).is_dir()

Match the existing file's style (other properties on AChangelogs are @property or @cached_property). Do not cache — the directory may appear/disappear during a single process lifetime (e.g. write_version removes it). If profiling shows is_dir() is hot, a future PR can add a @cached_property with explicit invalidation; not this PR's problem.

2. Per-entry directory path helper on AChangelogs

Add a property that returns the entries directory path (parallel to current_path / rel_current_path):

@property
def current_dir_path(self) -> pathlib.Path:
    return self.project.path.joinpath(self.rel_current_dir_path)

@property
def rel_current_dir_path(self) -> pathlib.Path:
    return pathlib.Path(CHANGELOG_CURRENT_DIR_PATH)

Mirrors the existing current_path / rel_current_path pair exactly. These will be reused by PR 3b; introducing them now keeps the read-path PR self-contained.

3. Dispatch AChangelog.get_data / AChangelog.data on layout

Current state (post-#4519):

@classmethod
def get_data(cls, path) -> typing.ChangelogDict:
    try:
        data = utils.from_yaml(path, typing.ChangelogSourceDict)
    except (_yaml.reader.ReaderError, utils.TypeCastingError) as e:
        raise exceptions.ChangelogParseError(f"Failed to parse: {path}\n{e}")
    return cast(
        typing.ChangelogDict,
        {k: (v if k == "date" else [...]) for k, v in data.items() if v})

@async_property(cache=True)
async def data(self) -> typing.ChangelogDict:
    return self.project.changelogs.validate_sections(
        await self.project.execute(self.get_data, self.path),
        self.path)

AChangelog.get_data is a @classmethod — it has no access to AChangelogs, so it cannot do layout detection itself. The dispatch must happen in AChangelog.data (which has self.project.changelogs).

Change data to detect layout and dispatch:

@async_property(cache=True)
async def data(self) -> typing.ChangelogDict:
    changelogs = self.project.changelogs
    if self.is_current and changelogs._entries_layout:
        parsed = await self.project.execute(
            self.get_data_from_entries,
            self.path,
            changelogs.current_dir_path)
    else:
        parsed = await self.project.execute(self.get_data, self.path)
    return changelogs.validate_sections(parsed, self.path)

Key constraints on this:

  • Gate on self.is_current. The per-entry layout only applies to the current changelog. Historical <version>.yaml files keep using the legacy reader regardless of whether current/ exists. AChangelog has access to its version via self.version, and AChangelogs already has an is_current notion (in AChangelogStatus on the checker side; on the aggregator side, current is next(iter(self.changelogs))). The cleanest check is: self.version == changelogs.current. Use whichever spelling already exists in this file's style; if there isn't one, add a small private _is_current property on AChangelog.

  • validate_sections still runs for both branches. It's source-agnostic and already exists on AChangelogs.

  • The classmethod get_data is unchanged. Do not touch its body. get_data_from_entries is unchanged. Only the dispatch in the `...

This pull request was created from Copilot chat.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 14, 2026

Deploy Preview for nifty-bassi-e26446 ready!

Name Link
🔨 Latest commit b883e70
🔍 Latest deploy log https://app.netlify.com/projects/nifty-bassi-e26446/deploys/6a0601c2e22fde0008c1555d
😎 Deploy Preview https://deploy-preview-4521--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.

Copilot AI and others added 2 commits May 14, 2026 17:07
- Add AChangelog._is_current property (version == changelogs.current)
- Update AChangelog.data to dispatch to get_data_from_entries when
  changelogs._entries_layout is True and self._is_current is True
- Add AChangelogs._entries_layout property (is_dir check, not cached)
- Add AChangelogs.current_dir_path / rel_current_dir_path properties
- Add/update tests for all new properties and 4 dispatch combinations

Agent-Logs-Url: https://github.com/envoyproxy/toolshed/sessions/3c33e997-440b-41d8-b26b-d64707f25344

Co-authored-by: phlax <454682+phlax@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement read-path for per-entry changelog plan PR 3a feat(changelog): PR 3a — per-entry changelog read-path dispatch May 14, 2026
Copilot AI requested a review from phlax May 14, 2026 17:10
Copilot finished work on behalf of phlax May 14, 2026 17:10
@phlax phlax closed this May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants