Learning Goals

  • Name and explain common workflows to automate in RSE.
  • Explain the differences between the various continuous methodologies.
  • Explain why automation is crucial in RSE.
  • Write and understand basic automation scripts for GitHub Actions.
    • s.t. we understand what PkgTemplates generates for us.

Material is taken and modified from the SSE lecture.


1. Workflow Automation


Why Automation?

  • Automatize tasks
    • Run tests frequently, give feedback early etc.
    • Ensure reproducible test environments
    • Cannot forget automatized tasks
    • Less burden to developer (and their workstation)
    • Avoid manual errors
  • Process often integrated in development workflow
    • Example: Support by Git hooks or Git forges

Typical Automation Tasks in RSE

  • Check code formatting and quality
  • Compile and test code for different platforms
  • Generate coverage reports and visualization
  • Build documentation and deploy it
  • Build, package, and upload releases

Continuous Methodologies (1/2)

  • Continuous Integration (CI)
    • Continuously integrate changes into “main” branch
    • Avoids “merge hell”
    • Relies on testing and checking code continuously
      • Should be automatized

Continuous Methodologies (2/2)

  • Continuous Delivery (CD)
    • Software is in a state that allows new release at any time
    • Software package is built
    • Actual release triggered manually
  • Continuous Deployment (CD)
    • Software is in a state that allows new release at any time
    • Software package is built
    • Actual release triggered automatically (continuously)

Automation Services/Software


2. GitHub Actions


What is “GitHub Actions”?

Automate, customize, and execute your software development workflows right in your repository with GitHub Actions.

From: https://docs.github.com/en/actions


General Information

  • Usage of GitHub’s runners is limited
  • Available for public repositories or accounts with subscription
  • By default Actions run on GitHub’s runners
    • Linux, Windows, or MacOS
  • Quickly evolving and significant improvements in recent years

Components (1/2)

  • Workflow: Runs one or more jobs
  • Event: Triggers a workflow
  • Jobs: Set of steps (running on same runner)
    • Steps executed consecutively and share data
    • Jobs by default executed in parallel
  • Action: Application performing common, complex task (step) often used in workflows
  • Runner: Server that runs jobs
  • Artifacts: Files to be shared between jobs or to be kept after workflow finishes

Components (2/2)

From GitHub Actions tutorial


Setting up a Workflow

  • Workflow file files stored ${REPO_ROOT}/.github/workflows
  • Configured via YAML file
name: learn-github-actions
on: [push]
jobs:
  check-bats-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
      - run: npm install -g bats
      - run: bats -v

Actions

- uses: actions/checkout@v3
- uses: actions/setup-node@v2
  with:
    node-version: '14'
  • Integrated via uses directive
  • Additional configuration via with (options depend on Action)
  • Find actions in marketplace
  • Write own actions

Some Useful Julia Actions

  • Find on github.com/julia-actions

    - uses: julia-actions/setup-julia@v1
         with:
           version: '1.9'
  • More:

    • cache: caches ~/.julia/artifacts/* and ~/.julia/packages/* to reduce runtime of CI
    • julia-buildpkg: build package
    • julia-runtest: run tests
    • julia-format: format code (not really an action, but example)

User-specified Commands

- name: "Single line command"
  run: echo "Single line command"
- name: "Multi line command"
  run: |
    echo "First line"
    echo "Second line. Directory ${PWD}"
  workdir: tmp/
  shell: bash

Events

  • Single or multiple events

    on: [push, fork]
  • Activities

    on:
      issue:
        types:
          - opened
          - labeled
  • Filters

    on:
      push:
        branches:
          - main
          - 'releases/**'

Artifacts

  • Data sharing between jobs and data upload

  • Uploading artifact

    - name: "Upload artifact"
      uses: actions/upload-artifact@v2
      with:
        name: my-artifact
        path: my_file.txt
        retention-days: 5
  • Downloading artifact

    - name: "Download a single artifact"
      uses: actions/download-artifact@v2
      with:
        name: my-artifact

    Note: Drop name to download all artifacts


Test Actions Locally

  • act

  • Relies extensively on Docker

    • User should be in docker group
  • Run act from root of the repository

    act (runs all workflows)
    act --job WORKFLOWNAME
  • Environment is not 100% identical to GitHub’s

    • Workflows may fail locally, but work on GitHub

Further Reading


3. Demo: Automation with GitHub Actions


Setting up a Test Job

  • Import Julia test package repository (the same code we used for testing)

  • Set up workflow file

    mkdir -p .github/workflows
    cd .github/workflows
    vi format-check.yml
  • Let’s check whether our code is formatted correctly. Edit format-check.yml to have following content

    name: format-check
    
    on: [push, pull_request]
    
    jobs:
      format:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - uses: julia-actions/setup-julia@v1
            with:
              version: '1.9'
          - name: Install JuliaFormatter and format
            run: |
              julia  -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter"))'
              julia  -e 'using JuliaFormatter; format(".", verbose=true)'
          - name: Format check
            run: |
              julia -e '
              out = Cmd(`git diff --name-only`) |> read |> String
              if out == ""
                  exit(0)
              else
                  @error "Some files have not been formatted"
                  write(stdout, out)
                  exit(1)
              end'     
  • runs-on does not refer to a Docker container, but to a runner tag.

  • Add, commit, push

  • After the push, inspect “Action” panel on GitHub repository

    • GitHub will schedule a run (yellow dot)
    • Hooray. We have set up our first action.
  • Failing test example:

    • Edit settings on GitHub that one can only merge if all tests pass:
      • Settings -> Branches -> Branch protection rule
      • Choose main branch
      • Enable “Require status checks to pass before merging”. Optionally enable “Require branches to be up to date before merging”
      • Choose status checks that need to pass: test
      • Click on “Create” at bottom of page.
    • Create a new branch break-code.
    • Edit some file, violate the formatting, commit it and push it to the branch. Afterwards open a new PR and inspect the failing test. We are also not able to merge the changes as the “Merge” button should be inactive.

act Demo

  • act is for quick checks while developing workflows, not for developing the code

  • Check available jobs (at root of repository)

    act -l
  • Run jobs for push event (default event)

    act
  • Run a specific job

    act -j test

4. Exercise

Set up GitHub Actions for your statistics package. They should format your code and run the tests. To structure and parallelize things, you could use two separate jobs.