Support explicit Deployment and pod annotations for HyperDX #15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Alert on external contributions | |
| on: | |
| issues: | |
| types: [opened, reopened] | |
| pull_request_target: | |
| types: [opened, reopened, ready_for_review] | |
| permissions: | |
| issues: write | |
| pull-requests: write | |
| concurrency: | |
| group: external-contrib-${{ github.event.issue.number || github.event.pull_request.number }} | |
| cancel-in-progress: false | |
| jobs: | |
| alert: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/github-script@v9 | |
| env: | |
| SLACK_WEBHOOK_OSS_PRS: ${{ secrets.SLACK_WEBHOOK_OSS_PRS }} | |
| with: | |
| script: | | |
| const LABEL = 'external'; | |
| const item = context.payload.issue ?? context.payload.pull_request; | |
| if (!item) return; | |
| if (item.draft) { core.info(`Skipping draft PR #${item.number}`); return; } | |
| if (item.user?.type === 'Bot') { core.info(`Skipping bot ${item.user?.login}`); return; } | |
| const { owner, repo } = context.repo; | |
| // fast path (free): public members & direct collaborators | |
| let internal = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(item.author_association); | |
| // slow path: catches PRIVATE org members via their granted repo access. | |
| // author_association only reports MEMBER for *public* members, so private | |
| // members would otherwise be misflagged as external. Public repos grant | |
| // everyone implicit 'read', so require triage+ (not permission != 'none'). | |
| if (!internal) { | |
| try { | |
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner, repo, username: item.user.login, | |
| }); | |
| const p = data.user?.permissions ?? {}; | |
| internal = !!(p.admin || p.maintain || p.push || p.triage); | |
| } catch (e) { | |
| if (e.status !== 404) throw e; // 404 = not a collaborator => external | |
| } | |
| } | |
| if (internal) { | |
| core.info(`#${item.number} by ${item.user?.login} is internal; skipping.`); | |
| return; | |
| } | |
| if (item.labels?.some(l => l.name === LABEL)) { core.info(`#${item.number} already handled.`); return; } | |
| const webhook = process.env.SLACK_WEBHOOK_OSS_PRS; | |
| if (!webhook) { | |
| core.warning('SLACK_WEBHOOK_OSS_PRS not set — no alert sent and not labeled (will retry on next event).'); | |
| return; | |
| } | |
| const kind = context.payload.pull_request ? 'PR' : 'issue'; | |
| const text = `:sparkles: *New external ${kind}* in \`${owner}/${repo}\`\n` | |
| + `<${item.html_url}|#${item.number}: ${item.title}>\n` | |
| + `by \`${item.user.login}\` (${item.author_association})`; | |
| let delivered = false; | |
| try { | |
| const res = await fetch(webhook, { | |
| method: 'POST', headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ blocks: [{ type: 'section', text: { type: 'mrkdwn', text } }] }), | |
| }); | |
| delivered = res.ok; | |
| if (!delivered) core.setFailed(`Slack post failed (${res.status}); leaving #${item.number} unlabeled to retry.`); | |
| } catch (e) { | |
| core.setFailed(`Slack post errored (${e.message}); leaving #${item.number} unlabeled to retry.`); | |
| } | |
| if (!delivered) return; | |
| try { | |
| await github.rest.issues.createLabel({ | |
| owner, repo, name: LABEL, color: 'D4C5F9', | |
| description: 'Opened by an external contributor', | |
| }); | |
| core.info(`Created '${LABEL}' label.`); | |
| } catch (e) { | |
| if (e.status !== 422) throw e; | |
| } | |
| await github.rest.issues.addLabels({ owner, repo, issue_number: item.number, labels: [LABEL] }); | |
| core.info(`Alerted Slack and labeled #${item.number} (${item.author_association}).`); |