Skip to content

changelog: dispatch write methods on entries vs legacy layout (PR 3b)#4524

Closed
Copilot wants to merge 2 commits into
mainfrom
copilot/implement-pr-3b-changelog
Closed

changelog: dispatch write methods on entries vs legacy layout (PR 3b)#4524
Copilot wants to merge 2 commits into
mainfrom
copilot/implement-pr-3b-changelog

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 14, 2026

Write-path half of the per-entry changelog plan. Dispatches four mutating AChangelogs methods on _entries_layout (single is_dir() against changelogs/current). Legacy branch of every method is byte-for-byte unchanged.

Changes

write_version — entries branch

  • Aggregates current/<section>/*.rst via AChangelog.get_data_from_entries; propagates ChangelogParseError with no side effects (no version file written, no rmtree)
  • Writes aggregated dict to <version>.yaml via dump_yaml
  • Resets current.yaml to date: Pending\n before rmtree (atomic ordering: file correct even if rmtree fails)
  • Defensive re-check of _entries_layout before shutil.rmtree; recreates empty current/ immediately after so _entries_layout stays True when write_current runs next

write_current — entries branch

Writes slim date: Pending\n and recreates current/ with .gitkeep, preserving _entries_layout = True across release → dev cycles. Documents the design choice in a comment.

write_date — entries branch

Slim current.yaml contains only the date key; literal f"date: {date}\n" write is sufficient — no YAML round-trip needed.

changes_for_commit — entries branch

Adds CHANGELOG_CURRENT_DIR_PATH to the changed set. changes_for_commit is called after write_version has rmtree'd the directory, so globbing **/*.rst would return nothing. Including the directory path is correct: git add <dir> stages deletions of tracked files removed from the working tree.

is_pending

No code change; added comment that it dispatches correctly via AChangelog.data on both layouts.

Tests

All four methods parametrised across entries_layout=[True, False]. New test_abstract_changelogs_write_version_entries_parse_error explicitly tests the no-side-effects safeguard on ChangelogParseError. Coverage on changelog.py remains 100%.

Original prompt

Context

Implements PR 3b of the per-entry changelog plan (see #4498, #4499, plans/per-entry-changelog-4498.md). This is the write-path half of "Toolshed PR 3"; the read-path half landed as #4523.

Dependencies (all landed on main):

Main is at ee9b62f5661abb7b76ed02c01a06b55774c06928.

Backward-compatibility principle (non-negotiable)

Single is_dir() check on <project_path>/changelogs/current. Both layouts must continue to work indefinitely:

  • Stable release branches (release/v1.xx) cut before the migration use legacy and stay legacy.
  • Cherry-picks to legacy branches must keep working with the new wheel installed.
  • Toolshed wheels upgradeable on legacy branches without breakage.

If changelogs/current/ is a directory → entries layout. Otherwise → legacy. Strict dispatch: the entries branch must never operate on a repo without the directory; the legacy branch must never look at the directory.

What this PR does

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

Dispatches four mutating methods on AChangelogs._entries_layout. The legacy branch of each method is byte-for-byte the existing implementation — do not refactor existing behaviour. Only the entries branch is new.

1. write_version — release moment, the dangerous one

Current behaviour (legacy branch — keep unchanged):

def write_version(self, version: _version.Version) -> None:
    version_path = self.project.path.joinpath(
        CHANGELOG_PATH_FMT.format(version=version))
    version_path.write_text(self.current_path.read_text())

This copies the full current.yaml content (with all entries) into changelogs/<version>.yaml, then write_current recreates a blank current.yaml.

On the entries layout, the slim current.yaml has only date: ...; the entries live in current/<section>/<area>__<slug>.rst. So write_version must:

  1. Aggregate the directory via AChangelog.get_data_from_entries(current_path, current_dir_path) — same code path used by data.
  2. Dump the aggregated ChangelogDict to YAML at <version>.yaml. Use the same dumper the file already uses for any existing YAML emission — look for dump_yaml, yaml.dump, or similar in this module / the package. Match the byte format other historical <version>.yaml files use (block style, key order: date first then sections in sections.yaml order if possible; otherwise alphabetical / insertion-ordered to match what the current pipeline produces).
  3. Then shutil.rmtree(current_dir_path) to remove the entries directory. The next dev cycle's write_current will recreate it.

Critical safeguards:

  • Refuse on parse failure. If get_data_from_entries raises ChangelogParseError, propagate it without writing <version>.yaml and without rmtree. A bad aggregation must not corrupt the historical record.

  • Re-check _entries_layout before rmtree. Guard the destructive step:

    if changelogs._entries_layout:
        shutil.rmtree(changelogs.current_dir_path)

    Defensive — covers the (unlikely) race where the directory disappeared between dispatch and rmtree.

  • Atomic-ish ordering. Write the new <version>.yaml first, then rmtree. If rmtree fails, the version file is already correct; the next run will retry the rmtree (or surface the failure visibly). Do NOT rmtree first.

Add import shutil at the top of the module if not already present.

2. write_current — start of new dev cycle

Current behaviour (legacy branch — keep unchanged):

def write_current(self) -> None:
    self.current_path.write_text(self.current_tpl.render(...))

The legacy template renders a full YAML scaffold with date: Pending plus empty section keys.

On the entries layout, write a slim YAML containing only date: Pending:

self.current_path.write_text("date: Pending\n")

That's it — no template, no section keys. The entries directory does not need to exist at this point; contributors create current/<section>/ on demand when they add the first entry. Do not create an empty current/ directory.

Detection: at the moment write_current is called (during dev), the previous write_version step may have just rmtree'd the directory, so _entries_layout is now False. But the layout the project is **going to be on...

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 16e61ba
🔍 Latest deploy log https://app.netlify.com/projects/nifty-bassi-e26446/deploys/6a0614f7c3f0c10007cd3136
😎 Deploy Preview https://deploy-preview-4524--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 changed the title [WIP] Implement PR 3b of the per-entry changelog plan changelog: dispatch write methods on entries vs legacy layout (PR 3b) May 14, 2026
Copilot AI requested a review from phlax May 14, 2026 18:34
Copilot finished work on behalf of phlax May 14, 2026 18:34
@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