Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 70 additions & 20 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,80 @@
## Description
<!--
Thank you for contributing to ADBPureFlow! 🎉

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change.
PLEASE READ BEFORE SUBMITTING:
* Fill in each section below. Sections left empty will be omitted from the
automatically generated release notes, but the section headings should be
left in place so the release tooling can parse your PR.
* Select the Semantic Versioning impact of your change using the checkbox
under "Type of Change". This determines whether your change produces a
MAJOR, MINOR, or PATCH release when merged into `main`. If you do not
select anything, the release will default to a PATCH bump.
* Keep the `## Summary`, `## Validation`, `## Breaking Changes`, and
`## Notes` headings exactly as written (case-insensitive, `##` prefix);
the release automation parses them directly.
* Optional: add a line `Release-As: vX.Y.Z` anywhere in this description to
force a specific version number (overrides automatic bump logic).
-->

## Summary

<!--
A concise, human-readable description of what this PR does and why.
This becomes the MAIN BODY of the GitHub Release when the PR is merged.
Use Markdown freely: lists, code blocks, links, etc. are preserved.
-->

- Replace this bullet with a summary of your change.
- Explain *what* changed and *why* it matters to users or contributors.

Fixes # (issue)

## Type of Change
## Validation

<!--
How did you verify this change? Test commands, platforms tested, manual
reproduction steps, screenshots, or evidence. This becomes the "Validation"
section of the release notes so users/developers can see how the change was
tested.
-->

- [ ] `gofmt -w CLI GUI` (or `gofmt -l CLI GUI` reports no files)
- [ ] `cd CLI && go vet ./... && go test -v -race ./...`
- [ ] `cd GUI && go vet ./... && go test -v -race ./...`
- [ ] Manually tested on the affected platform(s) (describe below):

Please delete options that are not relevant.
<!-- Add any additional validation details here. -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] CI/CD or Repository chore
## Breaking Changes

## Checklist
<!--
If this PR introduces any BREAKING CHANGE (CLI flags, GUI behavior,
on-disk layout, API, required Go version, etc.), describe it here and
check the "Breaking change" box below. If there are no breaking changes,
leave this section as `None.`.
-->

- [ ] My code follows the style guidelines of this project (run `gofmt -w .`)
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings or console errors
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
None.

## Notes

<!--
Anything else reviewers and users should know: follow-up work, known
limitations, migration steps, deprecations, credits, links to related
issues or designs. Remove this section if unused.
-->

<!-- SEPARATOR -->

## Type of Change

## Screenshots / Interactive GIFs
<!--
Check exactly ONE box. This controls the Semantic Version bump when the PR
is merged. If no box is checked, the release defaults to a PATCH bump.
You may also indicate a bump via labels on the PR (`breaking`, `feature`,
`bug`, `chore`, `ci`, `docs`, ...).
-->

*Please provide screenshots, pictures, or screen recordings demonstrating the changes, especially if they affect the GUI or the website.*
- [ ] **MAJOR** — Breaking change (existing behavior/API changes; users may need to migrate)
- [ ] **MINOR** — New feature (backward-compatible; adds functionality)
- [ ] **PATCH** — Bug fix, chore, docs, CI, or other maintenance change
96 changes: 96 additions & 0 deletions .github/scripts/build_release_body.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Build the GitHub Release body for a given tag (used by the publish job).

Priority order (highest to lowest):

1. `.release-body.md` if present and non-empty. Written by prepare_release.py
in the same workflow run. (Because this job runs on a fresh checkout at
the tag, the file will NOT exist when re-running against an already-
published tag; we still check so a future single-run release can use it.)
2. The body embedded in the annotated tag message between the
`---RELEASE-BODY-START---` and `---RELEASE-BODY-END---` markers. This is
the primary path for both the normal flow and for re-runs, because the
release body is always stamped into the tag object itself.
3. The matching `## [X.Y.Z]` entry in `CHANGELOG.md`.
4. A minimal fallback body.
"""

from __future__ import annotations

import os
import re
import subprocess
from pathlib import Path

REPO_ROOT = Path.cwd()
PRECOMPUTED = REPO_ROOT / ".release-body.md"
OUT = REPO_ROOT / "release-body.md"
CHANGELOG = REPO_ROOT / "CHANGELOG.md"
BODY_START = "---RELEASE-BODY-START---"
BODY_END = "---RELEASE-BODY-END---"


def git(*args: str) -> str:
r = subprocess.run(["git", *args], capture_output=True, text=True)
return r.stdout


def extract_from_tag(tag: str) -> str:
tag_msg = git("tag", "-l", "--format=%(contents)", tag)
if BODY_START in tag_msg and BODY_END in tag_msg:
start = tag_msg.index(BODY_START) + len(BODY_START)
end = tag_msg.index(BODY_END, start)
return tag_msg[start:end].strip() + "\n"
return ""


def extract_changelog_entry(ver: str) -> str:
if not CHANGELOG.exists():
return ""
text = CHANGELOG.read_text(encoding="utf-8")
pat = re.compile(
rf"^##\s*\[\s*{re.escape(ver)}\s*\].*?(?=^##\s*\[|\Z)",
flags=re.MULTILINE | re.DOTALL,
)
m = pat.search(text)
if not m:
return ""
entry = m.group(0).rstrip()
lines = entry.splitlines()
return "\n".join(lines[1:]).strip()


def main() -> int:
ref = os.environ.get("GITHUB_REF", "")
tag = os.environ.get("TAG", "").strip()
if not tag and ref.startswith("refs/tags/"):
tag = ref[len("refs/tags/"):]
ver = tag.lstrip("v")

body = ""
if PRECOMPUTED.exists() and PRECOMPUTED.stat().st_size > 0:
body = PRECOMPUTED.read_text(encoding="utf-8")
print(f"Using precomputed release body for {tag} ({len(body)} chars).")
elif tag:
body = extract_from_tag(tag)
if body:
print(f"Using release body from annotated tag message ({len(body)} chars).")
if not body and ver:
entry = extract_changelog_entry(ver)
if entry:
body = f"## ADBPureFlow {tag}\n\n{entry}\n"
print(f"Using CHANGELOG entry for {tag} ({len(body)} chars).")
if not body:
body = (
f"## ADBPureFlow {tag}\n\n"
f"Release {tag} of ADBPureFlow.\n\n"
f"See [CHANGELOG.md](./CHANGELOG.md) for details.\n"
)
print(f"Using minimal fallback body for {tag}.")

OUT.write_text(body if body.endswith("\n") else body + "\n", encoding="utf-8")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading