GitHub Actions: Workflow Automation, Matrix Builds, and OIDC Security

GitHub Actions provides automated workflow orchestration directly within the GitHub ecosystem. This guide covers YAML workflow authoring, event triggers, build matrices, reusable workflows, composite actions, caching strategies, and secretless cloud authentication using OpenID Connect (OIDC).


⚡ Quick Dive

GitHub Actions Core Syntax Cheat Sheet

Keyword / Directive Purpose Example
on: Event triggers that initiate workflow on: [push, pull_request, workflow_dispatch]
runs-on: Specifies runner virtual environment runs-on: ubuntu-latest
needs: Enforces job dependencies (DAG) needs: [lint, test] (waits for both to pass)
strategy.matrix: Parallelize job across OS / versions matrix: { go: ['1.22', '1.23'], os: [ubuntu-latest, macos-latest] }
env: Sets environment variables env: { NODE_ENV: 'production' }
secrets. Access encrypted repository secrets ${{ secrets.PROD_API_TOKEN }}
permissions: Set fine-grained GitHub token scopes permissions: { id-token: write, contents: read }
concurrency: Cancel redundant running workflows on new push concurrency: { group: ${{ github.ref }}, cancel-in-progress: true }

Production Pipeline Template (.github/workflows/ci.yml)

name: Production CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    name: Unit & Integration Tests
    runs-on: ubuntu-latest
    strategy:
      matrix:
        go-version: ['1.22', '1.23']
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go-version }}
          cache: true

      - name: Run Tests with Coverage
        run: go test -v -race -coverprofile=coverage.txt ./...

  build-and-push:
    name: Build & Publish Container
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      
      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

📖 Extended Guide

1. Workflow Architecture & Job Dependency DAG

GitHub Actions organizes workflows into a Directed Acyclic Graph (DAG) of jobs:

                  ┌──────────────┐
                  │ Lint / SAST  │
                  └──────┬───────┘
                         │
        ┌────────────────┴────────────────┐
        ▼                                 ▼
┌──────────────┐                  ┌──────────────┐
│  Unit Tests  │                  │ Build Binary │
└───────┬──────┘                  └───────┬──────┘
        │                                 │
        └────────────────┬────────────────┘
                         ▼ (needs: [unit-tests, build-binary])
                  ┌──────────────┐
                  │ Deploy Prod  │
                  └──────────────┘

Jobs run in parallel by default unless constrained by the needs: keyword.


2. Dependency Caching Strategies

Avoid re-downloading thousands of package dependencies on every commit:

- name: Cache Node Modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

3. Passwordless Cloud Authentication with OpenID Connect (OIDC)

[!IMPORTANT] Security Best Practice: Never store static, long-lived cloud access keys (AWS_SECRET_ACCESS_KEY) in repository secrets. Use OIDC short-lived token exchange instead.

jobs:
  deploy-aws:
    runs-on: ubuntu-latest
    permissions:
      id-token: write # Required for requesting the JWT
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeploymentRole
          aws-region: us-east-1

      - name: Deploy to Amazon ECS
        run: aws ecs update-service --cluster prod-cluster --service web-app --force-new-deployment

4. Composite Actions vs. Reusable Workflows

  • Composite Actions (action.yml): Bundle multiple shell steps into a single reusable action (ideal for standardizing internal setup steps across repositories).
  • Reusable Workflows (on: workflow_call): Complete multi-job workflows invoked from caller workflows with parameter pass-through (ideal for enterprise compliance pipelines).