GitHub Actions - Reuse outputs from other reusable workflows - github

I'm not sure if it's possible, but I'm attempting to use outputs from one reusable workflow, in another, that's part of the same caller workflow. For reference, please see the configs below:
Caller Workflow:
jobs:
call-workflow-calver:
uses: ./.github/workflows/called-workflow1.yaml
secrets: inherit
call-workflow-echo:
needs: call-workflow-calver
uses: ./.github/workflows/called-workflow2.yaml
secrets: inherit
Job for creating the CalVer tag (it outputs as $VERSION as part of the action)
Called Workflow 1:
...
jobs:
calver:
name: Create CalVer tag
...
steps:
- name: Calver tag
uses: StephaneBour/actions-calver#1.4.4
if: ${{ github.ref == 'refs/heads/main' }}
id: calVer
with:
date_format: "%Y-%m-%d"
release: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
...
Trying to use the CalVer $VERSION output from another caller workflow
Called Workflow 2:
...
- name: Echo
run: |
echo ${{needs.calver.outputs.VERSION}}
...
The basic concept is I'm trying to use outputs from one reusable workflow in another for setting CalVer versions in workflow 1, calling that in workflow 2 so I can set it as an image version. I will eventually use in a 3rd reusable workflow to deploy said image. If this is possible, that would be great!
Hopefully, this all makes a bit of sense, but if anything needs clarifying, please do let me know!
Many thanks in advance!

According to the official documentation, you can now declare outputs to reusable workflows.
These work just like job outputs and are available via needs.<reusable>.outputs.<output> format once you declare the output.
Example
1. Reusable workflow configuration:
name: Reusable workflow
on:
workflow_call:
# Map the workflow outputs to job outputs
outputs:
firstword:
description: "The first output string"
value: ${{ jobs.example_job.outputs.output1 }}
secondword:
description: "The second output string"
value: ${{ jobs.example_job.outputs.output2 }}
jobs:
example_job:
name: Generate output
runs-on: ubuntu-latest
# Map the job outputs to step outputs
outputs:
output1: ${{ steps.step1.outputs.firstword }}
output2: ${{ steps.step2.outputs.secondword }}
steps:
- id: step1
run: echo "::set-output name=firstword::hello"
- id: step2
run: echo "::set-output name=secondword::world"
2. Workflow using the reusable:
name: Call a reusable workflow and use its outputs
on:
workflow_dispatch:
jobs:
job1:
uses: octo-org/example-repo/.github/workflows/called-workflow.yml#v1
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo ${{ needs.job1.outputs.firstword }} ${{ needs.job1.outputs.secondword }}
Note that if a reusable workflow that sets an output is executed with a matrix strategy, the output will be the output set by the last successful completing reusable workflow of the matrix which actually sets a value. That means if the last successful completing reusable workflow sets an empty string for its output, and the second last successful completing reusable workflow sets an actual value for its output, the output will contain the value of the second last completing reusable workflow.
I used this workflow as example here if you want to check the logs of the workflow run.

Related

How to pass run number from one workflow to another?

I have 2 workflows
First runs tests and generated allure report and second builds and deploys to github pages and sends a slack notification with the link to gh pages.
I am curious, how can I get run number of the first workflow and pass it to the second workflow as ${{github.run_number}}?
I've tried to pass just ${{github.run_number}}
You can configure the first WF to trigger the second WF and pass some inputs. At first, you need to define an input in the second workflow:
name: Second workflow (deploy)
on:
workflow_dispatch:
inputs:
run_number: # Exposing the input through which we can pass the value
description: 'The run number'
required: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Echo the input
run: echo ${{ github.event.inputs.run_number }}
Then, you can trigger this workflow from another workflow in the following way:
name: First Workflow (report)
on:
push:
branches:
- '*'
jobs:
allure-report:
runs-on: ubuntu-latest
steps:
# ... Allure report generation steps, etc.
- name: Invoke the deploy WF
uses: benc-uk/workflow-dispatch#v1
with:
workflow: deploy.yaml
inputs: '{ "run_number": "${{ github.run_number }}" }'
In this example, I've used the Workflow Dispatch GH Action to pass the current run_number to the deploy.yml WF as an input value.
To read more about workflow inputs see the Specifying inputs and workflow_dispatch event.

GitHub Actions Set Output - Assure Parallel Job Access is Safe

I have configured my github actions which runs tests in parallel for different platforms. At the end of my tests I want the status to be saved to the outputs. Once all jobs complete I have another job that runs to send the results to a slack webhook.
I am having difficulty determining a method to save the output for multiple jobs and assuring there is no issues when they are running in parallel.
For example this is my code snippet
name: Test Notify
on:
push:
jobs:
build:
strategy:
matrix:
config:
- name: 'Ubuntu 18.04'
runner: 'ubuntu-18.04'
id: 'u18'
- name: 'Ubuntu 20.04'
runner: 'ubuntu-20.04'
fail-fast: false
runs-on: ${{ matrix.config.runner }}
outputs:
# Prefer to have one general output I can append to
global: ${{ steps.status.outputs.global }}
# I can output to separate outputs but I rather have a single one as shown above
u18: ${{ steps.status.outputs.u18 }}
u20: ${{ steps.status.outputs.u20 }}
steps:
- name: Test Failure u18
id: step1
if: ${{ matrix.config.id == 'u18' }}
run: |
exit 1
- name: Doing Step 2
id: step2
run: |
echo "DO NOTHING"
- name: Output Status
id: status
if: always()
env:
JOB_STATUS: "${{ job.status }}"
run: |
# This works, but is it safe since I have u18 and u20 running in parallel ?
echo "${{ matrix.config.id }}=$JOB_STATUS" >> $GITHUB_OUTPUT
# Is there a safe way to have a single status string that I add to, for example;
# echo "global=${{ github_output.global}}$JOB_STATUS" >> $GITHUB_OUTPUT
webhook:
needs: build
runs-on: 'ubuntu-20.04'
if: always()
steps:
- name: Send webhook update for all jobs
env:
JSON_RESULTS: "${{ toJSON(needs.build-and-test) }}"
run: |
# Will add code to properly send the information
echo $JSON_RESULTS
Currently, there is no easy way to reference all outputs of matrix jobs. Moreover combining it into a single output.
The issue is that only a single value is available for future jobs that need the strategy.matrix job’s output because even if the output is set by multiple matrix variations of the job, only one is retained.
For more detail, see the Community discussion.
TL;DR:
There are several workarounds:
defining separate outputs for a job with strategy.matrix by letting the job variations set different outputs
then process these outputs in a separate step that can provide single output for further steps
use artifacts to store the matrix jobs' outputs and then post-process it (discussioncomment-3814009)

How can I run a GitHub Actions job based on a complex condition (specific label removed)?

I have two reusable workflows to deploy and destroy GCP resources, which I call from one workflow based on different conditions.
One workflow creates infra and is triggered when the label preview is added to a PR:
on:
pull_request:
types: [opened, reopened, labeled]
jobs:
create-infrastructure:
if: ${{ contains( github.event.pull_request.labels.*.name, 'preview') }}
# Call to a reusable workflow here
The second workflow I need to trigger when the PR is closed or when a specific label is removed; I tried this:
on:
pull_request:
types: [ closed, unlabeled ]
jobs:
destroy_preview:
if: ${{ contains( github.event.pull_request.labels.*.name, 'preview') }}
uses: myrepo/.github/workflows/preview-app-destroy.yml#v0.3.6
with:
project_id: xxx
I don't know how to define unlabeled for a specific label. It would be great if someone has any idea.
The pull request webhook payload doesn't contain the removed label, as far as I can tell, but you can fetch the list of issue events (which work for pull requests, too), filter by unlabeled events, and then look at the label name of the last one.
Using the GitHub CLI in a run step, that might look something like this:
name: Preview removed workflow
on:
pull_request:
types:
- unlabeled
jobs:
destroy_preview:
runs-on: ubuntu-20.04
steps:
- name: Check if "preview" was removed
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
pr=${{ github.event.number }}
label=$(gh api "repos/$GITHUB_REPOSITORY/issues/$pr/events" \
--jq 'map(select(.event == "unlabeled"))[-1].label.name')
if [[ $label == 'preview' ]]; then
echo "The 'preview' label has been removed"
fi
where you'd replace the echo with your infrastructure commands.
Now, if you want to call a reusable workflow when that specific label is removed, you have to somehow find a way to add a condition to the job where the reusable workflow is called.
One option is to make two jobs, one to check the condition and setting the result as a job output. The other job is set up as depending on the first one, and its if condition checks if the output was set to true.
This would look something like this (omitting the name and trigger, as they are identical to above):
jobs:
checklabel:
runs-on: ubuntu-20.04
outputs:
waspreview: ${{ steps.check.outputs.preview }}
steps:
- name: Check if "preview" was removed
id: check
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
pr=${{ github.event.number }}
label=$(gh api "repos/$GITHUB_REPOSITORY/issues/$pr/events" \
--jq 'map(select(.event == "unlabeled"))[-1].label.name')
if [[ $label == 'preview' ]]; then
echo "::set-output name=preview::true"
fi
destroy_preview:
needs: checklabel
if: needs.checklabel.outputs.waspreview
uses: myrepo/.github/workflows/preview-app-destroy.yml#v0.3.6
with:
project_id: xxx

In a GitHub Action how to conditionalize a step based off the previous step's output?

Building a GitHub action based on the commit message I'm trying to base a step on whether the commit message contains a particular string, set it to a variable and then in the next step check with a condition.
My current implementation of my action works:
name: Smoke Test
on:
push:
branches:
- main
permissions:
contents: read
issues: write
jobs:
smoking:
runs-on: [ubuntu-latest]
steps:
- name: Run smoke tests
if: ${{ !contains(github.event.head_commit.message, 'smoke_test') }}
run: |
echo 'Smoke Test not requested'
exit 1
stuff:
needs: smoking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- uses: JasonEtco/create-an-issue#v2
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
with:
filename: .github/ISSUE_TEMPLATE/smoke-test.md
id: create-issue
- run: 'echo Created issue number ${{ steps.create-issue.outputs.number }}'
- run: 'echo Created ${{ steps.create-issue.outputs.url }}'
but with the implementation of:
exit 1
causes the action to indicate it error'ed out in the action panel and while that works that isn't technically accurate because I don't need it to error I just don't want the remaining steps to run.
I've tried setting a variable:
if: ${{ contains(github.event.head_commit.message, 'smoke_test') }}
with:
run-smoke-test: true
run: |
echo 'Smoke Test requested'
but it's not passing to the next step.
Research
Use environment variable in github action if
How to pass variable between two successive GitHub Actions jobs?
github-action: does the IF have an ELSE?
How to fail a job in GitHub Actions?
GitHub Actions - trigger another action after one action is completed
Without relying on another GitHub action is there a way in step smoking to set an env variable that step stuff would need to validate for before running the step?
Edit
After reading the answer and implementing job outputs I've written:
name: Smoke Test
on:
push:
branches:
- main
permissions:
contents: read
issues: write
jobs:
commitMessage:
runs-on: ubuntu-latest
outputs:
output1: ${{ steps.isSmoke.outputs.test }}
steps:
- id: isSmoke
if: ${{ contains(github.event.head_commit.message, 'smoke_test') }}
run: echo "::set-output name=test::true"
smokeTest:
runs-on: ubuntu-latest
needs: commitMessage
steps:
- uses: actions/checkout#v2
- uses: JasonEtco/create-an-issue#v2
if: steps.isSmoke.output.test == true
env:
GITHUB_TOKEN: ${{ secrets.DEV_TOKEN }}
with:
filename: .github/ISSUE_TEMPLATE/smoke-test.md
but when the commit message of smoke_test is used it bypasses create-an-issue:
and I'm basing my condition after reading "Run github actions step based on output condition" and reading:
Contexts
Expressions
Using conditions to control job execution
Can a condition come before a step and/or what is the correct way to run a step based off the previous step?
You are looking for job outputs, which allow you to send data to the following jobs.

GitHub Action workflow_call does not use up to date input values

I am encountering a strange behaviour with GitHub Action workflow_call.
Basically, everything works fine after the initial setup, but when I edit the input parameters of the workflow files it does not use the updated values. Hard to explain in words, so let me show you an example.
Consider this basic setup:
File: start-pipeline.yml
name: Start Pipeline
on:
workflow_dispatch:
inputs:
release_type:
required: true
type: choice
options:
- patch
- minor
- major
jobs:
call-sub-workflow:
uses: codezalot/workflow-call-issue/.github/workflows/sub-workflow.yml#main
with:
release_type: ${{ github.event.inputs.release_type }}
File: sub-workflow.yml
name: Sub Workflow
on:
workflow_call:
inputs:
release_type:
type: string
required: true
jobs:
my-job:
runs-on: ubuntu-latest
steps:
- name: Print Inputs
run: |
echo "Release Type: ${{ github.event.inputs.release_type }}"
I then start the pipeline with the value patch and the sub-workflow prints the input just fine:
I then update the workflow files and add an additional input value like this:
File: start-pipeline.yml
...
jobs:
call-sub-workflow:
uses: codezalot/workflow-call-issue/.github/workflows/sub-workflow.yml#main
with:
release_type: ${{ github.event.inputs.release_type }}
some_other_value: ${{ github.event.inputs.release_type }}
File: sub-workflow.yml
...
inputs:
...
some_other_value:
type: string
required: true
jobs:
...
run: |
echo "Release Type: ${{ github.event.inputs.release_type }}"
echo "Release Type: ${{ github.event.inputs.some_other_value }}"
I then run the pipeline again, once with release_type patch, once with minor.
The value some_other_value is missing however, see:
.
I have been playing around with this issue for hours, but I do not understand what is going wrong. For it does seem to be using the latest SHA versions of the files, just the input parameters cause an issue.
Does anyone know what's going on here?
According to the documentation: "When a workflow is triggered with the workflow_call event, the event payload in the called workflow is the same event payload from the calling workflow." In other words, the event parameter in the github context is the same for the called workflow as for the original workflow.
Consequently, there is no github.event.input.some_other_value parameter since the original workflow did not have this input. For inputs defined in a reusable workflow you can use the inputs context which contains the input properties passed to the reusable workflow.
To sum up, here is a working sub-workflow.yml file:
name: Sub Workflow
on:
workflow_call:
inputs:
release_type:
type: string
required: true
some_other_value:
type: string
required: true
jobs:
my-job:
runs-on: ubuntu-latest
steps:
- name: Print Inputs
run: |
echo "Release Type: ${{ inputs.release_type }}"
echo "Release Type: ${{ inputs.some_other_value }}"
Result: