Communicate between your Github Action Steps with custom outputs
Most of the time, Github Workflow are about chaining actions executed on some event:
on: (1)
push:
branches:
- main
jobs:
build: (2)
runs-on: ubuntu-latest
steps: (3)
- name: Step1
...
- name: Step2
...
1 | trigger/event which will launch the job(s) |
2 | one "job" = chain of steps |
3 | the steps definition |
An action often looks like the followin one:
- name: Do Something (1)
id: my_id (2)
uses: actions/some-action@v1 (3)
with: (4)
some_key: ${{ some_value }} (5)
1 | A name used in the Github Actions UI |
2 | An identifier used if the action has some output (you can then use it in another action with steps.<id>.outputs.<output name> ) |
3 | The action reference with its version (github repository with tag) |
4 | The action configuration |
5 | Configuration can use interpolated values, including the previous steps outputs. |
So everything is well chained and works well…until you need some particular value.
An example is to parse a tag name in a repository having multiple products.
Concretely, you create tags with this pattern: <product>/<version>
.
If you have a workflow which builds automatically the product when it is tagged you likely want something like - just a pseudo-workflow:
on: (1)
push:
tags:
- '*/*.*.*'
build:
runs-on: ubuntu-latest
steps:
- name: Checkout (2)
uses: actions/checkout@v2
- name: Login to DockerHub (3)
uses: docker/login-action@v1
if: startsWith( github.ref, 'refs/tags/')
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push (4)
id: docker_build
uses: docker/build-push-action@v2
if: startsWith( github.ref, 'refs/tags/')
with: (5)
push: true
context: ./${{ steps.parsed_tag.outputs.image }}
file: ./${{ steps.parsed_tag.outputs.image }}/Dockerfile
tags: myregistry.com/myorg/${{ steps.parsed_tag.outputs.image }}:${{ steps.parsed_tag.outputs.version }}
1 | When a tag is pushed - so a released is created |
2 | Clone the repository |
3 | Ensure your docker daemon is authenticated to be able to push images to a docker registry |
4 | Build the image and push it to your organisation |
5 | You can see that the configuration requires to get the image and version from the tag. |
Last step will, indeed, not run in current version because steps.parsed_tag.outputs
does not exist since we don’t have a step parsed_tag
.
The trick is to parse the tag (refs/tags/<product>/<version>
) to extract the exact version.
You have the choice to have a look to an action doing almost what you want - and potentially chain some actions to do exactly the needed parsing, or to do the parsing yourself - since you can always pass a bash command and/or a command in a custom docker image in a step.
Indeed we will use last option here.
The build will get github.ref
placeholder which looks like refs/tags/<product>/<version>
.
Parsing that in bash looks like:
ref=${{ github.ref }}
image=$(echo "$ref" | cut -d '/' -f 3)
version=$(echo "$ref" | cut -d '/' -f 4)
Then all the trick is to export image
and version
in a step output.
The nice thing with Github Action is that there is a protocol to write log lines, errors, … and outputs through the output of the program (stdout).
The syntax is ::set-output name=<output name>::<output value>
.
Concretely, for our bash script we just need to do:
echo "::set-output name=image::$image"
echo "::set-output name=version::$version"
Now we need to insert out bash script execution before the last step of our previous job (build
) and force its id
to parsed_tag
to match the placeholders used in the last step:
- name: Parsed tag
id: parsed_tag
run: |
ref=${{ github.ref }}
version=$(echo "$ref" | cut -d '/' -f 4)
image=$(echo "$ref" | cut -d '/' -f 3)
echo "::set-output name=image::$image"
echo "::set-output name=version::$version"
Indeed, the goal of this post is not to dicuss about bash or the best way to parse a string but to show that you can always, in a Github Workflow, define a custom step which can communicate with other steps through its outputs. Used with the interpolation mecanism of the steps and your preferred language for the custom steps it is really powerful and can make the workflows more readable since they can likely need less steps quite quickly.
From the same author:
In the same category: