A GitHub Action to write comments on GitHub Issues

The GitHub REST API for issues exposes an endpoint to create comments. Because pull requests are implemented as issues, the same endpoint can also be used to comment on pull requests. As the documentation states, “every pull request is an issue, but not every issue is a pull request.”

Permissions required to write comments in an issue

To create a comment, the token used by your workflow must have write access to issues and pull requests. Since the focus of this article is automation, we’ll use GitHub App installation access tokens as the input to the action.

Working with the REST API endpoints for issue comments

In the context of GitHub Actions, one of the simplest ways to interact with the GitHub API is by using the actions/github-script action. It allows you to run an asynchronous JavaScript function and provides a github argument, which is a pre-authenticated Octokit REST client with pagination helpers. Using this client, you can easily call the Issues API, including the endpoint for creating comments, and encapsulate that logic inside your custom action.

The inputs of the action

We are going to include the following inputs for this action:

InputDescription
gh-tokenGitHub token with sufficient permissions to create comments on issues and pull requests.
issue-numberNumeric identifier of the issue or pull request where the comment will be posted
repositoryTarget repository in owner/repository format where the issue or pull request exists.
bodyContent of the comment to be posted on the issue or pull request

The action.yml:

name: github-issue-commenter
description: Create a comment on a GitHub issue or pull request.
inputs:
  gh-token:
    required: true
    description: GitHub token to authenticate with the GitHub API.
  issue-number:
    required: true
    description: The issue or pull request number to comment on.
  comment-body:
    required: true
    description: The body of the comment to create.
  repository:
    required: true
    description: The repository in the format 'owner/repo'.

runs:
  using: "composite"
  steps:
    - name: Create GitHub issue comment
      id: create-issue-comment
      uses: actions/github-script@v7
      with:
        github-token: ${{ inputs.gh-token }}
        script: |
          const [owner, repo] = "${{ inputs.repository }}".split('/');
          if (!owner || !repo) {
            core.setFailed('Invalid repository format. Expected "owner/repo".');
          }

          const issueNumber = parseInt("${{ inputs.issue-number }}", 10);
          if (isNaN(issueNumber) || issueNumber <= 0) {
            core.setFailed('Invalid issue number. It must be a positive integer.');
          }

          const commentBody = '${{ inputs.comment-body }}';
          if (!commentBody) {
            core.setFailed('Comment body cannot be empty.');
          }

          console.log(`Creating comment on issue/pr #${issueNumber} in ${owner}/${repo}`);

          try {
            const response = await github.rest.issues.createComment({
              owner,
              repo,
              issue_number: issueNumber,
              body: commentBody
            });
            console.log(`Comment created: ${response.data.html_url}`);
          } catch (error) {
            console.log('An error occurred while creating the comment:', error);
            core.setFailed('Failed to create comment on the issue or pull request.');
          }

As mentioned, the action uses actions/github-script@v7, which supplies a preconfigured Octokit client (github) and the Actions toolkit (core). Before interacting with the API, the script performs strict input validation to fail fast with deterministic error messages: it splits the repository string to extract owner and repo, rejecting invalid formats; it parses and validates issue-number as a strictly positive integer; and it checks that comment-body is non-empty. Any invalid state results in core.setFailed(…), preventing unnecessary API calls and surfacing clear diagnostics in the workflow logs.

Once inputs are validated, the action logs the target resource and executes github.rest.issues.createComment, which invokes the POST /repos/{owner}/{repo}/issues/{issue_number}/comments endpoint with the provided body. A successful response confirms the operation and prints the created comment’s html_url for traceability. If the API call fails—due to permission issues, non-existent issue/PR, or other server/client errors—the error is logged and the step is explicitly marked as failed via core.setFailed, ensuring downstream jobs or steps can reliably detect and react to the failure state.

This action can be called, as expected, from a GitHub Actions workflow. A previous job there can create a matrix, for instance, to create different jobs for each issue.

| Theme: UPortfolio