I've been using different build systems for years, but after playing with github actions quite a bit there was still some things that I was only managing based on trial and error. I've written this guide to try and fill in those gaps. If you've been through the Quickstart for GitHub Actions and Understanding GitHub Actions documentation but still have some questions I'm hoping this post will answer some of them.
First some definitions:
- Workflows are combinations of jobs
- Jobs are combinations of steps that are run on a virtual machine, they can even be a workflow of their own
- Steps are either actions or shell commands
Sharing data in your workflow
This was one of the bits that I felt the documentation didn't explain clearly enough. All the information is there, but spread across multiple pages and can take quite a while to put it all together.
Environment variable types
It's worth baring in mind that because most of what follows uses environment variables under the hood it is a string
, so even though the value could be 'true'
or 'false'
you would need an expression that explicitly compares it to those values. e.g. if: env.isTag == 'true'
would work, but if: env.isTag
alone would not.
Inputs to workflows however can be string
, number
or boolean
types.
Sharing data between steps in a job
If one of your steps has an output that you need in a later step within the same job you can set it as an environment variable. This is done by sending a statement setting it to the $GITHUB_ENV
file. A minimal demonstration of this is:
steps:
- name: Set myValue
run: echo "myValue=wibble" >> $GITHUB_ENV
- name: Echo myValue
run: echo "$myValue"
Ofcourse the above isn't that useful, the wibble
part would usually be a calculated value. You would usually use some of the calculate the value from some other environment variables. You can either calculate your value in the bash script, e.g.
env:
wibble: wibble
steps:
- name: Set myValue
run: echo "myValue=$GITHUB_REF_NAME-$wibble" >> $GITHUB_ENV
or you can use the javascript context ${{ }}
and any of the expressions or contexts to calculate your value, e.g.
env:
wibble: wibble
steps:
- name: Set myValue
run: echo "myValue=${{ github.ref_name + "-" + env.wibble }} >> $GITHUB_ENV
These two blocks should be equivalent, but the expressions mean I generally use the JS version.
The key bit is that it is sent to the $GITHUB_ENV
file. This is what will make it available as an environment variable to the steps that follow it. But only within that job. What if you want to share that value with different jobs?
Sharing data between jobs within a workflow
In the above you used the $GITHUB_ENV
file to make a value available to later steps, but if you want to share a value to a different job you need to use the $GITHUB_OUTPUTS
file instead. But that would be too easy. In addition you also need to declare it as an output.
Within your job you will need to add an outputs
section that uses the JS context to access the value you will output. You do this by accessing the steps
context object. The step that you use will now need an id
so that you can access it through steps.<stepId>
.
For example, if you want a job that outputs a value that says if this build was triggered by a tag:
jobs:
isTagJob:
runs-on: ubuntu-latest
outputs:
isTag: ${{ steps.set_istag.outputs.isTag }}
steps:
- id: set_istag # step id used to find the output above
name: Set isTag # name that will appear in the logging for this section
run: echo "isTag=${{ startsWith(github.ref, 'refs/tags/') }}" >> $GITHUB_OUTPUT
This will make the isTag
value available to any jobs dependant on this one through the needs context. The format is needs.<jobId>.outputs.<outputName>
so the above would be needs.isTagJob.outputs.isTag
. For example the following will only run the announce_tag
step if isTag
is 'true'
.
...
announceTag:
runs-on: ubuntu-latest
needs: [isTagJob] # This makes it dependent on the `isTagJob` job above
steps:
- id: announce_tag
name: Announce tag
if: needs.isTag.outputs.isTag == 'true'
run: echo ::notice::This build is for a tag
Sharing data to reusable workflows
Workflows that are called by other workflows use workflow_call
as their on
trigger. You can pass values to these workflows by defining inputs
in that workflow and passing the values in using with
in the calling workflow.
For example the file .github/workflows/build-dotnet.yml
shown below takes two inputs projectDir
and isTag
and then uses them in it's jobs.
name: Build Dotnet
on:
workflow_call:
inputs:
projectDir:
required: true
type: string
isTag:
required: true
type: boolean
jobs:
build-code:
runs-on: ubuntu-latest
steps:
- id: build
name: "Build ${{ inputs.projectDir}}"
run: dotnet build ${{ inputs.projectDir}}
- id: push_artifact
name: "Push ${{ inputs.projectDir}} artifact"
if: inputs.isTag # the input is a boolean so don't need to compare to `'true'` like with env vars.
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.projectDir }}
path: ./temp/${{ inputs.projectDir}}/**/*
The calling workflow will need to ensure it provides the values correctly so that this workflow can use them. The calling workflow code could look like:
name: Build
on:
push:
jobs:
isTagJob:
runs-on: ubuntu-latest
outputs:
isTag: ${{ steps.set_istag.outputs.isTag }}
steps:
- id: set_istag # step id used to find the output above
name: Set isTag # name that will appear in the logging for this section
run: echo "isTag=${{ startsWith(github.ref, 'refs/tags/') }}" >> $GITHUB_OUTPUT
build-dotnet-proj-a:
needs: [isTagJob]
uses: ./.github/workflows/build-dotnet.yml
with:
projectDir: projects/projectA
isTag: ${{needs.isTagJob.outputs.isTag == 'true'}}
build-dotnet-proj-b:
needs: [isTagJob]
uses: ./.github/workflows/build-dotnet.yml
with:
projectDir: projects/projectB
isTag: ${{needs.isTagJob.outputs.isTag == 'true'}}
The same syntax for inputs in reusable workflows is also used actions.
Top comments (0)