I'm reading this quickstart guide.
The guide provides an example actions.yml:
# action.yml
name: 'Hello World'
description: 'Greet someone and record the time'
inputs:
who-to-greet: # id of input
description: 'Who to greet'
required: true
default: 'World'
outputs:
time: # id of output
description: 'The time we greeted you'
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.who-to-greet }}
My question concerns the last section in this file, runs:. My workflow will use multiple docker images. What's the 'right' way to do this? Should I create multiple actions.yml? Should I use multiple repos? Or can I somehow reference multiple Dockerfile's within runs:?
The docs only mention the usage of a single docker image in a docker action.
It is however possible to run docker containers in a step of a normal workflow as stated in this Stackoverflow thread. This means you can create a composite action to make use of the same features provided by a normal workflow.
name: CI
on: [push]
jobs:
myjob:
runs-on: ubuntu-latest # linux required if you want to use docker
steps:
- uses: docker://continuumio/anaconda3:2019.07 # runs anaconda image from DockerHub
Related
With the matrix strategy and two jobs like those below, the first job runs a "matrix" of possible configurations in parallel, and when all of them succeed, it moves onto the second job.
jobs:
job1:
runs-on: ubuntu-latest
strategy:
matrix:
version: [10, 12, 14]
steps:
# etc
job2:
runs-on: ubuntu-latest
needs: job1
strategy:
matrix:
version: [10, 12, 14]
steps:
# etc
What would be nice is to essentially have a matrix over the whole workflow, where for each matrix value, the entire workflow of jobs is run unimpeded, in parallel. That means for a particular configuration job2 can proceed right after job1 completes for that configuration, regardless of whether job1 for another configuration is still in process or has failed.
In the following diagram, the first case is what will currently happen, and the second is the desired case:
I feel like this could be "emulated" by launching the workflow several times with a different context. But how could this be done, for example from a trigger? Somehow specify on: push that 3 versions of the workflow must run simultaneously?
After some digging, I think "Reusable workflows" is the currently supported method.
The solution is to have two workflow files. One that has all the jobs, and another small workflow that has the matrix and calls the other workflow.
name: Common Workflow
on:
workflow_call:
inputs:
version: # the variable you can use in place of a matrix
required: true
type: number
jobs:
job1:
runs-on: ubuntu-latest
steps:
- run: echo 'job1 version ${{ inputs.version }}'
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo 'job2 version ${{ inputs.version }}'
name: Master Workflow
on:
push:
jobs:
version-matrix:
strategy:
# super important if you want to see all results, even if one fails
# fail-fast is true by default
fail-fast: false
matrix:
version: [10, 12, 14]
uses: ./.github/workflows/common-workflow.yml # calls the one above ^
with:
version: ${{ matrix.version }}
secrets: inherit
Here's a real example of it running in the UI:
One thing you will run into once you run this though, is the UI lists the subjobs in alphabetical order, and not the order they are listed in the workflow. So you lose the sense of the dependencies between the jobs. This is true in February 2023, so maybe Github will improve the UI down the road. Until then, we got around this by naming our jobs like 1. First Job, 2. Second Job so alphabetical is equivalent to the order they run in. It's hacky but it works.
Another thing to note, especially for deployments is the fail-fast attribute. This should be set to false to make sure every workflow in the matrix gets to run to completion, even if one of them fails.
I'm going to setup a workflow on GitHub which uses some docker images to check my package on different platform. I'd like to use the matrix strategy. Then I wrote a workflow like this:
name: Main Workflow
on: [push]
jobs:
test_linux:
runs-on: ubuntu-latest
strategy:
matrix:
image: [ubuntu, fedora]
steps:
- name: Checkout
uses: actions/checkout#v3
- name: Test
uses: docker://${{ matrix.image }}:latest
with:
entrypoint: /bin/bash
args: ./test.sh
This is just a demo workflow. The real one is much more complicated.
However, after I pushed it, GitHub gave me the following error message:
Invalid workflow file: .github/workflows/main.yml#L16
The workflow is not valid. .github/workflows/main.yml (Line: 13, Col: 15): Unrecognized named-value: 'matrix'. Located at position 1 within expression: matrix.image
Actually, I got this error instantly when I pushed codes.
Why? Is the matrix context inaccessible when parsing workflow file? Are there some ways to resolve it?
I tried to export the full docker name to environment in the previous step before "Test", but context env is also inaccessible just like the context matrix. I got nearly the same error. The only different is:
Unrecognized named-value: 'env'...
Now I have to call docker run manually in run scripts like
steps:
- name: Test
run: docker run --rm -v $(pwd):/code --workdir=/code ${{ matrix.image }}:latest /bin/bash -c "/code/test.sh"
It did work but I still prefer to use the uses entry.
I am trying to automate applying label to the GitHub PRs.
I came across this awesome GitHub Action I am not able to understand on where to put labels and any[] or all[]
This is what I have tried so far -
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
#
# To use this workflow, you will need to set up a .github/labeler.yml
# file with configuration. For more information, see:
# https://github.com/actions/labeler
name: Labeler
on: [pull_request]
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler#v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
- name: Applying labels.
deployment:
- any: ['deployment/backend-stack/deployment.yaml']
You can find some aspirations with this commit, basically the labeler config is in .github/labeler.yml.
As described in the official documentation you need to have two files in your .github/ folder:
actual workflow is defined in .github/workflows/labeler.yml
configuration is set in .github/labeler.yml
Difference of the key words any and all:
any: match ALL globs against ANY changed path
all: match ALL globs against ALL changed paths
Example
.github/workflows/labeler.yml
name: Labeler
on: [pull_request]
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler#v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
.github/labeler.yml
deployment:
- any: ['deployment/backend-stack/deployment.yaml']
I want to trigger a Github workflow only if a code is pushed to a specific branch and if a tag exists, but my config (github workflow) does not work as expected:
name: Deployment
on:
push:
branches:
- feature/BRANCH-NAME
tags:
- *
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- uses: actions/setup-node#v1
with:
node-version: '10.x'
- name: Install dependencies
run: |
npm install
- name: Lint & build
run: |
npm run build
The workflow is triggered even if a tag does not exist.
How could I fix this?
EDIT: This workaround seemed to have solved my problem at the time of writing but I cannot guarantee that it still works as expected.
Since I couldn't find a way to implement an AND condition (i.e. tagged AND on master), I used the following workaround:
name: Worflow demo
on:
push:
tags:
- v*
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- name: Exit if not on master branch
if: endsWith(github.ref, 'master') == false
run: exit -1
- name: Next job ...
This will trigger if there is a tagged (e.g. tag v1.0.0) commit pushed:
on:
push:
tags:
- v*
The first step ('Exit if not on master branch') then checks if the current branch doesn't end with master and exits the workflow (the subsequent tests will not start):
- name: Exit if not on master branch
if: endsWith(github.ref, 'master') == false
run: exit -1
Hope this helps someone else as well.
can use release event and github.event.release.target_commitish to make only tags on 'my_branch' to trigger the build
name: workflow demo
on:
release:
types:
- published
jobs:
my_job:
runs-on: ubuntu-latest
steps:
- name: build only on my_branch tag
if: ${{ github.event_name == 'release' && github.event.release.target_commitish == 'my_branch'}}
run: "something"
To fix the multiple unintended runs I removed the "branches:" scalar and just include and !exclude tags that I want my workflow to run on.
Following runs on tagged releases, not on release candidates:
name: 'tagged-release'
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- '!*-rc[0-9]+'
The accepted answer didn't seem to work for me, as pointed out by dilithiummatrix in the comments.
So I tried outputting the available values of the github object, which you can do by adding this in your workflow file to see what is available:
- name: Dump job github var
env:
GITHUB_VAR: ${{ toJson(github) }}
run: echo "$GITHUB_VAR"
From this I noticed that as Billy Clark also pointed out, that github.event.base_ref contains refs/heads/production. So this worked for me:
# Only release from prod branch
- name: Exit if not on production branch
if: endsWith(github.event.base_ref, 'production') == false
run: exit -1
You can so by writing the following YAML code.
Keep in mind that you have to put the branches-ignore so that the workflow is not activated when you create branches. The part where you check whether the tag is pushed to a specific branch is covered in the second part of the answer.
name: Deployment
on:
push:
tags:
- *
branches-ignore:
- '*'
You can check for the name of the branch with the following code; specifically for every step of the job you are trying to accomplish.
- name: job
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
if: contains(env.BRANCH_NAME, <branch-name>)
then follow up with some more code you want the workflow to do.
Here's a GitHub Actions workflow file for a Python project named spam:
name: PyInstaller
on:
[...]
jobs:
create_release:
[...]
make_artifact:
needs: create_release
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
runs-on: ${{ matrix.os }}
env:
ARTIFACT_PATH: dist/spam.zip
ARTIFACT_NAME: spam-${{ runner.os }}.zip
steps:
[...]
When this runs, the workflow fails at startup with this:
The workflow is not valid. [...]:
Unrecognized named-value: 'runner'. Located at position 1 within expression: runner.os
I'm attempting to use the os attribute of the runner context. This SO Q&A mentions that the env context can only be used in specific places, so I suspect something similar is happening here. However, I can't find any official documentation addressing this.
Is there any way to reference the runner context to set an environment variable within the env clause of a job, as shown above?
I'm looking for a way to set the environment variable for all steps in the job, so an env inside a step item won't do.
The workaround I've got for now is to add a step specifically to set environment variables:
steps:
- name: Setup environment
run: |
echo "ARTIFACT_NAME=spam-${{ runner.os }}.zip" >> $GITHUB_ENV
however this only works on the Linux runner.
If you scroll down a bit further in the GitHub Actions docs you linked, there's an example workflow printing different contexts to the log.
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
I set up a test repo with a workflow demonstration:
on: push
jobs:
one:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- 'ubuntu-latest'
- 'windows-latest'
- 'macos-latest'
steps:
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
- name: Get runner OS
env:
RUNNER_OS: ${{ runner.os }}
run: echo "$RUNNER_OS"
- name: Create file with runner OS in name
env:
OS_FILENAME: 'spam-${{ runner.os }}.zip'
run: |
echo "OS_FILENAME=spam-${{ runner.os }}.zip" >> $GITHUB_ENV
touch "./${{ env.OS_FILENAME }}"
touch blah.txt
- name: List created file
run: ls -l "./${{ env.OS_FILENAME }}"
It looks like you can also set and access env in steps, and those persist across workflow steps. For example, I set the environment variable $OS_FILENAME in step 3 using the echo syntax, and reference it in step 4. This works across all the OS options offered on GitHub Actions.
Note that the GitHub Actions docs state that "Environment variables must be explicitly referenced using the env context in expression syntax or through use of the $GITHUB_ENV file directly; environment variables are not implicitly available in shell commands.". Basically, it means you can't implicitly refer to env variables like $FOO and instead must refer to them as ${{ env.FOO }} in shell commands.
So for your scenario, does it satisfy your requirements if you set $ARTIFACT_NAME in the first step of the job? I wonder if the reason might be that the runner context isn't created until the first step - I'm not sure I have a way of testing this.