Skip to content
Merged
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
42 changes: 42 additions & 0 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Backport fixes to stable branch

on:
push:
branches:
- master
issue_comment:
types: [created]

concurrency:
group: backport-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
Comment on lines +10 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Concurrency group key collapses all runs into one queue.

github.ref for issue_comment events is always the default branch. That is the same value the push trigger produces when a commit lands on master/main, so every run — push-triggered or comment-triggered, regardless of which PR is being backported — shares a single concurrency group. With cancel-in-progress: false they all queue sequentially, meaning two concurrent /backport requests on unrelated PRs (e.g. targeting different stable branches) unnecessarily block each other.

Scope the key more narrowly so unrelated runs can proceed in parallel:

🔧 Proposed fix
 concurrency:
-  group: backport-${{ github.workflow }}-${{ github.ref }}
+  group: >-
+    ${{ github.event_name == 'issue_comment'
+        && format('backport-comment-{0}', github.event.issue.number)
+        || format('backport-push-{0}', github.sha) }}
   cancel-in-progress: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
concurrency:
group: backport-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
concurrency:
group: >-
${{ github.event_name == 'issue_comment'
&& format('backport-comment-{0}', github.event.issue.number)
|| format('backport-push-{0}', github.sha) }}
cancel-in-progress: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/backport.yml around lines 11 - 13, The concurrency group
currently uses github.ref which is identical across push and issue_comment
events and causes unrelated runs to serialize; update the concurrency.group
value (the concurrency: group setting) to include the event name and a
per-request identifier so runs for different PRs/issues can run in parallel—for
example use an expression combining github.event_name and a coalesced identifier
like ${{ github.event.pull_request.number || github.event.issue.number ||
github.sha }} (keep cancel-in-progress: false or adjust as desired) so
concurrency.group becomes something like backport-${{ github.workflow }}-${{
github.event_name }}-${{ github.event.pull_request.number ||
github.event.issue.number || github.sha }} referencing the symbols
concurrency.group, cancel-in-progress, github.ref,
github.event.pull_request.number, github.event.issue.number, github.sha, and
github.event_name.


permissions:
contents: write
pull-requests: write

jobs:
backport-on-push:
if: github.event_name == 'push'
uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pin the external reusable workflow to a commit SHA, not the mutable @master branch.

"Pinning an action to a full-length commit SHA is currently the only way to use an action as an immutable release." A tag (or branch name) can be moved or deleted if a bad actor gains access to the upstream repository, and the same principles apply to third-party reusable workflows. Additionally, when re-running a workflow that uses a reusable workflow and the reference is not a SHA, re-running all jobs will use the reusable workflow from the specified reference at that point in time, making reruns non-deterministic.

🔧 Proposed fix (apply to both lines 22 and 37)
-    uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master
+    uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@<full-commit-sha> # `@master` as of <date>

Retrieve the current SHA with:

gh api repos/openwisp/openwisp-utils/commits/master --jq '.sha'

Also applies to: 37-37

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/backport.yml at line 22, Replace the mutable branch
reference for the reusable workflow with a full commit SHA: locate the two
occurrences of "uses:
openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master" and
replace "@master" with the current full-length commit SHA for
openwisp/openwisp-utils (you can obtain it via `gh api
repos/openwisp/openwisp-utils/commits/master --jq '.sha'`), updating both
instances so the workflow is pinned to that exact commit SHA.

with:
commit_sha: ${{ github.sha }}
secrets:
app_id: ${{ secrets.OPENWISP_BOT_APP_ID }}
private_key: ${{ secrets.OPENWISP_BOT_PRIVATE_KEY }}

backport-on-comment:
if: >
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.issue.pull_request.merged_at != null &&
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

GitHub Actions issue_comment event payload github.event.issue.pull_request merged_at field documentation

💡 Result:

In an issue_comment workflow run, github.event is the webhook payload, and the issue object is the same “issue (or PR-as-issue)” shape you get from the Issues API. GitHub’s Actions docs note you can detect PR comments with github.event.issue.pull_request (it’s present for PRs, absent for issues). [1]

However, for issue_comment, github.event.issue.pull_request is only a link object (e.g., url, html_url, diff_url, patch_url) and does not include PR fields like merged_at. This is shown in the Issues API example response where pull_request contains only those URLs. [2]

To get merged_at, you must fetch the PR itself (e.g., via the Pull Requests API using the URL in github.event.issue.pull_request.url) or at least check merged status via the dedicated endpoint (GET /repos/{owner}/{repo}/pulls/{pull_number}/merge). [3]

Sources
[1] https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows
[2] https://docs.github.com/en/rest/issues/issues
[3] https://docs.github.com/en/rest/pulls/pulls


Remove merged_at check from trigger—this field does not exist in issue_comment payloads.

In issue_comment events, github.event.issue.pull_request contains only link metadata (url, html_url, diff_url, patch_url). The merged_at field is not present, making the current guard ineffective. Move merged status verification into the reusable workflow using the Pull Requests API.

🔧 Proposed fix
       github.event_name == 'issue_comment' &&
       github.event.issue.pull_request &&
-      github.event.issue.pull_request.merged_at != null &&
       github.event.issue.state == 'closed' &&
       contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) &&
       startsWith(github.event.comment.body, '/backport')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
github.event.issue.pull_request.merged_at != null &&
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.issue.state == 'closed' &&
contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) &&
startsWith(github.event.comment.body, '/backport')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/backport.yml at line 32, Remove the invalid condition
reference to github.event.issue.pull_request.merged_at from the issue_comment
trigger in the workflow (the check github.event.issue.pull_request.merged_at !=
null) because merged_at is not present in issue_comment payloads; instead, call
the reusable workflow as-is on issue_comment and move merged-state verification
into the reusable workflow by querying the Pull Requests API for the PR number
(from github.event.issue.pull_request.url or related fields) and confirming
merged status before performing backport logic.

github.event.issue.state == 'closed' &&
contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) &&
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider whether COLLABORATOR should be included in the author association check.

The guard currently restricts comment-triggered backports to MEMBER and OWNER. Repository collaborators with explicit write access (COLLABORATOR association) are excluded. If outside contributors with write access should be able to trigger backports, add "COLLABORATOR" to the list.

🔧 Proposed fix (if collaborators should be allowed)
-      contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) &&
+      contains(fromJSON('["MEMBER", "OWNER", "COLLABORATOR"]'), github.event.comment.author_association) &&
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) &&
contains(fromJSON('["MEMBER", "OWNER", "COLLABORATOR"]'), github.event.comment.author_association) &&
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/backport.yml at line 35, The workflow guard currently
checks contains(fromJSON('["MEMBER", "OWNER"]'),
github.event.comment.author_association) and therefore excludes repository
collaborators; if collaborators with write access should be allowed to trigger
backports, update that array to include "COLLABORATOR" (i.e., add "COLLABORATOR"
to the fromJSON array in the contains(...) check in backport.yml) so the
condition permits MEMBER, OWNER, and COLLABORATOR associations.

startsWith(github.event.comment.body, '/backport')
uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master
with:
pr_number: ${{ github.event.issue.number }}
comment_body: ${{ github.event.comment.body }}
secrets:
app_id: ${{ secrets.OPENWISP_BOT_APP_ID }}
private_key: ${{ secrets.OPENWISP_BOT_PRIVATE_KEY }}