I have the following workflow in Github Actions, where I have a job that create some outputs and a dependant job that read those outputs, pretty similar to the example from the docs:
name: Sandbox
on:
push:
env:
POSTGRESQL_VERSION: "14.4.0-debian-11-r13"
jobs:
setup:
runs-on: ubuntu-latest
outputs:
prod_tag: "steps.prod_tag.outputs.prod_tag"
postgresql_version: "steps.postgresql_version.outputs.postgresql_version"
steps:
- uses: actions/checkout#v3
- id: prod_tag
run: |
if [[ ${{ github.ref_type }} == "tag" ]]; then
echo "::set-output name=prod_tag::${{github.ref_name}}"
else
echo "::set-output name=prod_tag::latest"
fi;
- id: postgresql_version
run: echo "::set-output name=postgresql_version::${POSTGRESQL_VERSION}"
- name: Show output variables
run: |
echo "PROD TAG: ${{steps.prod_tag.outputs.prod_tag}}"
echo "POSTGRESQL_VERSION: ${{steps.postgresql_version.outputs.postgresql_version}}"
show_outputs:
runs-on: ubuntu-latest
needs: setup
steps:
- run: |
echo "PROD TAG: ${{needs.setup.outputs.prod_tag}}"
echo "POSTGRESQL_VERSION: ${{needs.setup.outputs.postgresql_version}}"
However, in my example, it doesn't work as expected and show_outputs shows PROD TAG: steps.prod_tag.outputs.prod_tag and POSTGRESQL_VERSION: steps.postgresql_version.outputs.postgresql_versioninstead of the values set in the setup job, that should be latest and 14.4.0-debian-11-r13. In the step Show output variables of the setup job I can see that the values are properly set, and I've tried several different approaches (setting the variables from the same step, not taking the value from the environment variable, etc) but with no success.
Any idea what can be wrong with my example?
You should surround the variables with ${{ and }}
try with:
outputs:
prod_tag: ${{ steps.prod_tag.outputs.prod_tag }}
postgresql_version: ${{ steps.postgresql_version.outputs.postgresql_version }}
instead of:
outputs:
prod_tag: "steps.prod_tag.outputs.prod_tag"
postgresql_version: "steps.postgresql_version.outputs.postgresql_version"
Related
For one of my projects, I am setting an action output from within a bash script that is executed inside a composite action. I found that GitHub has excellent documentation on how to create a GitHub composite action output. It states that this can be done using the following action.yml file.
name: 'Hello World'
description: 'Greet someone'
inputs:
who-to-greet: # id of input
description: 'Who to greet'
required: true
default: 'World'
outputs:
random-number:
description: "Random number"
value: ${{ steps.random-number-generator.outputs.random-number }}
runs:
using: "composite"
steps:
- run: echo Hello ${{ inputs.who-to-greet }}.
shell: bash
- id: random-number-generator
run: echo "random-number=$(echo $RANDOM)" >> $GITHUB_OUTPUT
shell: bash
- run: echo "${{ github.action_path }}" >> $GITHUB_PATH
shell: bash
- run: goodbye.sh
shell: bash
I checked the results using the following action workflow, and it works.
on: [push]
jobs:
hello_world_job:
runs-on: ubuntu-latest
name: A job to say hello
steps:
- uses: actions/checkout#v3
- id: foo
uses: actions/hello-world-composite-action#v1
with:
who-to-greet: 'Mona the Octocat'
- run: echo random-number ${{ steps.foo.outputs.random-number }}
shell: bash
My use case, however, differs from the example above in that I have to set the output variable inside the goodbye.sh script. According to the documentation, this should be done using the GITHUB_OUTPUT variable:
echo "{name}={value}" >> $GITHUB_OUTPUT
After some testing, this method is not working for composite actions. As this could also be a bug or not supported, I created a bug report at https://github.com/orgs/community/discussions/47775. However, I quickly wanted to double-check if there may be something wrong with my syntax.
Steps to reproduce
Fork this repository.
Enable GitHub actions on the fork.
Push a commit to your fork.
See that only the random-number variable is set while the random-number-bash` is set (See this example workflow).
I found my issue using #benjamin-w's comment. The problem was that the goodbye.sh step should contain an id key for the created output to be referenced correctly. The correct syntax should be:
action.yml
name: 'Hello World'
description: 'Greet someone'
inputs:
who-to-greet: # id of input
description: 'Who to greet'
required: true
default: 'World'
outputs:
random-number:
description: "Random number"
value: ${{ steps.random-number-generator.outputs.random-number }}
random-number-bash:
description: "Random number bash"
value: ${{ steps.random-number-generator-bash.outputs.random-number-bash }}
runs:
using: "composite"
steps:
- run: echo Hello ${{ inputs.who-to-greet }}.
shell: bash
- id: random-number-generator
run: echo "random-number=$(echo $RANDOM)" >> $GITHUB_OUTPUT
shell: bash
- run: echo "${{ github.action_path }}" >> $GITHUB_PATH
shell: bash
- run: goodbye.sh
id: random-number-generator-bash
shell: bash
And the correct syntax for creating the output in the goodbye.sh script should be:
Goodbye.sh
echo "Goodbye"
echo "random-number-bash=$(echo 123)" >> $GITHUB_OUTPUT
Which then can be tested using the following workflow file:
Test workflow
on: [push]
jobs:
hello_world_job:
runs-on: ubuntu-latest
name: A job to say hello
steps:
- uses: actions/checkout#v3
- id: foo
uses: rickstaa/hello-world-composite-action-output-bug#main
with:
who-to-greet: 'Mona the Octocat'
- run: echo random-number ${{ steps.foo.outputs.random-number }}
shell: bash
- run: echo random-number ${{ steps.foo.outputs.random-number-bash }}
shell: bash
I have set up a workflow with multiple jobs. Job A is a requirement for Job B to run.
The workflow is triggered on PR, and Job A checks for the existence of a comment on the PR:
job-a:
outputs:
comment: ${{ steps.find-comment.outputs.comment }}
steps:
- name: Check if QR already exists
uses: peter-evans/find-comment#v2
id: find-comment
with:
issue-number: ${{ github.event.number }}
comment-author: "github-actions[bot]"
body-includes: Preview Bundle
- name: Store find-comment output
run: echo "comment=${{ steps.find-comment.outputs.comment-id }}" >> $GITHUB_OUTPUT
- name: Check find-comment outcome
run: echo "This is the output of find-comment ${{ steps.find-comment.outputs.comment-id }}" # Successfully logs out comment id
I set the output of the check into comment in outputs for that job.
In Job B, I then need to check if that output is an ID or an empty string:
job-b:
needs: [job-a]
if: always() && needs.job-a.outputs.comment == ''
steps:
- name: Check for find-comment
run: echo "This is the input of find-comment ${{ needs.job-a.outputs.comment }}" # always logs out an empty string
The if check always equates to true, i.e. the outputs.comment is always an empty string. I've tried multiple variations of the if check, with and without the always(), with and without the [], but needs.job-a.outputs.comment always is an empty string even though it successfully logs out the comment id in the check in job-a. This job then always runs, which is not what I want. I only want the job to run when the PR comment doesn't exist.
Can anyone tell me what I'm doing wrong here?
You need to define an identifier for the step in order to be able to refer it as an output for the job. See the relevant part of the doc:
Sets a step's output parameter. Note that the step will need an id to be defined to later retrieve the output value.
So you should change your code like:
job-a:
outputs:
comment: ${{ steps.store-comment.outputs.comment }}
steps:
- name: Check if QR already exists
uses: peter-evans/find-comment#v2
id: find-comment
with:
issue-number: ${{ github.event.number }}
comment-author: "github-actions[bot]"
body-includes: Preview Bundle
- name: Store find-comment output
id: store-comment
run: echo "comment=${{ steps.find-comment.outputs.comment-id }}" >> $GITHUB_OUTPUT
The answer was to change the outputs variable to take comment-id instead of just comment. I thought the chained property had to match what you put int eh echo command, but apparently it has to match what is in the outputs of peter-evans/find-comment#v2: https://github.com/peter-evans/find-comment#outputs
job-a:
outputs:
comment: ${{ steps.store-comment.outputs.comment-id }}
I have a simple job in my yaml that figures out the branch name, and sets it as an output
The problem that I've noticed, is that if I push a branch, then I need to read from github.event.ref. But if I push a tag, then I need to read from github.event_base_ref instead.
The code below is my best take (and it works), but I'm wondering if there's a way to simplify it so that the output is set conditionally in a single step.
setup:
name: Setup variables
runs-on: ubuntu-latest
if: github.event_name == 'push'
outputs:
stackName: ${{steps.step3.outputs.branch}}
steps:
- uses: actions/checkout#v2
- id: step1
name: Is branch push?
# this is the default. It assigns the branch variable to the github env
run: |
echo "${{ github.event.ref }}"
raw="${{ github.event.ref }}"
branch=$(echo ${raw/refs\/heads\/} | tr -cd '[a-zA-Z0-9-]')
echo "Branch name is $branch."
echo "branch=$branch" >> $GITHUB_ENV
- id: step2
name: Is tag push?
# this runs conditionally.
if: startsWith(github.ref, 'refs/tags/deploy')
run: |
echo "${{ github.event.base_ref }}"
raw="${{ github.event.base_ref }}"
branch=$(echo ${raw/refs\/heads\/} | tr -cd '[a-zA-Z0-9-]')
echo "Branch name is $branch."
echo "branch=$branch" >> $GITHUB_ENV
- id: step3
name: Set stack name
# after both step 1 and 2 have run, see what's in the env and assign the output
run: |
echo "::set-output name=branch::${{env.branch}}"
You can use an existing action like EthanSK/git-branch-name-action, as example:
on: [push, pull_request]
jobs:
main_job:
runs-on: ubuntu-latest
steps:
- name: Git branch name
id: git-branch-name
uses: EthanSK/git-branch-name-action#v1
- name: Echo the branch name
run: echo "Branch name ${GIT_BRANCH_NAME}"
and use also like
if: ${{ env.GIT_BRANCH_NAME == 'master' }}
Link to the marketplace here
How can i read json key-value using inline script in git actions and pass them outside the bash tas a variable, below is the file to create a resource group and parse the json from the repo and pass the vaues as variable to the parameter
on: [push]
name: sp_keyvault
jobs:
build-and-deploy:
runs-on: ubuntu-latest
env:
ResourceGroupName: sp_keyvault-rg
ResourceGroupLocation: "eastus"
steps:
- uses: actions/checkout#master
- uses: azure/login#v1
with:
creds: ${{ secrets.Azure_cred }}
- uses: Azure/CLI#v1
with:
inlineScript: |
#!/bin/bash
if $(az group exists --name ${{ env.ResourceGroupName }}) ; then
echo "Azure resource group already exists, skipping creation..."
else
az group create --name ${{ env.ResourceGroupName }} --location ${{ env.ResourceGroupLocation }}
echo "Azure resource group created"
fi
inlineScript: |
#!/bin/bash
#need to access json from the repo and pass it to parameter variable
- uses: azure/arm-deploy#v1
with:
resourceGroupName: ${{ env.ResourceGroupName }}
template: ./sp_repo-main/KeyVaultSetup.json
parameters: "need variable from bash script"
I have: Job A, Job B & Job C.
when Job A completes
If job B runs I need job C to run (after job B has completed with success)
if Job B skipped I need Job C to run (If job A has completed with success)
See below for code snip:
check_if_containers_exist_to_pass_to_last_known_tagger_job: (JobA)
name: check_if_containers_exist
environment: test
runs-on: ubuntu-latest
#needs: [push_web_to_ecr, push_cron_###_to_ecr, push_to_###_shared_ecr, push_to_###_ecr]
needs: push_###_to_shared_ecr
#if: ${{ github.ref == 'refs/heads/main' }}
outputs:
signal_job: ${{ steps.step_id.outputs.run_container_tagger_job }}
steps:
- name: Configure AWS credentials
id: config-aws-creds
uses: aws-actions/configure-aws-credentials#v1
with:
aws-access-key-id: ${{ secrets.SHARED_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.SHARED_AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-2
- name: Check if container exists (If containers don't exist then don't run last known tagging job for rollback)
id: step_id
run: |
aws ecr describe-images --repository-name anonymizer --image-ids imageTag=testing-latest
if [ $? == 254 ]; then echo "::set-output name=run_container_tagger_job::false"; else echo "::set-output name=run_container_tagger_job::true"; fi
tag_latest_testing_containers_as_last_known_testing_containers: (Job B)
needs: check_if_containers_exist_to_pass_to_last_known_tagger_job
if: needs.check_if_containers_exist_to_pass_to_last_known_tagger_job.outputs.signal_job == 'true'
uses: ###/###/.github/workflows/container-tagger.yml####
with:
tag_to_identify_containers: testing-latest
new_tag_to_apply_to_containers: last-known-testing
aws-region: eu-west-2
run_cron_and_cycle_containers: false
secrets:
SHARED_AWS_ACCESS_KEY_ID: ${{ secrets.SHARED_AWS_ACCESS_KEY_ID }}
SHARED_AWS_SECRET_ACCESS_KEY: ${{ secrets.SHARED_AWS_SECRET_ACCESS_KEY }}
tag_testing_containers_to_testing_latest: (Job C)
needs: [check_if_containers_exist_to_pass_to_last_known_tagger_job,tag_latest_testing_containers_as_last_known_testing_containers]
if: ${{ always() }}
uses: ###/##/.github/workflows/container-tagger.yml####
with:
tag_to_identify_containers: dev-${{ github.sha }}
new_tag_to_apply_to_containers: james-cron-test
aws-region: eu-west-2
run_cron_and_cycle_containers: true
secrets:
SHARED_AWS_ACCESS_KEY_ID: ${{ secrets.SHARED_AWS_ACCESS_KEY_ID }}
SHARED_AWS_SECRET_ACCESS_KEY: ${{ secrets.SHARED_AWS_SECRET_ACCESS_KEY }}
ENVIRONMENT_AWS_ACCESS_KEY_ID: ${{ secrets.TESTING_AWS_ACCESS_KEY_ID }}
ENVIRONMENT_AWS_SECRET_ACCESS_KEY: ${{ secrets.TESTING_AWS_SECRET_ACCESS_KEY }}
It might not be the most elegant solution, but it works.
The workaround would consist of adding 2 extra steps at the end of the Job A, and set the 2 steps always execute (if: always()).
The first one is used to create a text file and write the job status into it.
The second one is used to upload this text file as an artifact.
Then, in Job B and Job C, you will need to add the steps to download the artifacts and read the status of Job A to then perform or not specific operations.
Here is a demo of how it might look:
jobs:
JOB_A:
name: Job A
...
steps:
- name: Some steps of job A
...
- name: Create file status_jobA.txt and write the job status into it
if: always()
run: |
echo ${{ job.status }} > status_jobA.txt
- name: Upload file status_jobA.txt as an artifact
if: always()
uses: actions/upload-artifact#v1
with:
name: pass_status_jobA
path: status_jobA.txt
JOB_B:
needs: [JOB_A]
if: always()
name: Job B
...
steps:
- name: Download artifact pass_status_jobA
uses: actions/download-artifact#v1
with:
name: pass_status_jobA
- name: Set the status of Job A as output parameter
id: set_outputs
run: echo "::set-output name=status_jobA::$(<pass_status_jobA/status_jobA.txt)"
- name: Check Job A status
if: ${{ steps.set_outputs.outputs.status_jobA }} == "success"
run: |
...
JOB_C:
needs: [JOB_A]
if: always()
name: Job C
...
steps:
- name: Download artifact pass_status_jobA
uses: actions/download-artifact#v1
with:
name: pass_status_jobA
- name: Set the status of Job A as output parameter
id: set_outputs
run: echo "::set-output name=status_jobA::$(<pass_status_jobA/status_jobA.txt)"
- name: Check Job A status
if: ${{ steps.set_outputs.outputs.status_jobA }} == "failure"
run: |
...
Note that here, all jobs will always run, but Job B steps after the check will only run for Job A success, and Job C steps after the check will only run for Job A failure.
Job A --> Success --> Job B + Job C checks --> Job B steps
Job A --> Failure --> Job B + Job C checks --> Job C steps
Reference