Skip to main content
  1. 🔰Posts/
  2. 🗂️My Trainings/
  3. VCS Trainings and Certifications/
  4. GitHub Actions - The Complete Guide/

GitHub Actions: Controlling Workflow and Job Execution

📚 Part 4 of 5: "GitHub Actions The Complete Guide" series.

External Resources »
GitHub Actions official DocumentationGitHub Actions MarketplaceGitHub.com

Controlling Execution Flow #

Using conditions to control job execution #

You can use the jobs.<job_id>.if conditional to prevent a job from running unless a condition is met. You can use any supported context and expression to create a conditional. For more information on which contexts are supported in this key, see Contexts reference.
The default action of GitHub Actions is when the step fails, the whole job the step belongs to also fails. For that reason we may want to have some form of control during our job execution.

Step execution #

Example:

📄 File: .github/workflows/06-01-execution-flow.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Get code
        uses: actions/checkout@v3
      - name: Cache dependencies
        id: cache
        uses: actions/cache@v3
        with:
          path: ~/06-react-exercise/.npm
          key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
      - name: Install dependencies
        run: npm ci
        working-directory: 06-react-exercise
      - name: Test code
        id: run-tests
        run: npm run test
        working-directory: 06-react-exercise
      - name: Upload test report
        if: ${{ failure() && steps.run-tests.outcome == 'failure' }}
        uses: actions/upload-artifact@v6
        with:
          name: test-report
          path: 06-react-exercise/test.json

Note: id: can be injected to any step to be later called by any context object.
Note: failure function (failure() &&) must be present in order to force GitHub Actions no to follow it’s default behaviour (to stop executions if step fails).

Special Conditional Functions:

  • failure() - Returns true when any previous Step or Job failed.
  • success()- Returns true when none of the previous Step or Job failed.
  • always() - Causing the step to always execute, even when cancelled.
  • cancelled() - Returns true if the workflow has been cancelled.

Job execution #

Example:

📄 File: .github/workflows/06-01-execution-flow.yml

1
2
3
4
5
6
7
8
  build:
    needs: test
    # run this job even if the test job fails
    if: ${{ always() }}
    runs-on: ubuntu-latest
    steps:
      - name: Get code
        uses: actions/checkout@v3

Example: report job depends on lint and deploy jobs (needs: [lint, deploy]). It will still run if any of the jobs fails.

continue-on-error: true changes the behaviour and sets the failed job to successful, even if it failed. Can be used for jobs that are allowed to fail.

Check contexts for more information.

Hint:

steps.<step_id>.conclusionstringThe result of a completed step after continue-on-error is applied. Possible values are success, failure, cancelled, or skipped. When a continue-on-error step fails, the outcome is failure, but the final conclusion is success.
steps.<step_id>.outcomestringThe result of a completed step before continue-on-error is applied. Possible values are success, failure, cancelled, or skipped. When a continue-on-error step fails, the outcome is failure, but the final conclusion is success.

More info:

Matrix Strategies #

The GitHub Actions matrix strategy is a powerful feature that allows you to run the same job multiple times across various combinations of variables (like different operating systems or language versions) without duplicating code. It maximizes parallelism and testing coverage within a single, maintainable workflow definition.

How it Works #

Within a workflow file, you define a strategy block within a job, which contains a matrix key. Inside the matrix, you specify variables as keys and a list of values for each variable. GitHub Actions then automatically generates a separate, parallel job run for every possible combination of these values. 

Example:

This example tests a Node.js application on two different operating systems and two different Node.js versions:

jobs:
  build-and-test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node_version: [14, 16]
    steps:
      - uses: [actions/checkout@v4](github.com)
      - name: Use Node.js ${{ matrix.node_version }}
        uses: [actions/setup-node@v4](github.com)
        with:
          node-version: ${{ matrix.node_version }}
      - name: Install dependencies
        run: npm install
      - name: Run tests
        run: npm test

This configuration creates four jobs:

  • os: ubuntu-latestnode_version: 14
  • os: ubuntu-latestnode_version: 16
  • os: windows-latestnode_version: 14
  • os: windows-latestnode_version: 16

Inclusions and Exclusions in Matrix strategies #

In GitHub Actions matrix strategies, the include keyword adds specific configurations or properties, while the exclude keyword removes unwanted combinations from the default matrix permutations. This provides fine-grained control over which jobs run in the workflow.

Example:

strategy:
  matrix:
    os: [ubuntu-latest, macos-latest]
    node: [14, 16]
    include:
      # Add a specific job for Node 18 on Ubuntu
      - os: ubuntu-latest
        node: 18
      # Add an extra property (e.g., 'test-suite') to the macos-latest/Node 16 job
      - os: macos-latest
        node: 16
        test-suite: "extended"

More info: Running variations of jobs in a workflow

🔥Re-Using Workflows #

Learn how to avoid duplication when creating a workflow by reusing existing workflows.
flowchart TD %% Comments COMMENT1@{ shape: braces, label: "Example: Upload website code to hosting server." } %% Class Definitions classDef redclass fill:#EB4925 classDef redclasss stroke:#EB4925 classDef yellowclass stroke:#EBAC25 classDef greenclass stroke:#C7EB25 %% Workflows WF1@{ shape: doc, label: "**Workflow 1**" } WF2@{ shape: doc, label: "**Workflow 2**" } %% Jobs Job1WF1@{ shape: processes, label: "**Job 1**" } Job1WF2@{ shape: processes, label: "**Job 1**" } Job2WF2@{ shape: processes, label: "**Job 2**" } WF1:::redclass --> Job1WF1:::yellowclass WF2:::redclasss --> Job1WF2:::yellowclass Job1WF1 --> Steps1WF1[Steps]:::greenclass Job1WF2 --> Steps2WF2[Steps]:::greenclass Steps2WF2 --> Job2WF2:::redclass WF1 WF1-Job2WF2@----> Job2WF2 WF1-Job2WF2@{ animate: true } Steps1WF1 -.- COMMENT1

Reusable workflows are YAML-formatted files, very similar to any other workflow file. As with other workflow files, you locate reusable workflows in the .github/workflows directory of a repository. Subdirectories of the workflows directory are not supported.

IMPORTANT: For a workflow to be reusable, the values for on must include workflow_call (See below example).

📄 File: cicd-gh-actions-course/.github/workflows/06-04-reusable-workflow.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
name: 06-04 Reusable Workflow
on:
  workflow_call:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Output information
        run: |
          echo "This is a reusable workflow example."
          echo "You can add deployment steps here."

Using inputs and secrets in a reusable workflow #

You can define inputs and secrets, which can be passed from the caller workflow and then used within the called workflow. There are three stages to using an input or a secret in a reusable workflow.

  1. In the reusable workflow, use the inputs and secrets keywords to define inputs or secrets that will be passed from a caller workflow.

    on:
      workflow_call:
        inputs:
          config-path:
            required: true
            type: string
        secrets:
          personal_access_token:
            required: true
    

    For details of the syntax for defining inputs and secrets, see on.workflow_call.inputs and on.workflow_call.secrets.

  2. In the reusable workflow, reference the input or secret that you defined in the on key in the previous step.

    Note

    If the secrets are inherited by using secrets: inherit in the calling workflow, you can reference them even if they are not explicitly defined in the on key. For more information, see Workflow syntax for GitHub Actions.

    jobs:
      reusable_workflow_job:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/labeler@v6
          with:
            repo-token: ${{ secrets.personal_access_token }}
            configuration-path: ${{ inputs.config-path }}
    

    In the example above, personal_access_token is a secret that’s defined at the repository or organization level.

    Warning

    Environment secrets cannot be passed from the caller workflow as on.workflow_call does not support the environment keyword. If you include environment in the reusable workflow at the job level, the environment secret will be used, and not the secret passed from the caller workflow. For more information, see Managing environments for deployment and Workflow syntax for GitHub Actions.

  3. Pass the input or secret from the caller workflow.

    To pass named inputs to a called workflow, use the with keyword in a job. Use the secrets keyword to pass named secrets. For inputs, the data type of the input value must match the type specified in the called workflow (either boolean, number, or string).

1
2
3
4
5
6
7
    jobs:
      call-workflow-passing-data:
        uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
        with:
          config-path: .github/labeler.yml
        secrets:
          personal_access_token: ${{ secrets.token }}

Workflows that call reusable workflows in the same organization or enterprise can use the inherit keyword to implicitly pass the secrets.

1
2
3
4
5
6
	    jobs:
	      call-workflow-passing-data:
	        uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
	        with:
	          config-path: .github/labeler.yml
	        secrets: inherit

More info: Reuse workflows


» Sources « #

Matrix strategies:

» Disclaimer « #

This series draws heavily from Maximilian Schwarzmüller’s GitHub Actions - The Complete Guide course on Udemy.

About the instructor:
🌐 Website📺 YouTube
💼 LinkedIn🗃️ GitHub
My Repos for this section:
cicd-gh-actions-courseLearnings from "GitHub Actions - The Complete Guide" on Udemy.

ℹ️Shared for educational purposes only, no rights reserved.

Series Overview: GitHub Actions The Complete Guide

5 parts in this series. View full series →


RobK
Author
RobK
DevOps | Agile | AWS | Ansible | Terraform | GitHub Actions | Linux | Windows