Creating a wrapping action on GitHub Actions
02 Nov 2020 · 4 min

In this post I'll describe how you can create an action for GitHub Actions that has a post or a cleanup section. Additionally, I'll show a mechanism to retrieve the job status in the post section.

The post section can be leveraged by actions that want to wrap the build to clean up resources or do some work after all the steps are finished. For example, my action gha-buildevents uses the post section to build and send a trace to Honeycomb.io containing information of the build.

Example source code of a simple wrapping action can be found on GitHub: kvrhdn/gha-wrapping-action-demo.

1. Declare that your action has a post section

First, you have to tell the GitHub Actions runtime that your action has a post section. This is done using the metadata file action.yaml:

# action.yaml

# describe your action here

runs:
    using: node12
    main: dist/index.js
    post: dist/index.js

To break this down, the runs section contains information about how this action should be run:

It's possible to set two different scripts as main and post, but I prefer using a single entrypoint. You can see this being used in actions/checkout as well.

Conditional post section

It's possible to tweak when the post section is run using post-if, you can find more details about this in the documentation.

# action.yaml
runs:
    using: node12
    main: dist/index.js
    post: dist/index.js
    post-if: 'runner.os == linux'

2. Detect when you are in post

Since both entrypoints will execute the same script, how do we know when we are in the main section and when in post?

This can be resolved using state, a mechanism to share information between the main and post section of the same action. When we finish executing the main section, we can store a variable (isPost) in the state to indicate main has finished. Every time the script starts, it checks the state for isPost to decide whether it should run the main or the post logic.

// src/index.ts

import * as core from '@actions/core'

async function run(): Promise<void> {
  core.info('Hello from main')

  // put isPost in the action state
  core.saveState('isPost', true)
}

async function post(): Promise<void> {
  core.info('Hello from post')
}

// check if isPost is present in the action state
const isPost = !!core.getState('isPost')

if (!isPost) {
    run()
} else {
    post()
}

Running this action will result in the following logs:

Full source code of the demo can be found on GitHub: kvrhdn/gha-wrapping-action-demo. Please note this is a minimal example for demonstration purposes, refer to actions/javascript-action or typescript-action for best practices when writing an action.

That's it!


Bis. Get the job status in post

It's currently not possible to get the job status directly from the GitHub Actions runtime environment, while the runtime exposes a lot of information through environment variables the job status is not one of them.

If you always want to run your post section but you need to change your logic based upon the status of the entire job, you won't be able to use post-if. Instead, you can pass the job status as an input variable. In your step definition set this input to ${{ job.status }}, this value will always evaluate to the current job status (even in the post section).

- uses: kvrhdn/gha-wrapping-action-demo
  with:
    # input to pass the current job status to the action
    job-status: ${{ job.status }}

In your action, get this input as usual:

const jobStatus = core.getInput('job-status', { required: true })
core.info(`Job status is ${jobStatus}`)

Possible job statuses I have observed so far are success, errored and canceled.


blog · about · home