Kahibaro
Discord Login Register

6.2.3 GitHub Actions

Introduction

GitHub Actions is an automation platform built into GitHub that lets you run workflows in response to events in your repositories. For DevOps on Linux, it is especially useful for automating builds, tests, deployments, and checks on code stored in GitHub.

This chapter focuses on how GitHub Actions works, how workflows are structured, and what is specific to using it in typical Linux based DevOps scenarios. General CI/CD principles and general YAML syntax are covered in other chapters, so here we will only connect those ideas to GitHub Actions itself.

Core Concepts of GitHub Actions

GitHub Actions revolves around a few core ideas: events, workflows, jobs, and steps. Everything you do in GitHub Actions is described in YAML files stored in your repository, usually under .github/workflows/.

A workflow is a configurable automation that runs when a specified event occurs. Typical events include a push to a branch, opening a pull request, or running a workflow manually. Within a workflow you define one or more jobs. Each job runs in its own virtual machine or container. Inside each job there are steps, which are individual actions, either shell commands or reusable "actions" provided by GitHub or the community.

Workflows are defined using YAML. For example, a very minimal workflow file might look like this:

name: Example workflow
on:
  push:
    branches: [ "main" ]
jobs:
  example-job:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v4
      - name: Run a command
        run: echo "Hello from GitHub Actions on Linux"

This tells GitHub to run example-job on an Ubuntu Linux runner every time there is a push to the main branch.

Important rule: Every workflow file must be stored in the .github/workflows/ directory of the repository and must be valid YAML, otherwise GitHub Actions will ignore it.

Events and Triggers

GitHub Actions workflows start when an event occurs. The on section of a workflow file describes what should trigger the workflow. Some common triggers useful in DevOps are:

push events, which start workflows when commits are pushed, often to main or feature branches. pull_request events, which start workflows when pull requests are opened or updated. workflow_dispatch, which allows manual runs from the GitHub web interface. schedule, which uses cron style syntax to run workflows periodically.

For example, to run tests on every push and also allow manual triggering, you might write:

on:
  push:
    branches: [ "main", "develop" ]
  workflow_dispatch:

You can combine multiple events and even filter them, for example to react only when certain paths change. This flexibility lets you tune when your Linux build or deployment pipelines execute, without moving away from GitHub.

Jobs and Runners

Jobs are units of work that run in parallel by default. Each job runs on a runner. A runner is a machine with an agent that executes the workflow job. GitHub provides hosted runners, including several Linux images, or you can host your own runners on Linux machines.

In GitHub Actions, specifying a runner for a job is done with runs-on. For Linux based CI/CD, you will most often see:

runs-on: ubuntu-latest

This uses a GitHub hosted Ubuntu runner with a preinstalled set of tools. You can also specify specific versions such as ubuntu-22.04 if you need consistent behavior.

A job definition might look like this:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # steps here

If you have multiple jobs, they run in parallel unless you define dependencies. To make one job depend on another, use needs:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Building"
  test:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - run: echo "Testing after build"

This can be useful for DevOps pipelines where you first build artifacts on a Linux runner, then test or deploy them in subsequent jobs.

Steps and Actions

Within a job, steps run sequentially in the same environment. Each step either uses an action or runs a command. Actions are reusable pieces of automation that encapsulate logic, while run steps are simple shell commands.

A typical sequence of steps in a Linux CI workflow is: check out the code, set up a language environment, install dependencies, run tests, and possibly build and upload artifacts.

For example:

steps:
  - name: Check out repository
    uses: actions/checkout@v4
  - name: Set up Python
    uses: actions/setup-python@v5
    with:
      python-version: "3.11"
  - name: Install dependencies
    run: |
      python -m pip install --upgrade pip
      pip install -r requirements.txt
  - name: Run tests
    run: pytest

Each run step on Linux is executed using /bin/bash by default on the Ubuntu runners. This means you can rely on typical Linux command line behavior and tools in your workflow scripts.

Using the GitHub Hosted Linux Environment

When you use runs-on: ubuntu-latest, your jobs run inside a fresh virtual machine that runs a specific Ubuntu version with a large set of tools preinstalled. This environment is recreated for every job, so any files or changes you create do not persist between runs unless you explicitly store them as artifacts or cache them.

Common Linux command line tools are available, such as bash, git, curl, and package managers like apt. You can install additional tools in a step:

- name: Install system packages
  run: |
    sudo apt-get update
    sudo apt-get install -y libpq-dev

Since each job starts from a clean Linux image, DevOps workflows on GitHub Actions tend to be predictable and reproducible. Any required tool or configuration should be described in the workflow or in scripts stored in the repository, not assumed to exist on the runner.

Caching and Artifacts in Linux Builds

To speed up repeated Linux builds or tests, GitHub Actions lets you cache files between workflow runs. This is especially important for package caches, compiled dependencies, or build outputs. Another related concept is artifacts, which are files that you want to store and download after a workflow run completes.

Caching is usually done with the official actions/cache action. You provide a key that represents a specific state of your dependencies. For example, to cache Python dependencies on Linux:

- name: Cache pip
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

This tells GitHub Actions to store the pip cache from a Linux runner and restore it on future runs when the requirements file has not changed.

Artifacts, in contrast, are meant as outputs you want to download. To upload build artifacts from a Linux build job:

- name: Upload build artifacts
  uses: actions/upload-artifact@v4
  with:
    name: linux-build
    path: dist/

This preserves build outputs created in dist/ so they can be inspected or used by other jobs or people.

Environments, Secrets, and Security

In DevOps workflows, you often need to interact with external services, such as registries or cloud providers. To do this securely, GitHub Actions provides secrets and environments.

Secrets are encrypted values stored in the repository or organization settings. They are injected into workflows as environment variables. For example, you might define DOCKER_PASSWORD as a secret and then use it in a Linux job:

- name: Log in to registry
  run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

Secrets are never printed in logs in plain text. They are specific to GitHub Actions and are crucial for safe automation.

Environments allow you to group secrets and configure protection rules per environment, like staging or production. When deploying from a Linux runner to a production server, you can require manual approval or restrict who can trigger that deployment. In the workflow, you reference the environment:

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: ./deploy.sh

This lets you structure your DevOps pipeline so that production deployments from Linux runners are controlled and auditable.

Important rule: Never hard code passwords, tokens, or API keys directly in workflow YAML or source code. Always store them as GitHub secrets and reference them with ${{ secrets.NAME }}.

Matrix Builds on Linux

Matrix builds are a GitHub Actions feature that lets you define multiple similar jobs across different parameters, such as versions, operating systems, or configurations, using a single job definition.

For Linux focused DevOps work, you might use a matrix to test multiple language versions, frameworks, or toolchains on the same Ubuntu runner. For example:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [ "3.9", "3.10", "3.11" ]
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest

In this case, your tests run three times on Linux, once for each Python version, without needing to write three separate jobs. Matrix builds help you keep Linux CI configurations concise and powerful.

Connecting GitHub Actions to External Linux Systems

GitHub Actions can act as a central automation hub while your actual runtime or production systems are separate Linux servers or cloud instances. A common pattern is to run build and test jobs on GitHub Actions’ Linux runners and then deploy from there to external Linux hosts.

To perform operations on remote Linux machines, you typically interact over SSH or use CLI tools. For example, you might use an action that wraps ssh, or simply call ssh directly if you have installed openssh-client and configured keys via secrets.

A simplified example that connects from the Ubuntu runner to a remote Linux server with SSH could be:

- name: Deploy to server
  run: |
    ssh -o StrictHostKeyChecking=no user@server.example.com "cd /var/www/app && ./deploy.sh"
  env:
    SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

Before running this, you would import the private key into an SSH agent in an earlier step. This pattern lets you use GitHub Actions as a Linux based orchestrator that interacts with other Linux machines across your infrastructure.

Summary

GitHub Actions provides a flexible, GitHub integrated platform for automating DevOps workflows on Linux. Workflows are defined in YAML files, triggered by repository events, and run as jobs and steps on Linux runners such as ubuntu-latest. You can rely on a consistent Linux environment, use caching and artifacts to manage build data, protect sensitive information with secrets and environments, and coordinate with external Linux systems using SSH and other tools.

By understanding how events, jobs, runners, and steps work together, you can create robust CI/CD pipelines that run reliably within the GitHub ecosystem and operate across your wider Linux based infrastructure.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!