DevOps

βš™οΈ GitHub Actions Workflows

GitHub Actions is Maxi's Computers' primary CI/CD automation platform. Workflows are YAML files stored in .github/workflows/ and run on GitHub-hosted or self-hosted runners in response to repository events.

Workflow Syntax

yaml
# .github/workflows/ci.yml
name: CI

on:                        # Triggers
  push:
    branches: [main, dev]
  pull_request:
    branches: [main]

defaults:
  run:
    shell: bash

jobs:
  test:
    name: Test Suite
    runs-on: ubuntu-latest      # GitHub-hosted runner
    timeout-minutes: 15         # Kill runaway jobs

    permissions:
      contents: read
      id-token: write           # For OIDC / AWS assume role

    steps:
      - name: Checkout source
        uses: actions/checkout@v4
        with:
          fetch-depth: 0        # Full history for git blame/changelog

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test
        env:
          NODE_ENV: test
          DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}

      - name: Upload coverage report
        uses: codecov/codecov-action@v4
        if: always()
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

Triggers

yaml
# Common trigger patterns
on:
  # Push to specific branches
  push:
    branches: [main, 'release/**']
    paths:
      - 'src/**'         # Only when src changes
      - 'package.json'

  # PRs targeting main
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Manual trigger with inputs
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options: [dev, staging, prod]
      image_tag:
        description: 'Docker image tag to deploy'
        required: true

  # Scheduled (cron)
  schedule:
    - cron: '0 6 * * 1-5'    # 6 AM UTC, Mon-Fri

  # On release creation
  release:
    types: [published]

Secrets & Variables

⚠️

Use OIDC for AWS Prefer OpenID Connect (OIDC) over long-lived AWS access keys in secrets. OIDC tokens are short-lived and scoped to the specific workflow run.

yaml
# OIDC β€” AWS assume role (no static keys)
- name: Configure AWS credentials (OIDC)
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
    role-session-name: GitHubActions-${{ github.run_id }}
    aws-region: us-east-1

# Reference secrets and variables in steps
- run: |
    echo "Deploying to ${{ vars.ENVIRONMENT }}"
    echo "Slack webhook: [REDACTED]"
  env:
    SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
    APP_VERSION: ${{ github.sha }}

# Mask a dynamically generated value
- run: |
    TOKEN=$(generate-token.sh)
    echo "::add-mask::$TOKEN"
    echo "TOKEN=$TOKEN" >> $GITHUB_ENV

Matrix Builds

yaml
# Run tests across Node versions and OSes
test:
  strategy:
    fail-fast: false           # Don't cancel other matrix jobs on failure
    matrix:
      node: ['18', '20', '22']
      os: [ubuntu-latest, macos-latest]
      exclude:
        - os: macos-latest     # Skip Node 18 on macOS (cost saving)
          node: '18'
  runs-on: ${{ matrix.os }}
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node }}
    - run: npm ci && npm test

Dependency Caching

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

# Docker layer caching
- name: Build and push
  uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max
    push: true
    tags: ${{ steps.meta.outputs.tags }}

Reusable Workflows

yaml
# .github/workflows/_deploy.yml (reusable)
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
      image_tag:
        required: true
        type: string
    secrets:
      AWS_DEPLOY_ROLE_ARN:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - name: Configure AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
          aws-region: us-east-1
      - name: Helm deploy
        run: |
          aws eks update-kubeconfig --name mc-${{ inputs.environment }}-eks
          helm upgrade --install mc-api ./charts/api \
            --set image.tag=${{ inputs.image_tag }} \
            --namespace ${{ inputs.environment }}

# Caller workflow
jobs:
  deploy-prod:
    uses: ./.github/workflows/_deploy.yml
    with:
      environment: production
      image_tag: ${{ needs.build.outputs.image-tag }}
    secrets:
      AWS_DEPLOY_ROLE_ARN: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}

Environments & Protection Rules

GitHub Environments add protection gates before jobs can run against protected resources:

  • Required reviewers β€” Designated people must approve before deployment proceeds.
  • Wait timer β€” Delay deployment by N minutes (useful for canary monitoring).
  • Deployment branches β€” Restrict which branches can deploy to an environment.
  • Environment secrets β€” Secrets scoped to a specific environment (e.g., prod DB credentials).

Best Practices

  • Pin action versions to a full SHA: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 prevents supply-chain attacks.
  • Set timeout-minutes on every job to prevent runaway billing.
  • Use permissions: {} at the workflow level and grant only what's needed per job.
  • Cache aggressively β€” Use actions/cache or built-in caching in setup actions.
  • Fail fast on PRs, not on main β€” Use fail-fast: true for PR checks and false for release pipelines.
  • Use concurrency to cancel in-progress runs when new commits are pushed to the same branch.
πŸ“± Install MC Wiki
Add to home screen for offline access.