Skip to content

PRRTE/PMIx: advance shas #1792

PRRTE/PMIx: advance shas

PRRTE/PMIx: advance shas #1792

# Slash-command handler for /backport.
#
# Posting a comment on a merged PR with:
#
# /backport v5.0.x v4.1.x
#
# is equivalent to manually triggering the "Backport" workflow from the
# GitHub Actions UI with those branch names. Multiple branches may be
# supplied as space- or comma-separated values on the same line.
#
# Only repository owners, organisation members, and collaborators may trigger
# the command. If an unauthorised user attempts /backport, the bot replies
# with an explanatory comment. For valid commands it acknowledges with a 👀
# reaction; invalid or unrecognised commands get a usage hint as a comment.
name: Backport slash command
on:
issue_comment:
types: [created]
permissions: {}
jobs:
dispatch:
name: Handle /backport comment
runs-on: ubuntu-latest
# Only act on PR comments (issue_comment fires for both issues and PRs).
if: github.event.issue.pull_request != null
permissions:
actions: write # trigger workflow_dispatch
issues: write # post comments
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Parse command and validate PR
id: parse
uses: actions/github-script@v8
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const body = context.payload.comment.body;
const commentId = context.payload.comment.id;
const issueNumber = context.payload.issue.number;
const login = context.payload.comment.user.login;
// Best-effort comment helper — if Issues are disabled on
// the repo (common for forks) the call returns 403 and we
// log a warning rather than aborting the workflow.
async function tryComment(text) {
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: text,
});
} catch (err) {
core.warning(`Could not post comment: ${err.message}`);
}
}
// Detect a bare /backport with no arguments and reply helpfully.
const bareMatch = /^\/backport\s*$/m.test(body);
// Look for /backport with arguments at the start of any line.
const match = body.match(/^\/backport\s+([^\r\n]+)/m);
// If the comment doesn't contain any /backport command at all,
// do nothing — no need to check permissions.
if (!bareMatch && !match) {
core.setOutput('triggered', 'false');
return;
}
// Use author_association from the webhook payload — no extra
// API call required. getCollaboratorPermissionLevel requires
// org-level "Members" read permission, which is not available
// to either GITHUB_TOKEN or a GitHub App without explicit
// org-level permission grants.
const assoc = context.payload.comment.author_association;
if (!['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc)) {
await tryComment(`⚠️ @${login} Backports can only be triggered by repository owners, organization members, or collaborators.`);
core.setOutput('triggered', 'false');
return;
}
if (bareMatch && !match) {
core.setOutput('triggered', 'false');
await tryComment('⚠️ `/backport` requires at least one target branch, e.g. `/backport v5.0.x`.');
return;
}
if (!match) {
core.setOutput('triggered', 'false');
return;
}
// Parse branch list (space- or comma-separated).
const branches = match[1].trim().split(/[\s,]+/).filter(Boolean);
if (branches.length === 0) {
// e.g. "/backport ,,," — separators only, no real branch names
core.setOutput('triggered', 'false');
await tryComment('⚠️ `/backport` requires at least one target branch, e.g. `/backport v5.0.x`.');
return;
}
// Validate branch names with the same allow-list used in
// backport.yaml so the user gets immediate feedback rather
// than a silent dispatch failure.
const validBranchRe = /^[a-zA-Z0-9][a-zA-Z0-9._\-/]*$/;
const invalidBranches = branches.filter(b => !validBranchRe.test(b));
if (invalidBranches.length > 0) {
core.setOutput('triggered', 'false');
await tryComment(`⚠️ Invalid branch name(s): ${invalidBranches.map(b => `\`${b}\``).join(', ')}. Branch names may only contain alphanumeric characters, dots, hyphens, underscores, and slashes.`);
return;
}
// Confirm the PR is actually merged.
// merged_at is present in the issue_comment webhook payload
// for PRs, so no extra API call is needed.
if (!context.payload.issue.pull_request.merged_at) {
await tryComment('⚠️ Cannot backport: this PR has not been merged yet.');
core.setOutput('triggered', 'false');
return;
}
// Acknowledge the command with a 👀 reaction on the triggering comment.
// Ignore 422 (reaction already exists) so re-runs don't fail.
try {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
content: 'eyes',
});
} catch (err) {
if (err.status !== 422) throw err;
}
core.setOutput('triggered', 'true');
core.setOutput('pr_number', String(issueNumber));
core.setOutput('branches', branches.join(','));
core.notice(`Dispatching backport of PR #${issueNumber} to: ${branches.join(', ')}`);
- name: Trigger backport workflow
if: steps.parse.outputs.triggered == 'true'
uses: actions/github-script@v8
env:
PR_NUMBER: ${{ steps.parse.outputs.pr_number }}
BRANCHES: ${{ steps.parse.outputs.branches }}
with:
script: |
// workflow_dispatch requires a ref; use the default branch.
const { data: repo } = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo,
});
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'backport.yaml',
ref: repo.default_branch,
inputs: {
pr_number: process.env.PR_NUMBER,
branches: process.env.BRANCHES,
},
});