diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml new file mode 100644 index 0000000000..7a9379bae5 --- /dev/null +++ b/.github/workflows/changelog-preview.yml @@ -0,0 +1,45 @@ +name: Changelog Preview + +# Renders the CHANGELOG.md section the next release would generate from this +# PR's .nextchanges/ fragments, so reviewers can see the result (in this check's +# logs / summary) without cutting a release. +# +# Uses `pull_request` with a read-only token: it renders the PR's own +# .nextchanges/ content and never needs write credentials. +on: + pull_request: + types: [opened, reopened, synchronize] + paths: + - ".nextchanges/**" + - "internal/genkit/**" + - "tools/validate_nextchanges.py" + - "tools/update_github_links.py" + +permissions: + contents: read + +jobs: + preview: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + version: "0.8.9" + + - name: Render changelog preview + run: |- + { + echo '### Changelog preview' + echo '' + echo "What the next release would add to CHANGELOG.md from this PR's .nextchanges/:" + echo '' + echo '```markdown' + uv run --locked internal/genkit/release_tagging.py --preview + echo '```' + echo '' + echo '_Preview only — the date and version are computed at release time._' + } | tee "$GITHUB_STEP_SUMMARY" diff --git a/Taskfile.yml b/Taskfile.yml index ac81551a18..0e18296c53 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -292,6 +292,11 @@ tasks: cmds: - "./tools/validate_nextchanges.py" + changelog-preview: + desc: Print the CHANGELOG.md section the next release would add from .nextchanges/ + cmds: + - "uv run --locked internal/genkit/release_tagging.py --preview" + deadcode: desc: Check for dead code sources: diff --git a/internal/genkit/release_tagging.py b/internal/genkit/release_tagging.py index 4f80fc28cc..8a34b595a2 100644 --- a/internal/genkit/release_tagging.py +++ b/internal/genkit/release_tagging.py @@ -21,7 +21,10 @@ untouched ``process()`` for all commit/tag/race/recovery logic. """ +import argparse import os +import re +from datetime import datetime, timezone from typing import Optional import tagging @@ -162,8 +165,43 @@ def install_nextchanges() -> None: tagging.clean_next_changelog = clear_nextchanges +def preview() -> None: + """ + Print the ``## Release vX.Y.Z`` section(s) the next release would prepend to + CHANGELOG.md, rendered from the current ``.nextchanges/`` — without touching + git, GitHub, or any file. Mirrors write_changelog's date stamp so the output + matches what would land. Read-only: safe to run anytime, no credentials. + """ + current_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") + printed = False + for package in tagging.find_packages(): + tag_info = get_next_tag_info(package) + if tag_info is None: + continue + dated = re.sub( + rf"## Release v({tagging.Version.PATTERN})", + rf"## Release v\1 ({current_date})", + tag_info.content.strip(), + ) + print(dated) + printed = True + if not printed: + print("No .nextchanges/ entries — the next release would add no changelog section.") + + if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--preview", + action="store_true", + help="Print the changelog section the next release would add, then exit (no writes, no network).", + ) + args = parser.parse_args() + install_nextchanges() - tagging.validate_git_root() - tagging.init_github() - tagging.process() + if args.preview: + preview() + else: + tagging.validate_git_root() + tagging.init_github() + tagging.process()