Build continuous integration (CI) workflows by using GitHub Actions

Prepearing for GitHub certification - Build continuous integration (CI) workflows by using GitHub Actions

Table of Contents


How do I use GitHub Actions to create workflows for CI?

Create a workflow from a template

  • Create your workflow
  • Use templates or build it yourself
  • Sample of Node.js template workflow:

{% highlight yml %} name: Node.js CI

on: push: branches: [ master ] pull_request: branches: [ master ]

jobs: build:

runs-on: ubuntu-latest

strategy:
  matrix:
    node-version: [10.x, 12.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
  uses: actions/setup-node@v1
  with:
    node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm test {% endhighlight %}

Action Logs for the build

  • Workflow run produces a log that includes the details of what happened and any errors or test failures.

Customize workflow templates

  • Might want to target a different version of eg. Node and different operating systems
  • Separate build and test steps into different jobs
strategy:

matrix:

    os: [ubuntu-lastest, windows-2016]

    node-version: [8.x, 10.x]
  • Sample of build matrix
    • enables testing across multiple OS and language versions
    • will produce 4 builds for each OS paired with each version of Node
  • Good practice to move tests to separate job
  • This sample job tests against multiple targets

{% highlight yml %} test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-lastest, windows-2016] node-version: [8.x, 10.x] steps:

  • uses: actions/checkout@v1
  • name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }}
  • name: npm install, and test run: | npm install npm test env: CI: true {% endhighlight %}

  • Separating build and test will help easier to understand the workflow log

What are artifacts?

  • Artifact - produced by a workflow that is not a log entry
  • For example, the Node.js build will produce a Docker container that can be deployed
  • The artifact can be uploaded to storage using the action actions/upload-artifact
  • The artifact can be downloaded from storage using the action actions/download-artifact
  • Storing an artifact helps to preserve it between jobs

Artifact storage

  • Artifacts are stored in storage space on GitHub
  • It’s free for public repositories
  • Limited storage for private repositories
  • Artifact is stored for 90 days
build:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v1

      - name: npm install and build webpack

        run: |

          npm install

          npm run build

      - uses: actions/upload-artifact@master

        with:

          name: webpack artifacts

          path: public/
  • Notice the path: attribute in the actions/upload-artifact@master action
    • This is the path to store the artifact
    • Everything is uploaded to directory public/ or you can upload a single file public/mytext.txt
  • To download the artifact
    • Build mas have completed successfully
    • Artifact is uploaded
test:

    needs: build

    runs-on: ubuntu-latest

    steps:

        - uses: actions/checkout@v1

        - uses: actions/download-artifact@master

          with:

            name: webpack artifacts

            path: public

Automate reviews in GitHub using workflows

  • Possible to run a workflow after a reviewer has approved the pull request
  • trigger needed: pull-request-review
  • Optional is to add a label to a pull request

{% highlight yml %} steps: - name: Label when approved uses: pullreminders/label-when-approved-action@master env: APPROVALS: “1” GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ADD_LABEL: “approved” {% endhighlight %}

  • Note the block env:
    • This sets the environment variables for the action
    • Eg. can set number of approvers needed
    • the GITHUB_TOKEN variable is needed because action must make changes to your repository

Customize your workflow with environment variables and artifact data

  • Use default environment variables
  • Use custom environment variables
  • Use custom scripts
  • Cache dependencies
  • Pass artifacts data between jobs

Default environment variables and contexts

  • There are default environment variables to use
  • Only within a runner that is executing a job
  • Are case-sensitive
  • Reference to configuration values for the system and the current user
  • Recommended to use reference to filesystem rather than using hard-coded file paths
  • Specify $ followed by the environment variable’s name
jobs:

  prod-check:

    steps:

      - run: echo "Deploying to production server on branch $GITHUB_REF"
  • Also can use defined variables as contexts
  • Context variables can be used at any point within the workflow
  • Eg. enable to run an if statement to evaluate an expression before the runner is executed
name: CI

on: push

jobs:

  prod-check:

    if: github.ref == 'refs/heads/main'

    runs-on: ubuntu-latest

    steps:

      - run: echo "Deploying to production server on branch $GITHUB_REF"
  • This example is using github.ref
  • Note: the default environment variables are all uppercase where context variables are all lowercase

Custom environment variables

  • Can create custom variable
  • Need to define it in the workflow file using the env context
    • if you want to use it inside a runner
name: CI

on: push

jobs:

  prod-check:

    if: github.ref == 'refs/heads/main'

    runs-on: ubuntu-latest

    steps:

      - run: echo "Nice work, $First_Name. Deploying to production server on branch $GITHUB_REF"

        env:

          First_Name: Mona

Scripts in your workflow

  • Can use the run keyword to run actions or scripts.
jobs:

  example-job:

    steps:

      - run: npm install -g bats
  • Can also run a script as an action
  • Store script in your repository .github/scripts directory
jobs:

  example-job:

    steps:

      - name: Run build script

        run: ./.github/scripts/build.sh

        shell: bash

Cache dependencies with the cache action

  • The need to reuse the same outputs or download dependencies from one run to another
  • Can make the workflow run faster and more efficient
  • To cache use GitHub’s cache action
    • This retrieves a cache identified by a unique key that you provided
Parameter Description Required
Key Refers to the key identifier created when saving and searching for a cache. yes
Path Refers to the file path on the runner to cache or search. yes
Restore-keys consists of alternative existing keys to caches if the desired cache key is not found. no

{% highlight yml %} steps:

  • uses: actions/checkout@v2

  • name: Cache NPM dependencies uses: actions/cache@v2 with: path: ~/.npm key: ${{ runner.os }}-npm-cache-${{ hashFiles(‘**/package-lock.json’) }} restore-keys: | ${{ runner.os }}-npm-cache- {% endhighlight %}

  • Above sample sets the path to ~/.npm and the key includes the runner’s OS and the SHA-256 hash of the package-lock.json file
  • Prefixing the key with ID is useful when using restore-keys fallback and have multiple caches

Pass artifact data between jobs

  • Possible to pass data between jobs within the same workflow
  • Use upload-artifact and download-artifact actions
  • Jobs that are depended on a previous jobs artifact must wait for the dependent job to complete successfully
name: Share data between jobs

on: push

jobs:

  job_1:

    name: Upload File

    runs-on: ubuntu-latest

    steps:

      - run: echo "Hello World" > file.txt

      - uses: actions/upload-artifact@v2

        with:

          name: file

          path: file.txt


  job_2:

    name: Download File

    runs-on: ubuntu-latest

    needs: job_1

    steps:

      - uses: actions/download-artifact@v2

        with:

          name: file

      - run: cat file.txt

Enable step debug logging in a workflow

  • Can enable additional debug logging for two options
    • run
    • steps
  • Enabling this is done by setting some repository secrets that require admin access to the repository to true.
  • Two options
    • To enable runner diagnostic logging, set the ACTIONS_RUNNER_DEBUG secret in the repository that contains the workflow to true.
    • To enable step diagnostic logging, set the ACTIONS_STEP_DEBUG secret in the repository that contains the workflow to true.

Access the workflow logs from the REST API

  • For public repository keep in mind that anyone with read access to the repository can use this endpoint
  • If the repository is private you must use an access token with the repo scope.
GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs

Excerise - Create the CI workflow on GitHub