GitHubActionsBot #1
This file contains 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: GitHubActionsBot | |
on: | |
issue_comment: | |
types: | |
- created | |
# We need to be call in context of the main branch to have write permissions | |
# "pull_request" target is called in context of a fork | |
# "pull_request_target" is called in context of the repository but not necessary latest main | |
workflow_run: | |
workflows: | |
- PullRequestOpened | |
types: | |
- completed | |
env: | |
SUPPORTED_COMMANDS: | | |
<details> | |
<summary> Supported commands </summary> | |
Please post this commands in separate comments and only one per comment: | |
* `@github-actions run-benchmark` - Run benchmark comparing base and merge commits for this PR | |
* `@github-actions publish-pr-on-npm` - Build package from this PR and publish it on NPM | |
</details> | |
permissions: {} | |
jobs: | |
hello-message: | |
if: github.event_name == 'workflow_run' | |
runs-on: ubuntu-latest | |
permissions: | |
actions: read # to download event.json | |
pull-requests: write # to add comment to pull request | |
steps: | |
- name: Download event.json | |
run: gh run download "$WORKFLOW_ID" --repo "$REPO" --name event.json | |
env: | |
REPO: ${{ github.repository }} | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
WORKFLOW_ID: ${{github.event.workflow_run.id}} | |
- name: Add comment on PR | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require('node:fs'); | |
const event = JSON.parse(fs.readFileSync('./event.json', 'utf8')); | |
await github.rest.issues.createComment({ | |
...context.repo, | |
issue_number: event.pull_request.number, | |
body: | |
`Hi @${event.sender.login}, I'm @github-actions bot happy to help you with this PR 👋\n\n` + | |
process.env.SUPPORTED_COMMANDS, | |
}) | |
accept-cmd: | |
if: | | |
github.event_name == 'issue_comment' && | |
github.event.issue.pull_request && | |
startsWith(github.event.comment.body, '@github-actions ') | |
runs-on: ubuntu-latest | |
permissions: | |
pull-requests: write # to add comment to pull request | |
outputs: | |
cmd: ${{ steps.parse-cmd.outputs.cmd }} | |
pull_request_json: ${{ steps.parse-cmd.outputs.pull_request_json }} | |
steps: | |
- uses: actions/github-script@v7 | |
with: | |
script: | | |
await github.rest.reactions.createForIssueComment({ | |
...context.repo, | |
comment_id: context.payload.comment.id, | |
content: 'eyes', | |
}); | |
- id: parse-cmd | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const comment = context.payload.comment.body; | |
core.setOutput('cmd', comment.replace('@github-actions ', '').trim()); | |
const { url } = context.payload.issue.pull_request; | |
const { data } = await github.request(url); | |
core.setOutput('pull_request_json', JSON.stringify(data, null, 2)); | |
cmd-publish-pr-on-npm: | |
needs: [accept-cmd] | |
if: needs.accept-cmd.outputs.cmd == 'publish-pr-on-npm' | |
permissions: | |
contents: read # for actions/checkout | |
uses: ./.github/workflows/cmd-publish-pr-on-npm.yml | |
with: | |
pull_request_json: ${{ needs.accept-cmd.outputs.pull_request_json }} | |
secrets: | |
npm_canary_pr_publish_token: ${{ secrets.npm_canary_pr_publish_token }} | |
cmd-run-benchmark: | |
needs: [accept-cmd] | |
if: needs.accept-cmd.outputs.cmd == 'run-benchmark' | |
permissions: | |
contents: read # for actions/checkout | |
actions: read # to list workflow runs | |
uses: ./.github/workflows/cmd-run-benchmark.yml | |
with: | |
pull_request_json: ${{ needs.accept-cmd.outputs.pull_request_json }} | |
respond-to-cmd: | |
needs: | |
- accept-cmd | |
- cmd-publish-pr-on-npm | |
- cmd-run-benchmark | |
if: needs.accept-cmd.result != 'skipped' && always() | |
runs-on: ubuntu-latest | |
permissions: | |
pull-requests: write # to add comment to pull request | |
steps: | |
- uses: actions/download-artifact@v4 | |
with: | |
name: replyMessage | |
- if: failure() | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require('node:fs'); | |
const needs = JSON.parse(process.env.NEEDS); | |
let allSkipped = true; | |
for (const [ name, job ] of Object.entries(needs)) { | |
if (name.startsWith('cmd-')) { | |
allSkipped = allSkipped && job.result === 'skipped'; | |
} | |
} | |
const replyMessage = allSkipped | |
? 'Unknown command 😕\n\n' + process.env.SUPPORTED_COMMANDS | |
: `Something went wrong, [please check log](${process.env.RUN_URL}).`; | |
fs.writeFileSync('./replyMessage.txt', replyMessage); | |
env: | |
NEEDS: ${{ toJSON(needs) }} | |
RUN_URL: ${{github.server_url}}/${{github.repository}}/actions/runs/${{github.run_id}} | |
- if: always() | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require('node:fs'); | |
const replyMessage = fs.readFileSync('./replyMessage.txt', 'utf-8'); | |
const { issue, comment, sender } = context.payload; | |
const quoteRequest = comment.body | |
.split('\n') | |
.map((line) => '> ' + line) | |
.join('\n'); | |
await github.rest.issues.createComment({ | |
...context.repo, | |
issue_number: issue.number, | |
body: quoteRequest + `\n\n@${sender.login} ` + replyMessage, | |
}); | |
// `github.rest` doesn't have this method :( so use graphql instead | |
await github.graphql(` | |
mutation ($subjectId: ID!) { | |
minimizeComment(input: { subjectId: $subjectId, classifier: RESOLVED}) | |
{ __typename } | |
} | |
`, { subjectId: comment.node_id }); |