Skip to content

Instantly share code, notes, and snippets.

@brianjbayer
Last active December 17, 2022 22:55
Show Gist options
  • Save brianjbayer/46d707e8f74f6e25c29e5085ca3e9568 to your computer and use it in GitHub Desktop.
Save brianjbayer/46d707e8f74f6e25c29e5085ca3e9568 to your computer and use it in GitHub Desktop.
Understand the Basics of GitHub Actions Workflows and Workflow Files

The Basics of GitHub Actions Workflows

Corndogs Ohio State Fair- Brian Bayer


You can use GitHub Actions as your Continuous Integration (CI) / Continuous Deployment (CD) pipeline for your GitHub source code repositories.

This gives you the advantages of...

  • Source code control for your CI/CD (e.g. Pull Requests)
  • Collocating it with your application code that uses it
  • Combining with other GitHub Features such as webhooks and secrets

You can also use GitHub Actions (GH Actions) with your free GitHub account to learn how to develop and add CI/CD for your personal applications.

In this post you will learn the basics of a GitHub Actions workflow which can run several different and parallel jobs.

πŸ™‡ For all the things GitHub Actions, see the GitHub Actions Documentation


Location of GitHub Actions Workflow Files

πŸ‘‰ In the repository's root directory...

.github/workflows/

GitHub will look for and automatically run workflow files located in the repository's .github/workflows directory. You can have multiple workflow files and multiple workflows can run simultaneously.

πŸ™‡ From GitHub Actions Documentation on workflow syntax

Testing GitHub Actions Workflows

πŸ™„ Unfortunately GitHub does not provide a native ability to test your GH Actions workflows. This is especially painful for merge-only workflows where the only option is to merge to see if the workflow runs correctly (or even runs).

There is a third-party library nektos/act which apparently allows for local running of GH Actions files, but I have not yet used it.

Some techniques that I have used to test GitHub Actions include...

  1. Putting the GH Actions configuration under test in an on-Pull-Request workflow letting you to run it when pushing a new commit on a Pull Request (PR). However, this approach will not help if there is any data or state that is only present during merges or other events.

  2. Using and calling scripts in your GH Actions workflows where these scripts can be run and tested outside of GitHub Actions.

  3. Creating a separate (and disposable) GitHub repository as a testbed for your GH Actions workflows. This is especially useful when developing and testing merge workflows.

General Structure of a GitHub Actions Workflow File

GH Actions workflow files must be written in YAML syntax and have either a .yml or .yaml file extension.

πŸ™‡ See GitHub Documentation on Workflow syntax for GitHub Actions for more specifics.

Here is an example of a GitHub Actions workflow file from https://github.com/brianjbayer/example-gh-actions...

name: Example Workflow on PR and Push

# What events trigger this workflow
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

# Define Any Workflow Global Variables
env:
  GREETEE: "World"
  GITHUB_REPOSITORY: ${{ github.repository }}:latest

# Define parallel (and file-dependent) jobs
# Each job can run on a different worker so
# state is not shared
jobs:

  greeting:
    runs-on: ubuntu-latest
    steps:
      - name: Hello world
        run: echo "Hello ${GREETEE}!"

  workflow-env-variables:
    runs-on: ubuntu-latest
    env:
      GREETEE: Sarah
    steps:
      - name: Hello greeted one
        run: echo "Hello ${GREETEE}!"

      - name: Greeted local one
        env:
          GREETEE: Sailor
        run: echo "Hello ${GREETEE}!"

  some-info:
    name: Display some git information
    runs-on: ubuntu-latest
    steps:
      # Use an exiting action (git checkout for source)
      - uses: actions/checkout@v1

      - name: Git log
        run: git log

      - name: Show GitHub context
        env:
          GITHUB_CONTEXT: ${{ toJson(github) }}
        run: echo "$GITHUB_CONTEXT"

  dependent-workflow:
    needs: [greeting, some-info]
    runs-on: ubuntu-latest
    env:
      GREETEE: Bob

    steps:
      - uses: actions/checkout@v1

      - name: Run a (workflow) script in source code
        run: ./.github/scripts/greetings-world-script ${GREETEE}

      - name: It has docker
        run: docker --version

Specifying the Workflow Name with name

πŸ™‡ For more information on name, see the GitHub Documentation on Workflow syntax for GitHub Actions

name is used to specify the name of the GH Actions workflow which is displayed on your repository's pages like the Actions page (and PR if workflow is run).

name: Example Workflow on PR and Push

Here the workflow name is shown in the repository's Actions tab...

Workflow Name Displayed Repository Actions Tab

Here the workflow name is shown in the Pull Request page...

Workflow Name Displayed in PR Checks

If you do not specify name, GitHub will set it to the relative workflow file path in the repository.


Specifying the Workflow Triggers with on

πŸ™‡ For more information on on, see the GitHub Documentation on Workflow syntax for GitHub Actions

on is used to specify the specific GitHub events that will trigger the workflow to run. You can specify...

  • Single events
  • Multiple events
  • Time schedule

And you can limit a workflow to only run for specific files, tags, or branches.

Here the workflow only runs on pushes and Pull Requests for the main branch...

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
You can also use the alternate YAML array syntax...
on:
  pull_request:
    branches:
      - main

Specifying the Parallel Workflow Jobs with jobs

πŸ™‡ For more information on jobs, see the GitHub Documentation on Workflow syntax for GitHub Actions

jobs is used to start the specification of the parallel jobs that will be run during your workflow.

jobs:

  greeting:
    runs-on: ubuntu-latest
    steps:
      - name: Hello world
        run: echo "Hello ${GREETEE}!"

Let's take a closer look at specifying a job.

Specifying the Job Name

By default, GitHub will set the name of the job to the YAML Map for the job (i.e. greeting).

Here the (default) workflow name is shown in the Pull Request page...

Default Job Name Displayed in PR Checks

If you want to specify a job name other than the default, you can use name.

  some-info:
    name: Display some git information
    runs-on: ubuntu-latest

Specifying the Machine Type for the Job with runs-on

πŸ™‡ For more information on runs-on, see the GitHub Documentation on Workflow syntax for GitHub Actions

runs-on is used to specify the type of machine on which the job will be run. For example...

  • ubuntu-latest to run on Linux
  • windows-latest to run on Windows Server
  • macos-latest to run on macOS

Making a Job Dependent on Another Job in the Same Workflow with needs

πŸ™‡ For more information on needs, see the GitHub Documentation on Workflow syntax for GitHub Actions

needs is used to specify that the job is dependent on the completion of another job. By default the dependency job must complete successfully otherwise the dependent job will be skipped from running.

For example in the dependent-workflow job shown here, the greeting and some-info jobs will run first and must both complete successfully in order for the dependent-workflow job to run.

  dependent-workflow:
    needs: [greeting, some-info]
    runs-on: ubuntu-latest

Specifying the Tasks for a Job with steps

πŸ™‡ For more information on steps, see the GitHub Documentation on Workflow syntax for GitHub Actions

steps is used to specify the sequential tasks comprising the job in the workflow.

    steps:
      - name: Hello world
        run: echo "Hello ${GREETEE}!"

Generally a step has at least a name (- name) and the command to run run.

Here the step name for the selected job is shown in the Pull Request Actions page...

Step Name for Selected Job Displayed on Actions Tab

Examples of commands that you can run include...

  • (Job Runner) Machine-native commands:
    run: echo "Hello World!"
  • (Source Code) Scripts:
    run: ./.github/scripts/greetings-world-script ${GREETEE}
  • Docker Commands:
    docker --version

You can also use other GitHub Actions in your job with uses.

For example you can use the actions/checkout action to do a git checkout in your job so that your job has the repository's source code available.

Here we are using version 1 of actions/checkout...

  some-info:
    name: Display some git information
    runs-on: ubuntu-latest
    steps:
      # Use an exiting action (git checkout for source)
      - uses: actions/checkout@v1

      - name: Git log
        run: git log

πŸ”₯ To see available actions including those from third-parties, see the GitHub Actions Marketplace


GitHub Actions Workflow Environment Variables

You can define reusable values to use in your workflow with GitHub Actions workflow environment variables.

Some Limitations of Workflow Environment Variables

There are some limitations to GitHub Actions workflow environment variables.

  • You can not use a workflow environment variable defined in another workflow file

  • You can not use a workflow environment variable defined previously in the workflow file to define a later workflow environment variable. For example, here the output would actually be Hello ${GREETEE}!...

      this-will-not-work:
        runs-on: ubuntu-latest
        env:
          GREETEE: Sarah
        steps:
          - name: Greeting Fail
            env:
              MESSAGE: "Hello ${GREETEE}!"
            run: echo "${MESSAGE}"
  • Although you can define and/or set a (workflow) environment variable in one job, that environment variable (or set value) will not be available in another job in the workflow. Because that actual instance of a workflow environment variable and its value only exist on that machine that is running that job.

Defining Workflow Environment Variables

You can define workflow environment variables that are available to...

  1. The entire workflow (and all jobs in it)
    name: Example Workflow on PR and Push
    
    on:
      push:
        branches: [ main ]
    
    env:
      GREETEE: "World"
    
    jobs:
  2. The entire job (and all steps in it)
    workflow-env-variables:
        runs-on: ubuntu-latest
        env:
          GREETEE: Sarah
        steps:
          - name: Hello greeted one
            run: echo "Hello ${GREETEE}!"
  3. A single step in a job
          - name: Show GitHub context
            env:
              GITHUB_CONTEXT: ${{ toJson(github) }}
            run: echo "$GITHUB_CONTEXT"
Scope

✨ The more specific definition of a workflow environment variable is used.

If you define the same name (i.e. override) of a GH Actions workflow environment variable at multiple levels or places in your workflow file, the executing workflow will use the more specific definition, that is in the following order...

  1. Step-level definition
  2. Job-level definition
  3. Workflow (file) level

For example, this workflow when it runs...

name: Example Workflow on PR and Push

on:
  pull_request:
    branches: [ main ]

# Define Any Workflow Global Variables
env:
  GREETEE: "World"

jobs:

  greeting:
    runs-on: ubuntu-latest
    steps:
      - name: Hello world
        run: echo "Hello ${GREETEE}!"

  workflow-env-variables:
    runs-on: ubuntu-latest
    env:
      GREETEE: Sarah
    steps:
      - name: Hello greeted one
        run: echo "Hello ${GREETEE}!"

      - name: Greeted local one
        env:
          GREETEE: Sailor
        run: echo "Hello ${GREETEE}!"

will have this output...

Job Step Output
greeting Hello world Hello World!
workflow-env-variables Hello greeted one Hello Sarah!
workflow-env-variables Greeted local one Hello Sailor!

Setting Workflow Environment Variables

You can set a Workflow environment variable in one step and have it available for a later step in the same job. using...

echo "{environment_variable_name}={value}" >> $GITHUB_ENV

For example...

name: A workflow
on: push
jobs:
  set-env:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set env
      run: echo "LATEST_COMMIT=$(git log -1 --pretty=%H)" >> $GITHUB_ENV
    - name: Test
      run: echo $LATEST_COMMIT

πŸ™‡ I learned this from Stackoverflow and the GitHub Actions Documentation


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment