GitHub Actions

GitHub Actions were introduced in 2018 to design CI/CD workflows for GitHub projects. Actions are relatively short and reusable pieces of code that simplify the process of creating a CI/CD workflow.


  • Steps βš’οΈ: a command or an action such as run tests

  • Jobs πŸ—ƒοΈ: a job is an ordered set of steps to achieve a goal. They can be run in parallel or sequentially.

  • Workflow πŸš€: an automated process made of jobs. For instance, we could have a workflow for building and testing the project.

The workflow status is visible next to each commit: pipeline success (passed).

Basic syntax

Workflows are defined in YAML files stored in .github/workflows/. Their names are based on the YAML file name (e.g., Build for build.yml).

A basic Action executing job_name on every push:

name: Action Name

on: [push]

    runs-on: ubuntu-latest
    # unnamed command
    - run: echo "Hello, World!"
    # named command
    - name: Saying Hello
      run: echo "Hello, World!"

A runner is an agent executing commands. The runs-on parameter designates which runner to use. You can use GitHub-hosted runners such as ubuntu-latest or windows-latest or use self-hosted runners.

A common action is checkout to clone your repository:

    - name: Check out repository code
      uses: actions/checkout@v3

For each step, you may use these optional attributes:

      - name: XXX
        # only execute based on a condition
        if: runner.os == 'Windows'
        # run multiple commands
        run: |
        env: # set an environment variable
          XXX: xxx

Advanced syntax


The on keyword determines what events can trigger a workflow.

  pull_request:         # on any pull request

  push:                 # push on main
    branches: [ main ]
  pull_request:         # merge request on main
    branches: [ main ]
  schedule:             # automatically
    - cron: '0 3 * * 5'


The strategy keyword lets us define variables. The job below will be executed 4 times as we have 4 values (cartesian product of all variables).

        some_variable: [10, 11, 12, 13]

      - name: Use Version ${{ matrix.some_variable }}
        run: echo "use version ${{ matrix.some_variable }}"

Some actions

Java actions

actions/setup-java to install and configure Java.

    - name: Set up JDK 16
      uses: actions/setup-java@v2
        java-version: '16'
        distribution: 'adopt'

    - name: Run gradle tests
      uses: gradle/gradle-build-action@v2
        arguments: test

➑️ See also: gradle-wrapper-validation.

OCAML actions

ocaml/setup-ocaml to install and configure OCaml.

      - name: Use OCaml 4.13.1
        uses: ocaml/setup-ocaml@v2
          ocaml-compiler: 4.13.1

Node.js actions

actions/setup-node to install and configure Node.

    - name: Use Node.js 16.16.0
      uses: actions/setup-node@v2
        node-version: 16.16.0
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test

Cache build

actions/cache to cache build results.

Static code analysis

GitHub/codeql-action for CodeQL static code analysis.

Code quality analysis

JetBrains/qodana-action for Qodana code quality analysis.


Dependabot is a "bot" that checks your dependencies. If it detects that we can upgrade a dependency, it opens a pull-request with the suggested upgrade. Here is the official tutorial πŸš€.

It can also detect vulnerabilities in dependencies and notify developers to upgrade their dependencies.

Here are some dependabot.yml examples:

version: 2
  - package-ecosystem: "gradle"
    directory: "/"
      interval: "daily"
version: 2
  - package-ecosystem: "npm"
    directory: "/"
      interval: "daily"
      time: "13:00"
    open-pull-requests-limit: "99"
    versioning-strategy: "increase"

It works by analyzing dependencies in the ecosystem file:

  • npm: package.json
  • gradle: build.gradle
  • docker: Dockerfile
  • pip: requirements.txt
  • ...

πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.