Skip to content

[PM-34709] Create workflow to categorize changes on PR for the release notes#696

Draft
djsmith85 wants to merge 5 commits into
mainfrom
ps/pm-34709/sdlc-label-pr-workflow
Draft

[PM-34709] Create workflow to categorize changes on PR for the release notes#696
djsmith85 wants to merge 5 commits into
mainfrom
ps/pm-34709/sdlc-label-pr-workflow

Conversation

@djsmith85

Copy link
Copy Markdown

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-34709

📔 Objective

Originally built by @vvolkgang to categorize PRs by change type and applying labels accordingly (t:feature, t:bugfix, t:deps, etc).
It uses PR title (conventional commits) and changed files to identify and categorize.

This is already in use on the bitwarden/android and bitwarden/ios, but now that it will also be needed on the bitwarden/clients and bitwarden/server repos.

Goal is to centralize the workflow/scripts and to simplify the existing workflows and easily use them in the other two.

@sonarqubecloud

sonarqubecloud Bot commented Apr 9, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown

Logo
Checkmarx One – Scan Summary & Details9e8435a9-2154-404b-b0a0-a5e4cb903181


New Issues (1) Checkmarx found the following issues in this Pull Request
# Severity Issue Source File / Package Checkmarx Insight
1 CRITICAL Command_Injection /.github/label-prs/label-pr.py: 107
detailsThe application's gh_replace_labels method calls an OS (shell) command with payload, at line 107 of /.github/label-prs/label-pr.py, using a...
Attack Vector

payload = json.dumps({"labels": labels})
subprocess.run(
["gh", "api", "repos/{owner}/{repo}/issues/" + pr_number, "-X", "PATCH", "--silent", "--input", "-"],
input=payload,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djsmith85 pr_number is a script argument, but we should still change this to set a good example. Thought we could change the argument type, update all of these methods to receive an int and then convert it back to string for the gh commands. ⚠️ LLM generated changes based on that direction, reviewed them but didn't test:

diff --git forkSrcPrefix/.github/scripts/label-pr.py forkDstPrefix/.github/scripts/label-pr.py
index 4e803df447ce5f5edee45d01d6f91dfd8e38d55d..bfae9220e4b58fe1d02a7cc34efac8c0322a9ce9 100644
--- forkSrcPrefix/.github/scripts/label-pr.py
+++ forkDstPrefix/.github/scripts/label-pr.py
@@ -62,11 +62,11 @@ def load_config_json(config_file: str) -> dict:
         print(f"❌ Unexpected error loading label-pr.json config: {e}")
         sys.exit(1)
 
-def gh_get_changed_files(pr_number: str) -> list[str]:
+def gh_get_changed_files(pr_number: int) -> list[str]:
     """Get list of changed files in a pull request."""
     try:
         result = subprocess.run(
-            ["gh", "pr", "diff", pr_number, "--name-only"],
+            ["gh", "pr", "diff", str(pr_number), "--name-only"],
             capture_output=True,
             text=True,
             check=True
@@ -77,11 +77,11 @@ def gh_get_changed_files(pr_number: str) -> list[str]:
         print(f"::error::Error getting changed files: {e}")
         return []
 
-def gh_get_pr_title(pr_number: str) -> str:
+def gh_get_pr_title(pr_number: int) -> str:
     """Get the title of a pull request."""
     try:
         result = subprocess.run(
-            ["gh", "pr", "view", pr_number, "--json", "title", "--jq", ".title"],
+            ["gh", "pr", "view", str(pr_number), "--json", "title", "--jq", ".title"],
             capture_output=True,
             text=True,
             check=True
@@ -91,19 +91,19 @@ def gh_get_pr_title(pr_number: str) -> str:
         print(f"::error::Error getting PR title: {e}")
         return ""
 
-def gh_add_labels(pr_number: str, labels: list[str]) -> None:
+def gh_add_labels(pr_number: int, labels: list[str]) -> None:
     """Add labels to a pull request (doesn't remove existing labels)."""
     gh_labels = ','.join(labels)
     subprocess.run(
-        ["gh", "pr", "edit", pr_number, "--add-label", gh_labels],
+        ["gh", "pr", "edit", str(pr_number), "--add-label", gh_labels],
         check=True
     )
 
-def gh_replace_labels(pr_number: str, labels: list[str]) -> None:
+def gh_replace_labels(pr_number: int, labels: list[str]) -> None:
     """Replace all labels on a pull request with the specified labels."""
     payload = json.dumps({"labels": labels})
     subprocess.run(
-        ["gh", "api", "repos/{owner}/{repo}/issues/" + pr_number, "-X", "PATCH", "--silent", "--input", "-"],
+        ["gh", "api", f"repos/{{owner}}/{{repo}}/issues/{pr_number}", "-X", "PATCH", "--silent", "--input", "-"],
         input=payload,
         text=True,
         check=True
@@ -181,6 +181,7 @@ def parse_args():
     )
     parser.add_argument(
         "pr_number",
+        type=int,
         help="The pull request number"
     )
 

"app:authenticator": [
"authenticator/"
],
"t:feature": [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi we're planning to remove setting t: (Change Type) labels based on changed files, relying exclusively on the PR title convention for that (or devs manually setting them), leaving path matching for the app: labels alone. This can match multiple labels, but GitHub's Release Categories system will only match one category which may not be the most relevant one for the PR, reason why sdlc-enforce-labels.yml ensures there's only a single t: label present. Ultimately, the PR title convention has been working better for the team.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm okay, I had extended the categorization for changed files especially for clients, which android/ios would also benefit from (app:password-manager/app:authenticator). I agree that match per title guides the dev to choose a better category, if there's no auto-category per file-change set.

One thing you haven't seen yet, is that I don't rely on the Github generate release notes feature for clients. I wanted to ensure that change on one app would only show for that app-release, unless the change applies to all clients. This also means a single change could occur in multiple categories if we wanted them to.

I kept the release.yml as definition as it was already there, well-known, documented and wouldn't involve changing the scripts. I was thinking of extending the release.yml-standard with a bw-description-tag, which we could use to add a message to each category. For example staged feature rollout (cloud vs self-host).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants