Skip to main content

Write GitHub Action: Checks and Annotations API (Part 2)

ยท 5 min read

In the first blog post of this series, I have explained how to create a custom GitHub Action that is interesting when you do not find the action that you need on the GitHub Marketplace.

I will now focus on some interesting API that you can use when building an action: Checks & Annotations.

It is import, when a workflow is running to provide visual feedback to the user. This is where the checks and annotation API is handy as it allows you for example indicates to the user that a specific step has failed ( โŒ ) or was successfully ( โœ… ) executed; and using the API it is also possible to create a detailed annotation that points to a specific line of code; this helping the user to understand what is going on in the workflow.

The following screenshot shows the annotation API in action:

Workflow Annotations

๐Ÿ“— In this second post, you will learn how to:

  1. Create custom Checks
  2. Add some detailed annotation with reference to source code (lines) with error
  3. Deploy the action

If you prefer the video version of this post go to Github Actions: Create custom Checks and Annotations .

It is time now to dive into the example!

In this new example, you will simply add few lines of code to the action that you have created in the previous post. If you have not yet created the action, please refer to the previous post or take the source from the v1.0.0 release of the tgrall/check-files-action.

In the first version, the action simply use core.info() to log a message or core.setFailed() to raise an annotation.

The goal now, is to check if the README.md file starts with a title, and if not, raise create an annotation that point to the first line of the file. (As mentioned in the previous post, I am not trying to create an action that you can reuse directly but use this to discover and learn the API related to Actions & Workflows)

Check if the README.md file starts with a title#

So we need to add a new function in our action that check if the file starts with a title (a markdown header using the # character).

Creating a new function to test the content of the README.md file#

I have used GitHub Copilot to create a new function that read the file and chec if it starts with a title, inclusing a small regular expression to remove all empty lines.

// create a function that checks if the file starts with a markdown headerasync function checkFileStartsWithHeader(filePath) {    return fs.promises.readFile(filePath, 'utf8')    .then(fileContent => {
        // remove all empty lines ad the beginning of the file        fileContent = fileContent.replace(/^\s*\n/gm, '');
        if (fileContent.startsWith('#')) {            core.info(`File ${filePath} starts with a header`);            return true;        } else {            core.setFailed(`File ${filePath} does not start with a header`);            return false;        }    });}

This function receive the path to the file, open and read the content, then:

  • on line 7, remove all the empty lines of the file using a regular expression generated by GitHub Copilot. (See the video to view it in action; also I remove all the empty line for simplicity)
  • on line 9, the function checks if the string start with a # character. If it does, it returns true, otherwise it returns false.

I kept the comments in the snippet to let you test GitHub Copilot if you have access to technical preview.

Calling the new function#

Before testing the new version of the action, you must call this method from the main method of the action.

...    checkFileStartsWithHeader("README.md");...

Creating a Check with Annotation#

Now that the aciton has been tested, I will create a new check and annotation.

    ...        if (            ! await checkFileStartsWithHeader("README.md")        ) {            // get token for octokit            const token = core.getInput('repo-token');            const octokit = new github.getOctokit(token);

            // call octokit to create a check with annotation and details            const check = await octokit.rest.checks.create({                owner: github.context.repo.owner,                repo: github.context.repo.repo,                name: 'Readme Validator',                head_sha: github.context.sha,                status: 'completed',                conclusion: 'failure',                output: {                    title: 'README.md must start with a title',                    summary: 'Please use markdown syntax to create a title',                    annotations: [                        {                            path: 'README.md',                            start_line: 1,                            end_line: 1,                            annotation_level: 'failure',                            message: 'README.md must start with a header',                            start_column: 1,                            end_column: 1                        }                    ]                }            });        }    ...           
  • line 3: call the function to check if the file starts with a title, and if not, raise an annotation
  • lines 6-7: get the token for octokit, this will allow the action to call the GitHub REST API (see line 11)
  • line 11: call the GitHub REST API to create a check with annotation and details.

This brief call is creating a new Check with an Annotation to point the user to the proper file and lines, as you can see below, the user is informed that the line 1 in README.md should be a title.

Workflow Annotations

The annotation objects are defined in the GitHub API documentation.

Video#