@@ -266,17 +266,120 @@ jobs:
266266 run : |
267267 # Remove the granular manifests
268268 rm -f artifacts/*-dist-manifest.json
269+ - name : Install git-cliff
270+ uses : taiki-e/install-action@v2
271+ with :
272+ tool : git-cliff
273+ - name : Generate release notes
274+ id : release_notes
275+ env :
276+ ANNOUNCEMENT_BODY : " ${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
277+ RELEASE_TAG : " ${{ needs.plan.outputs.tag }}"
278+ REPOSITORY : " ${{ github.repository }}"
279+ run : |
280+ python <<'PY'
281+ import os
282+ import pathlib
283+ import re
284+ import subprocess
285+
286+ body = os.environ["ANNOUNCEMENT_BODY"]
287+ repo = os.environ["REPOSITORY"]
288+ tag = os.environ["RELEASE_TAG"]
289+
290+
291+ def replace_install_urls(text: str) -> str:
292+ pattern = re.compile(
293+ "https://github.com/"
294+ + re.escape(repo)
295+ + "/releases/download/"
296+ + re.escape(tag)
297+ + r"/(thanks-stars-installer\.(?:sh|ps1))"
298+ )
299+
300+ def _replace(match):
301+ return f"https://github.com/{repo}/releases/latest/download/{match.group(1)}"
302+
303+ return pattern.sub(_replace, text)
304+
305+
306+ body = replace_install_urls(body).strip()
307+ version = tag[1:] if tag.startswith("v") else tag
308+
309+ changelog_path = pathlib.Path("CHANGELOG.md")
310+ changelog = ""
311+ if changelog_path.exists():
312+ changelog_text = changelog_path.read_text(encoding="utf-8")
313+ changelog_pattern = re.compile(
314+ rf"^##\s+\[?{re.escape(version)}\]?(?:\s+-\s+[^\n]+)?\n(.*?)(?=^##\s+\[?.+\]?|\Z)",
315+ re.MULTILINE | re.DOTALL,
316+ )
317+ match = changelog_pattern.search(changelog_text)
318+ if match:
319+ changelog = match.group(1).strip()
320+ if not changelog:
321+ changelog = "_No changelog entries for this release._"
322+
323+ subprocess.run(["git", "fetch", "--tags", "--force"], check=False)
324+ tags_proc = subprocess.run(
325+ ["git", "tag", "--sort=-version:refname"], capture_output=True, text=True, check=False
326+ )
327+ tags = [line.strip() for line in tags_proc.stdout.splitlines() if line.strip()]
328+ prev_tag = None
329+ if tag in tags:
330+ idx = tags.index(tag)
331+ if idx + 1 < len(tags):
332+ prev_tag = tags[idx + 1]
333+ elif len(tags) > 1:
334+ prev_tag = tags[1]
335+
336+ range_spec = f"{prev_tag}..{tag}" if prev_tag else tag
337+ log_proc = subprocess.run(
338+ ["git", "log", range_spec, "--format=%aN"], capture_output=True, text=True, check=False
339+ )
340+ contributors = sorted({line.strip() for line in log_proc.stdout.splitlines() if line.strip()})
341+
342+ changes = subprocess.check_output(
343+ [
344+ "git-cliff",
345+ "--config",
346+ "cliff.toml",
347+ "--tag",
348+ tag,
349+ "--strip",
350+ "footer",
351+ ],
352+ text=True,
353+ ).strip()
354+
355+ sections = [body]
356+ if changes:
357+ sections.append(changes)
358+ sections.extend(
359+ [
360+ "## Changelog",
361+ changelog,
362+ "## Contributors",
363+ "\n".join(f"- {name}" for name in contributors)
364+ if contributors
365+ else "_No new contributors in this release._",
366+ ]
367+ )
368+
369+ notes = "\n\n".join(section.strip() for section in sections if section.strip()) + "\n"
370+ notes_path = pathlib.Path(os.environ["RUNNER_TEMP"]) / "notes.md"
371+ notes_path.write_text(notes, encoding="utf-8")
372+
373+ with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh:
374+ fh.write(f"path={notes_path}\n")
375+ PY
269376 - name : Create GitHub Release
270377 env :
271378 PRERELEASE_FLAG : " ${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
272379 ANNOUNCEMENT_TITLE : " ${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
273- ANNOUNCEMENT_BODY : " ${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
274380 RELEASE_COMMIT : " ${{ github.sha }}"
275381 run : |
276- # Write and read notes from a file to avoid quoting breaking things
277- echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
278-
279- gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
382+ gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "${{ steps.release_notes.outputs.path }}" artifacts/*
280383
281384 publish-homebrew-formula :
282385 needs :
@@ -294,6 +397,7 @@ jobs:
294397 with :
295398 persist-credentials : true
296399 repository : " Kenzo-Wada/thanks-stars"
400+ ref : ${{ github.event.repository.default_branch || 'main' }}
297401 token : ${{ secrets.HOMEBREW_TAP_TOKEN }}
298402 # So we have access to the formula
299403 - name : Fetch homebrew formulae
0 commit comments