Architecting Custom Feedback Loops with GitHub Check Runs and Apps

The integration of continuous integration and deployment (CI/CD) into version control workflows has evolved from simple pass/fail status indicators to complex, interactive feedback systems. At the core of this modernization is the GitHub Checks API, which allows developers and platform engineers to create granular, detailed, and interactive feedback directly on pull requests and commits. While standard GitHub Actions jobs automatically generate check runs, the true power of the Checks API lies in its ability to support custom GitHub Apps, detailed code annotations, interactive buttons, and rich media content. This capability transforms a static code review process into a dynamic, automated engineering workflow.

The Distinction Between Actions and Check Runs

In the GitHub ecosystem, the terms "Actions" and "Check Runs" are often conflated because every job in a GitHub Actions workflow automatically generates a check run. This automatic generation is sufficient for most standard testing scenarios, such as running unit tests or linting code. If a workflow is broken down into smaller, logical jobs, each job will appear as a distinct check run in the pull request interface. However, relying solely on the automatic check runs generated by standard workflow jobs limits the depth of feedback that can be provided.

To unlock advanced capabilities—such as adding specific annotations to lines of code, displaying images, or providing interactive action buttons—developers must utilize the Check Runs API directly. This is typically achieved through a GitHub App rather than a standard user or OAuth token. The Checks API allows for the creation of check runs that are decoupled from the immediate execution context of a workflow job. This separation is critical when the analysis requires external processing, long-running tasks, or integration with third-party security and compliance tools that operate outside the standard GitHub Actions environment.

Permission Models and API Access

A fundamental constraint when working with the Checks API is the strict permission model enforced by GitHub. Write access to the endpoints that create and manage check runs is exclusively reserved for GitHub Apps. Standard OAuth apps and authenticated users possess read-only access; they can view check runs and check suites but cannot create them. This design choice ensures that only verified, application-level entities can inject automated feedback into the code review process, reducing the risk of malicious or erroneous check runs cluttering the interface.

For organizations not building a dedicated GitHub App, the alternative is to use the Commit Status API. While less feature-rich than the Checks API, the Commit Status API allows for simpler pass/fail indicators without the complexity of app registration and management. However, for teams seeking to implement sophisticated feedback loops, the GitHub App route is the standard. To utilize the Check Runs endpoints, the GitHub App must be granted the checks:write permission. Additionally, the app can subscribe to the check_run and check_suite webhooks to receive real-time notifications about the state of checks, enabling reactive workflows where the app responds to events initiated by other systems or users.

Lifecycle and State Management

A check run represents an individual test or analysis task that is part of a broader check suite. A check suite groups together all the check runs associated with a specific commit. When a check run is created without an explicit check suite, GitHub automatically generates one. The lifecycle of a check run is defined by its status and conclusion properties, which change as the analysis progresses.

The status field indicates the current operational state of the check run. Possible values include queued, in_progress, requested, waiting, pending, and completed. It is important to note that only GitHub Actions can set the status to requested, waiting, or pending. For external GitHub Apps, the typical workflow begins with a queued status upon receiving a check_suite webhook. As the app begins processing, it updates the status to in_progress. Finally, when the analysis is finished, the status becomes completed, at which point the conclusion field is populated.

The conclusion field provides the final result of the check run. When the status is completed, the conclusion can be one of the following: success, failure, neutral, cancelled, timed_out, skipped, or action_required. The action_required conclusion is particularly useful for interactive checks, signaling to the user that manual intervention is needed. If a check run remains in an incomplete state for more than 14 days, GitHub automatically marks its conclusion as stale. This stale status is managed solely by GitHub and cannot be manually set by applications, ensuring that abandoned checks do not indefinitely block pull request merges or confuse reviewers.

Rich Output: Annotations and Media

One of the most significant advantages of using the Checks API over simple commit statuses is the ability to provide rich, contextual feedback. This is achieved through the output object, which can be updated throughout the lifecycle of the check run. The output object supports a title, a summary, and details. Both the summary and details fields support up to 64KB of content and render Markdown, allowing for formatted text, tables, and links.

Beyond text, the output object can include images via the images parameter. These images are displayed between the summary and details sections and are scaled to 100% width, making them ideal for displaying charts, screenshots of UI tests, or visualizations of code coverage. This capability is particularly valuable for performance testing or security scanning tools that benefit from visual data representation.

Annotations provide a more granular level of feedback by attaching specific messages to lines of code in the repository. Each annotation includes an annotation_level (which can be notice, warning, or failure), a path (the file name), and start_line and end_line parameters to pinpoint the exact location of the issue. The annotation also includes a message describing the finding. These annotations appear directly in the pull request file view, allowing developers to see exactly where linting errors, security vulnerabilities, or style violations occur without leaving the code review interface.

Interactive Actions and Workflow Integration

The Checks API supports interactivity through "requested actions." This feature allows a GitHub App to display a button in the pull request interface, enabling users to trigger additional tasks. For example, a code linting app might detect syntax errors and offer a "Fix this" button that automatically attempts to resolve the issues. When defining a check run, the actions object can be used to configure these buttons. Each action includes a label (the text displayed on the button), a description (tooltip text), and an identifier (a unique key used to identify the action when triggered).

When a user clicks the action button, GitHub sends a check_run.requested_action webhook to the GitHub App. The app can then process the request, update the check run, and provide feedback on the result. This creates a closed-loop interaction where the feedback mechanism is not just passive information but an active tool for code improvement.

To facilitate complex workflows, developers often use a hybrid approach involving GitHub Actions and external systems. One common pattern is to create a check run within a GitHub Actions workflow and pass the check run ID to an external system via a repository dispatch event. The external system, operating as a GitHub App, then uses this ID to update the check run's status and output as processing occurs. This decouples the initial trigger from the long-running analysis, allowing for robust, scalable integrations.

Below is an example of how a GitHub Action can create a check run and dispatch the ID to a separate workflow for processing:

```bash
echo "checkId=$CHECKID" >> $GITHUB_OUTPUT

  • name: Repository Dispatch
    env:
    GHTOKEN: ${{ github.token }}
    run: |
    #########################################################
    # Create a repository
    dispatch event of type my-check
    # Send the SHA and the Check Run ID in the clientpayload
    #########################################################
    gh api -X POST -H "Accept: application/vnd.github+json" \
    -H "X-GitHub-Api-Version: 2022-11-28" \
    -f 'event
    type=my-check' \
    -f 'clientpayload[checkRunId]=${{ steps.checkrun.outputs.checkId }}' \
    -f 'client
    payload[sha]=${{ github.sha }}' \
    /repos/${{ github.repository }}/dispatches
    ```

The receiving workflow can then use the GitHub CLI to update the check run status. For instance, acknowledging the request and setting the status to in_progress:

bash - name: Acknowledge Request env: GH_TOKEN: ${{ github.token }} run: | gh api -X PATCH -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ -f 'status=in_progress' \ -f 'output[title]=Processing Request' \ /repos/${{ github.repository }}/check-runs/${{ github.event.client_payload.checkRunId }}

This pattern illustrates the flexibility of the Checks API. By leveraging repository dispatch events and the GitHub CLI, engineers can build sophisticated, multi-stage feedback systems that integrate external tools seamlessly into the GitHub workflow. The ability to update the status and output at various stages—queued, in_progress, and completed—ensures that developers always have visibility into the progress of automated checks, reducing uncertainty and improving the efficiency of the code review process.

Conclusion

GitHub Check Runs represent a significant advancement in developer tooling, moving beyond simple status indicators to provide rich, interactive, and contextual feedback. By leveraging the Checks API through GitHub Apps, organizations can implement sophisticated workflows that include detailed code annotations, visual media, and interactive action buttons. The strict permission model ensures security and reliability, while the flexible state management allows for complex, multi-stage integrations with external systems. As development processes become increasingly automated and complex, the ability to provide granular, actionable feedback directly within the code review interface is essential for maintaining code quality and accelerating delivery. The Checks API provides the necessary foundation for building these advanced integrations, enabling teams to create a more informed and efficient development experience.

Sources

  1. GitHub Checks Action
  2. Using the REST API to Interact with Checks
  3. GitHub Check Runs Action
  4. Creating GitHub Checks

Related Posts