workflows: add Discord notification for ready PRs (#33655)
* workflows: add Discord notification for ready PRs Adds a new workflow that posts to Discord whenever a pull request is opened as non-draft or marked as ready for review. Uses jq to safely construct the JSON payload and sends it via curl to a webhook URL stored in repository secrets. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor * workflows: harden Discord webhook notification Add early guard for missing webhook secret, disable Discord mention parsing to prevent abuse via PR titles/bodies, and check the HTTP response to surface webhook failures. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor * workflows: upgrade github-script to v8 and truncate embed title Align actions/github-script pin with the rest of the repo (v8.0.0) and truncate the Discord embed title to the 256-character limit to prevent webhook failures on long PR titles. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor --------- Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
name: Discord PR notification
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, ready_for_review]
|
||||
|
||||
permissions:
|
||||
actions: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
issues: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
# Only run for non-draft PRs in the main repo
|
||||
if: github.repository == 'backstage/backstage' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Send Discord notification
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
DISCORD_PULL_REQUESTS_WEBHOOK: ${{ secrets.DISCORD_PULL_REQUESTS_WEBHOOK }}
|
||||
with:
|
||||
script: |
|
||||
if (!process.env.DISCORD_PULL_REQUESTS_WEBHOOK) {
|
||||
throw new Error('DISCORD_PULL_REQUESTS_WEBHOOK secret is not set');
|
||||
}
|
||||
|
||||
const { pull_request: pr, action } = context.payload;
|
||||
|
||||
let description = (pr.body || '').slice(0, 300);
|
||||
if ((pr.body || '').length > 300) {
|
||||
description += '...';
|
||||
}
|
||||
|
||||
// Discord embed titles are limited to 256 characters
|
||||
const titlePrefix = `#${pr.number}: `;
|
||||
const maxTitleLength = 256 - titlePrefix.length;
|
||||
let prTitle = pr.title || '';
|
||||
if (prTitle.length > maxTitleLength) {
|
||||
prTitle = prTitle.slice(0, maxTitleLength - 3) + '...';
|
||||
}
|
||||
|
||||
const isOpened = action === 'opened';
|
||||
const color = isOpened ? 5763719 : 3447003; // green : blue
|
||||
const footer = isOpened ? 'New pull request' : 'Ready for review';
|
||||
|
||||
const payload = {
|
||||
embeds: [{
|
||||
title: `${titlePrefix}${prTitle}`,
|
||||
url: pr.html_url,
|
||||
description,
|
||||
color,
|
||||
author: {
|
||||
name: pr.user.login,
|
||||
url: pr.user.html_url,
|
||||
icon_url: pr.user.avatar_url,
|
||||
},
|
||||
footer: { text: footer },
|
||||
}],
|
||||
allowed_mentions: { parse: [] },
|
||||
};
|
||||
|
||||
const response = await fetch(process.env.DISCORD_PULL_REQUESTS_WEBHOOK, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const body = await response.text().catch(() => '');
|
||||
throw new Error(`Discord webhook failed: ${response.status} ${response.statusText} - ${body}`);
|
||||
}
|
||||
Reference in New Issue
Block a user