GitHub actions 101
Tags: githubactions
Published: 2023-02-19 13:25:47

---
title: GitHub actions 101
published: true
tags: ['githubactions']
canonical_url: https://sevic.dev/notes/github-actions-101/
---

GitHub action is a CI/CD tool integrated within GitHub repositories that can run different kinds of jobs (building, testing, deployment). Store workflow files in `.github/workflows` inside the repository, which will be triggered based on specified conditions.

This post covers GitHub actions basics, from specifying the workflow name to configuring different jobs.

### Name

Specify the name of the workflow with the `name` field.

```yml
# .github/workflows/config.yml
name: CI/CD pipeline
```

### Running

`on` field specifies when the workflow should be running.

#### Automatically

The following configuration runs on every push to a specific branch.

```yml
# .github/workflows/config.yml
on:
  push:
    branches:
      - main
```

The following configuration runs on every push to every branch.

```yml
# .github/workflows/config.yml
on:
  push:
    branches:
      - '*'
```

#### Cron jobs

The following configuration runs at a specified interval (e.g., every hour).

```yml
# .github/workflows/config.yml
on:
  schedule:
    - cron: '0 * * * *'
```

#### Manual triggers

The following configuration enables manual triggering. Trigger it on the Actions tab by selecting the workflow and clicking the Run workflow button.

Use a manual trigger to upload apps to the Google Play console or update the GitHub profile Readme.

```yml
# .github/workflows/config.yml
on:
  workflow_dispatch:
```

### Environment variables

Specify with the `env` field. Set repository secrets in Settings &rarr; Secrets and variables &rarr; Actions page.

```yml
# .github/workflows/config.yml
env:
  API_KEY: ${{ secrets.API_KEY }}
```

### Jobs

Specify the job name with the `name` field. Otherwise, the workflow will use the jobs item as the job name.

Every job can have a separate working directory in case you have multiple subdirectories, and you want to run a different job in a different subdirectory so you can specify it within the defaults field

```yml
jobs:
  job-name:
    defaults:
      run:
        working-directory: directory-name
```

Every job should have a `runs-on` field specified for the [machine](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners), which will be running on (e.g., `ubuntu-latest`) or `container` field with set Docker image (e.g., `node:20.9.0-alpine3.17`)

You can specify multiple tasks inside one job. Every task can have the following fields

- `name` - task name
- `uses` - GitHub action path from [GitHub Marketplace](https://github.com/marketplace?type=actions)
- `with` - parameters for the specified GitHub action
- `run` - bash commands
- `env` - environment variables

```yml
# .github/workflows/config.yml
jobs:
  build:
    name: Custom build job

    runs-on: ubuntu-latest
    # container: node:20.9.0-alpine3.17

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install and build
        run: |
          npm ci
          npm run build
```

Use the `needs` field for running jobs sequentially. It specifies a job that has to be finished before starting the next one. Otherwise, jobs will run in parallel.

```yml
# .github/workflows/config.yml
jobs:
  build:
  # ...
  deploy:
    name: Custom deploy job
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy
        run: |
          npm run deploy
```

Every job can run multiple times with different versions using a matrix strategy (e.g., Node versions 18 and 20 or multiple OS versions inside an array of objects).

```yml
# .github/workflows/config.yml
jobs:
  build:
    name: Custom build job
    strategy:
      matrix:
        node-version: [18, 20]
        os:
          [
            { name: 'linux', image: 'ubuntu-latest' },
            { name: 'windows', image: 'windows-latest' },
            { name: 'macos', image: 'macos-latest' }
          ]

    runs-on: ${{ matrix.os.image }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install and build
        run: |
          npm ci
          npm run build
```

Every job can provision databases for e2e tests with a `services` field like Postgres in the following example.

```yml
# .github/workflows/config.yml
jobs:
  build:
    name: Custom build job
    runs-on: ubuntu-latest

    strategy:
      matrix:
        database-name:
          - test-db
        database-user:
          - username
        database-password:
          - password
        database-host:
          - postgres
        database-port:
          - 5432

    services:
      postgres:
        image: postgres:latest
        env:
          POSTGRES_DB: ${{ matrix.database-name }}
          POSTGRES_USER: ${{ matrix.database-user }}
          POSTGRES_PASSWORD: ${{ matrix.database-password }}
        ports:
          - ${{ matrix.database-port }}:${{ matrix.database-port }}
        # Set health checks to wait until postgres has started
        options: --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      # ...
      - run: npm run test:e2e
        env:
          DATABASE_URL: postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }}
```

Passing artifacts between jobs can be done with uploading (`actions/upload-artifact`) and downloading (`actions/download-artifact`) actions.

```yml
# .github/workflows/config.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install and build
        run: |
          npm ci
          npm run build

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: artifact
          path: public

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: artifact
      # ...
```

### Running locally

You can use [act](https://github.com/nektos/act) to run GitHub actions locally.

Install it and run it with the following commands.

```bash
curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
cp ./bin/act /usr/local/bin/act
act
```