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:
using
indicates whether this plugin is a Javascript (node12
) or a Docker action (docker
)main
is the script that will be run firstpost
is the script that will be run as post action
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
.