32

I am trying to get a regular comment inserted in a pull request from a GitHub action. I can't seem to get it right. Octokit, the underlying library, allows you to create reviewComments to PRs, but those refer to a commit and that's not what I want, I want a simple comment. I figured I can just use octokit.issues.createComment. However, that does not seem to work. Here's the code

import * as core from '@actions/core';
const {GitHub, context} = require('@actions/github')
const parse = require('parse-diff')

async function run() {
    try {
        // get information on everything
        const token = core.getInput('github-token', {required: true})
        const github = new GitHub(token, {} )
        console.log( context )
        const PR_number = context.payload.pull_request.number

        // Check if the body contains required string
        const bodyContains = core.getInput('bodyContains')

        if ( context.payload.pull_request.body.indexOf( bodyContains) < 0  ) {
            core.setFailed("The body of the PR does not contain " + bodyContains);
            console.log( "Actor " + context.actor + " pr number " PR_number)
            const result = await github.issues.createComment({
                owner: context.actor,
                repo: context.payload.repository.full_name,
                issue_number: PR_number,
                body: "We need to have the word " + bodyContains + " in the body of the pull request"
            });
            console.log(result)
       } // more irrelevant stuff below
}}

This simply seems to retur "Not found". I can't seem to be able to find out if it's a type problem, or something different. Theoretically, owner, repo, and issue number, as well as body, should be right, and it's printed correctly. Any help will be appreciated. This is probably a more general question in the realm of GitHub API, with GitHub actions being simply the context, so I might be wrong there.

jjmerelo
  • 22,578
  • 8
  • 40
  • 86
  • 1
    I had a similar need and wanted a bot-like comment inserted by GitHub Actions. I'm going to shamelessly share my repo, which I managed to do the trick with axios and REST call - some example PR and issue are in the repo: https://github.com/rytswd/respost/ – Ryota Sep 24 '19 at 15:30

6 Answers6

62

A canonical way is using the official Github Script actions. Don't get confused, issues and PRs are the same for the GitHub API.

2020:

on:
  # Trigger the workflow on push or pull request
  pull_request:
    branches:
      - master
      - develop

jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v3
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            github.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: ' Thanks for reporting!'
            })

Seen on: https://github.com/actions/github-script#comment-on-an-issue

EDIT 2021:

Adapting to GH's newer versions, one has to reference the rest client directly. See note as of writing.

on:
  # Trigger the workflow on push or pull request
  pull_request:
    branches:
      - master
      - develop

jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v5
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: ' Thanks for reporting!'
            })

EDIT 2023:

V6 is out, here's how to use it. No breaking changes, just the script version has changed.

on:
  # Trigger the workflow on push or pull request
  pull_request:
    branches:
      - master
      - develop

jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v6
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: ' Thanks for reporting!'
            })

Kevin Goedecke
  • 1,553
  • 2
  • 14
  • 26
  • 2
    I don't like this 2020 approach. Touching real API endpoints with `curl` should be the way to do it. Otherwise, this all github actions thingie is useless as a script, as it uses another "script", thus obscuring the intent and jeopardizing itself. As a result, making you dumb, cause you don't know (or have control over, rather) what's going on. – Sevastyan Savanyuk Aug 11 '21 at 11:32
  • 7
    @SevastyanSavanyuk OP's wording could have been less opinionated, but once you are in the GH ecosystem, I think wanting to be less dependent on it is kind of not necessary. Having worked a lot with actions, this is the best way to go imo – eljefedelrodeodeljefe Nov 27 '21 at 14:25
  • 2
    As for "Don't get confused, issues and PRs are the same for the GitHub API.", check https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts#L55-L62 – yskkin Dec 21 '21 at 01:45
  • hello @kevin-goedecke, the 6th version of actions/github-script is now available, you should update your snippet – Hassan ALAMI Mar 09 '22 at 15:51
  • 4
    Shouldn't this be triggered by `pull_request_target` instead of `pull_request`? In a run triggered by `pull_request`, `GITHUB_TOKEN` doesn't have write access to the repository and causes the error 'Resource not accessible by integration'. – David Lechner Mar 30 '22 at 17:20
  • @DavidLechner yes we are having this issue - 'Resource not accessible by integration' what is the solution here? – Kay Jun 08 '23 at 10:12
  • My answer doesn't have may votes yet, so it is farther down the page. https://stackoverflow.com/a/71683208/1976323 – David Lechner Jun 09 '23 at 14:04
  • 1
    @Kay I recently solved the 'Resource not accessible by integration' issue from a PR. You need to add the `pull_requests: write` permission to your GitHub workflow. Examples can be found here: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs – wjkw1 Jul 17 '23 at 04:39
  • 1
    @wjkw1: it seems to be `pull-requests: write` with a hyphen, rather than an underscore? – sam-w Aug 10 '23 at 07:11
  • @sam-w Yes you're correct! Use a hyphen folks, not an underscore: e.g. `pull-requests: write` – wjkw1 Aug 10 '23 at 23:30
28

I initially tried to use Respost, but it doesn't allow setting a raw body string.

So here's a way to do it with curl.

In a .github/workflows/whatever.yml:

name: Some workflow

on:
  issue_comment:
    types: [created]

jobs:
  comment:
    if: contains(github.event.comment.body, 'special string')

    runs-on: ubuntu-latest

    steps:
      - name: Add comment to PR
        env:
          URL: ${{ github.event.issue.comments_url }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          curl \
            -X POST \
            $URL \
            -H "Content-Type: application/json" \
            -H "Authorization: token $GITHUB_TOKEN" \
            --data '{ "body": "blah blah" }'
slow
  • 810
  • 9
  • 14
  • 11
    For the event `pull_request` I had to use `${{ github.event.pull_request.comments_url }}` for the URL – binaryfunt Jul 06 '20 at 21:44
  • 1
    for help with string substitution see here for single/double quotes https://stackoverflow.com/questions/13799789/expansion-of-variables-inside-single-quotes-in-a-command-in-bash – bubbaspike Sep 15 '21 at 15:59
9

You can also use the @actions/github which will permit you to use an octokit client to simplify the actions :

const core = require('@actions/core');
const github = require('@actions/github');

async function run() {
  try {
    const message = core.getInput('message');
    const github_token = core.getInput('GITHUB_TOKEN');

    const context = github.context;
    if (context.payload.pull_request == null) {
        core.setFailed('No pull request found.');
        return;
    }
    const pull_request_number = context.payload.pull_request.number;

    const octokit = new github.GitHub(github_token);
    const new_comment = octokit.issues.createComment({
        ...context.repo,
        issue_number: pull_request_number,
        body: message
      });

  } catch (error) {
    core.setFailed(error.message);
  }
}

run();

Taken from this repo.

Térence
  • 456
  • 4
  • 5
  • Here's a demo as standalone workflow: [workflow.yml](https://github.com/cirosantilli/china-dictatorship/blob/97408314e85673e20c10c1232de8d46f8cdd6004/.github/workflows/issue.yml), [main.js](https://github.com/cirosantilli/china-dictatorship/blob/97408314e85673e20c10c1232de8d46f8cdd6004/action.js). But yeah, just use `actions/github-script` most likely: https://stackoverflow.com/a/64126737/895245 – Ciro Santilli OurBigBook.com Apr 19 '21 at 19:50
7

The other answers don't mention is the security restrictions of a GitHub action run from a fork that triggers the pull_request event. The GITHUB_TOKEN in these actions does not have write access to the repository and therefore cannot create a comment. See permissions for the GITHUB_TOKEN.

The GitHub docs for the workflow_run event have a good example of how to work around this. The basic idea is to have the workflow that is triggered by the pull_request event upload any info needed in the comment as a build artifact using actions/upload-artifact. Then a separate workflow triggered by a workflow_run event downloads the information using actions/download-artifact.

NOTE: For security, since the workflow triggered by workflow_run has write access, it must be committed to the default branch before it can be used. (Also keep in mind that the build artifacts could contain malicious data from a malicious pull request).

Here is a copy of the example workflows from the linked docs (in case the link breaks or the docs change):

name: Upload data

on:
  pull_request:

jobs:
  upload:
    runs-on: ubuntu-latest

    steps:        
      - name: Save PR number
        env:
          PR_NUMBER: ${{ github.event.number }}
        run: |
          mkdir -p ./pr
          echo $PR_NUMBER > ./pr/pr_number
      - uses: actions/upload-artifact@v3
        with:
          name: pr_number
          path: pr/
name: Use the data

on:
  workflow_run:
    workflows: [Upload data]
    types:
      - completed

jobs:
  download:
    runs-on: ubuntu-latest
    steps:
      - name: 'Download artifact'
        uses: actions/github-script@v5
        with:
          script: |
            let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
               owner: context.repo.owner,
               repo: context.repo.repo,
               run_id: context.payload.workflow_run.id,
            });
            let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
              return artifact.name == "pr_number"
            })[0];
            let download = await github.rest.actions.downloadArtifact({
               owner: context.repo.owner,
               repo: context.repo.repo,
               artifact_id: matchArtifact.id,
               archive_format: 'zip',
            });
            let fs = require('fs');
            fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr_number.zip`, Buffer.from(download.data));

      - name: 'Unzip artifact'
        run: unzip pr_number.zip

      - name: 'Comment on PR'
        uses: actions/github-script@v5
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            let fs = require('fs');
            let issue_number = Number(fs.readFileSync('./pr_number'));
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: issue_number,
              body: 'Thank you for the PR!'
            });
David Lechner
  • 1,433
  • 17
  • 29
1

That's what works for me, when I want to comment on a PR from a push event (not pull_request event, where it's easy):

      - uses: actions/github-script@v6
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: (await github.rest.repos.listPullRequestsAssociatedWithCommit({
                commit_sha: context.sha,
                owner: context.repo.owner,
                repo: context.repo.repo,
              })).data[0].number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: ' Thanks for reporting!'
            })

The key part is this, which allows to get the right "issue number" for a PR:

await github.rest.repos.listPullRequestsAssociatedWithCommit({
                commit_sha: context.sha,
                owner: context.repo.owner,
                repo: context.repo.repo,
              })).data[0].number
Kirill Groshkov
  • 1,535
  • 1
  • 22
  • 23
1

I like the GitHub action Comment Pull Request very much, because it allows for updating comments.

Example for adding a comment:

- name: Comment PR
  uses: thollander/actions-comment-pull-request@v2
  with:
    message: |
      Hello world ! :wave:
    reactions: eyes, rocket

Updating a comment is done via "tags" (which render as "hidden" HTML elements)

- name: Comment PR with execution number
  uses: thollander/actions-comment-pull-request@v2
  with:
    message: |
      _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_
    comment_tag: execution
koppor
  • 19,079
  • 15
  • 119
  • 161