From 31c9a36ce4597c6179259275ed4f5f9d93c6436d Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Fri, 14 Aug 2026 19:20:47 +0000 Subject: [PATCH] workflows(release): support /backport comment on release tracking issue and pr Support /backport (and /backports) on both release tracking issues and PRs. Update documentation and issue templates to reflect the standard backport command, and emit error annotations using GitHub Actions workflow command syntax when arguments are missing. --- .../release_tracking_template.md | 23 ++++++++------ .github/workflows/on_comment.py | 18 +++++++---- RELEASING.md | 2 +- tests/workflows/on_comment_test.py | 30 +++++++++++++------ 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/release_tracking_template.md b/.github/ISSUE_TEMPLATE/release_tracking_template.md index ab634e75f3..efa78a9b63 100644 --- a/.github/ISSUE_TEMPLATE/release_tracking_template.md +++ b/.github/ISSUE_TEMPLATE/release_tracking_template.md @@ -11,24 +11,29 @@ labels: ['type: release'] - [ ] Tag Final ## Backports - -To request a backport, add it to the checklist below and process it. See [RELEASING.md: How to add backports](https://github.com/bazel-contrib/rules_python/blob/main/RELEASING.md#how-to-add-backports) for details. - + +To request a backport, comment `/backport` on the PR, comment `/backport ` +on this issue, or add it to the checklist below. See +[RELEASING.md: How to add backports](https://github.com/bazel-contrib/rules_python/blob/main/RELEASING.md#how-to-add-backports) +for details. + --- - -To manually control the release flow, see the [RELEASING.md: Manual Editing](https://github.com/bazel-contrib/rules_python/blob/main/RELEASING.md#manual-editing-of-tracking-issue) section. - + +To manually control the release flow, see the +[RELEASING.md: Manual Editing](https://github.com/bazel-contrib/rules_python/blob/main/RELEASING.md#manual-editing-of-tracking-issue) +section. +
Available Commands - + Comment commands: - `/prepare`: Determines version, creates tracking issue and preparation PR. - `/prepare-complete [PR]`: Marks preparation task as complete. - `/create-release-branch`: Cuts and pushes the release branch. - `/create-rc`: Tags and publishes a new release candidate (RC). - `/process-backports`: Cherry-picks pending backports. -- `/add-backports `: Adds PRs to the backports and processes backports. +- `/backport `: Adds PRs to the backports and processes backports. - `/promote`: Promotes the latest RC to final release. - + See [RELEASING.md](https://github.com/bazel-contrib/rules_python/blob/main/RELEASING.md) for details on how to use them.
diff --git a/.github/workflows/on_comment.py b/.github/workflows/on_comment.py index 4d4b4ebb6a..1aa0f1baa0 100755 --- a/.github/workflows/on_comment.py +++ b/.github/workflows/on_comment.py @@ -15,11 +15,17 @@ def _get_bool(key: str, default: bool = False) -> bool: return val.lower() == "true" -def _match_command(command: str, comment_body: str) -> re.Match[str] | None: +def _match_command( + command: str | tuple[str, ...], comment_body: str +) -> re.Match[str] | None: """Matches a slash command at the start of any line, capturing optional trailing args.""" - cmd = command.lstrip("/") + if isinstance(command, str): + commands = (command,) + else: + commands = command + pattern = "|".join(re.escape(cmd.lstrip("/")) for cmd in commands) return re.search( - rf"^\s*/{re.escape(cmd)}(?:\s+(\S.*?))?\s*$", + rf"^\s*/(?:{pattern})(?:\s+(\S.*?))?\s*$", comment_body, re.MULTILINE, ) @@ -61,7 +67,7 @@ def _add_comment_reaction(repo: str, comment_id: str, content: str) -> None: def _react_negative(repo: str, comment_id: str) -> None: """Logs error and adds a negative reaction to the comment.""" - print("Error: No PRs specified for add-backports.", file=sys.stderr) + print("::error::No PRs specified for backport.") if comment_id and repo: _add_comment_reaction(repo=repo, comment_id=comment_id, content="-1") @@ -95,7 +101,7 @@ def _process_release_issue_comment( _write_github_output("command", "process-backports") return - if m := _match_command("add-backports", comment_body): + if m := _match_command(("backport", "backports"), comment_body): raw_args = m.group(1) if m.group(1) else "" items = [item for item in re.split(r"[\s,]+", raw_args) if item] if csv := ",".join(items): @@ -128,7 +134,7 @@ def _process_backport_issue_comment(comment_body: str) -> None: def _process_pr_comment(comment_body: str, pr_number: str) -> None: """Processes comments on a pull request.""" - if _match_command("backport", comment_body): + if _match_command(("backport", "backports"), comment_body): _write_github_output("command", "pr-backport") _write_github_output("pr_number", pr_number) return diff --git a/RELEASING.md b/RELEASING.md index 6749e2f10b..f740cb3fa9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -108,7 +108,7 @@ automatically processed. ### Method B: Comment on the Tracking Issue -Comment `/add-backports [ ...]` (space or comma separated) on +Comment `/backport [ ...]` (space or comma separated) on the tracking issue. The `` can be a PR number (optionally prefixed with `#`) or a PR URL (strictly for the configured repository). This will automatically add the PRs to the checklist and trigger processing. diff --git a/tests/workflows/on_comment_test.py b/tests/workflows/on_comment_test.py index 9ec9413db1..d7df7da13e 100644 --- a/tests/workflows/on_comment_test.py +++ b/tests/workflows/on_comment_test.py @@ -152,10 +152,10 @@ def test_release_issue_process_backports(monkeypatch, gha_env): assert gha_env.read_env() == {"issue_number": "100"} -def test_release_issue_add_backports(monkeypatch, gha_env): +def test_release_issue_backport(monkeypatch, gha_env): _run_comment( monkeypatch, - "/add-backports 1, 2, 3", + "/backport 1, 2, 3", has_release_label="true", ) assert gha_env.read_outputs() == { @@ -166,10 +166,10 @@ def test_release_issue_add_backports(monkeypatch, gha_env): assert gha_env.read_env() == {"issue_number": "100"} -def test_release_issue_add_backports_hashes(monkeypatch, gha_env): +def test_release_issue_backport_hashes(monkeypatch, gha_env): _run_comment( monkeypatch, - "/add-backports #123 #567", + "/backport #123 #567", has_release_label="true", ) assert gha_env.read_outputs() == { @@ -180,12 +180,10 @@ def test_release_issue_add_backports_hashes(monkeypatch, gha_env): assert gha_env.read_env() == {"issue_number": "100"} -def test_release_issue_add_backports_empty( - monkeypatch, gha_env, mock_add_reaction, capsys -): +def test_release_issue_backport_empty(monkeypatch, gha_env, mock_add_reaction, capsys): _run_comment( monkeypatch, - "/add-backports", + "/backport", has_release_label="true", repo="bazel-contrib/rules_python", comment_id="789", @@ -196,7 +194,7 @@ def test_release_issue_add_backports_empty( } assert gha_env.read_env() == {"issue_number": "100"} captured = capsys.readouterr() - assert "Error: No PRs specified for add-backports." in captured.err + assert "::error::No PRs specified for backport." in captured.out mock_add_reaction.assert_called_once_with( repo="bazel-contrib/rules_python", comment_id="789", @@ -311,6 +309,20 @@ def test_pr_backport(monkeypatch, gha_env): assert gha_env.read_env() == {} +def test_pr_backports_plural_alias(monkeypatch, gha_env): + _run_comment( + monkeypatch, + "/backports", + is_pr="true", + event_number="300", + ) + assert gha_env.read_outputs() == { + "command": "pr-backport", + "pr_number": "300", + } + assert gha_env.read_env() == {} + + def test_pr_prepare_complete(monkeypatch, gha_env): _run_comment( monkeypatch,