Skip to content

GitHub Actions Reference

Comprehensive reference for GitHub Actions workflows used across tower-fleet infrastructure.


Overview

We use GitHub Actions for: - Automated code review - Claude AI reviews on PRs - CI/CD pipelines - Build, test, deploy applications - Documentation sync - Keep docs site updated - Infrastructure automation - K8s deployments, database migrations


Reusable Workflows

Reusable workflows defined in tower-fleet repository and called by individual projects.

PR Code Review

Location: .github/workflows/pr-review.yml

Purpose: Automated code review using Claude API

Inputs: - project_name (required, string) - Name of the project - node_version (optional, string, default: '20') - Node.js version

Secrets: - ANTHROPIC_API_KEY (required) - API key for Claude

Example Usage:

# In project repo: .github/workflows/pr-review.yml
name: PR Code Review

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

jobs:
  review:
    uses: jakecelentano/tower-fleet/.github/workflows/pr-review.yml@main
    with:
      project_name: home-portal
      node_version: '20'
    secrets:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

What It Does: 1. Static Analysis Job: - Checks out code - Installs dependencies - Runs linting (npm run lint) - Runs type checking (npx tsc --noEmit) - Gets PR diff and commit history - Uploads analysis artifacts

  1. Claude Review Job:
  2. Downloads analysis artifacts
  3. Runs claude-review.js script
  4. Calls Anthropic API with code review prompt
  5. Posts/updates review comment on PR

Artifacts: - analysis-results (7 days) - Lint/type outputs, diffs, commits - review-output (30 days) - Full review markdown

Documentation: Automated PR Review


Project Workflows

Deployment Workflows

Most projects have deployment workflows that trigger on push to main.

Example (home-portal/.github/workflows/deploy-to-k8s.yml):

name: Deploy to Kubernetes

on:
  push:
    branches:
      - main
  workflow_dispatch:

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

      - name: Build Docker image
        run: |
          docker build -t home-portal:${{ github.sha }} .

      - name: Push to registry
        run: |
          # Push to container registry

      - name: Deploy to K8s
        run: |
          kubectl set image deployment/home-portal \
            home-portal=home-portal:${{ github.sha }}

Common patterns: - Trigger on push to main or workflow_dispatch - Build Docker image with commit SHA tag - Push to registry (Docker Hub, GHCR, etc.) - Update Kubernetes deployment


Scripts

claude-review.js

Location: .github/scripts/claude-review.js

Purpose: Node.js script that calls Claude API for code review

Dependencies (.github/scripts/package.json):

{
  "dependencies": {
    "@anthropic-ai/sdk": "^0.32.1"
  }
}

Usage:

node claude-review.js \
  --diff pr-diff.txt \
  --commits pr-commits.txt \
  --files changed-files.txt \
  --lint lint-output.txt \
  --types type-output.txt \
  --project "home-portal" \
  --pr-number "42" \
  --pr-title "Add feature" \
  --pr-author "jakecelentano" \
  --output review-output.md

Environment Variables: - ANTHROPIC_API_KEY (required) - API key - PROJECT_NAME (optional) - Fallback for --project - PR_NUMBER (optional) - Fallback for --pr-number - PR_TITLE (optional) - Fallback for --pr-title - PR_AUTHOR (optional) - Fallback for --pr-author

Output: Markdown file with structured code review

Prompt Template: Includes: - PR information - Static analysis results - Code changes (diff, commits, files) - Complete code review checklist - Output format specification

Claude Model: claude-sonnet-4-20250514

Token Limits: - Max tokens: 4096 - Temperature: 0 (deterministic)


Secrets Management

Organization Secrets

Recommended: Set secrets at organization level for reuse across repos.

Required Secrets: - ANTHROPIC_API_KEY - For automated code review

Future Secrets (as needed): - DOCKER_HUB_TOKEN - For Docker image pushes - KUBECONFIG - For K8s deployments from CI - SUPABASE_ACCESS_TOKEN - For Supabase migrations

Setting Secrets

Via GitHub CLI:

# Repository secret
gh secret set ANTHROPIC_API_KEY

# Organization secret (accessible to all repos)
gh secret set ANTHROPIC_API_KEY --org jakecelentano

Via GitHub UI: 1. Navigate to: Settings → Secrets and variables → Actions 2. Click "New repository secret" or "New organization secret" 3. Enter name and value 4. Save

Using Secrets in Workflows

Reusable workflow:

jobs:
  review:
    uses: jakecelentano/tower-fleet/.github/workflows/pr-review.yml@main
    secrets:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Standard workflow:

steps:
  - name: Run script
    env:
      API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    run: |
      node script.js


Common Actions

Checkout

- name: Checkout code
  uses: actions/checkout@v4
  with:
    fetch-depth: 0  # Full history (for diffs)

Common options: - fetch-depth: 0 - Full git history - fetch-depth: 1 - Shallow clone (default, faster) - ref: branch-name - Specific branch/tag/SHA - repository: owner/repo - Different repo - path: subdir - Checkout to subdirectory

Setup Node.js

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

Common options: - node-version: '20' - Node version - cache: 'npm' - Cache npm packages - cache: 'yarn' - Cache yarn packages

Upload/Download Artifacts

# Upload
- name: Upload artifacts
  uses: actions/upload-artifact@v4
  with:
    name: analysis-results
    path: |
      lint-output.txt
      type-output.txt
    retention-days: 7

# Download
- name: Download artifacts
  uses: actions/download-artifact@v4
  with:
    name: analysis-results
    path: ./artifacts

Retention: Default 90 days, max 400 days

GitHub Script

- name: Comment on PR
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');
      const body = fs.readFileSync('comment.md', 'utf8');

      await github.rest.issues.createComment({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        body: body
      });

Use cases: - Create/update PR comments - Manage issues, labels, milestones - API calls to GitHub - Complex logic requiring Node.js


Workflow Triggers

Pull Request

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

Event types: - opened - PR created - synchronize - New commits pushed - reopened - PR reopened - closed - PR closed/merged - ready_for_review - Draft → ready

Push

on:
  push:
    branches:
      - main
    paths:
      - 'src/**'
      - '!docs/**'

Options: - branches - Filter by branch - tags - Filter by tag - paths - Include paths - paths-ignore - Exclude paths

Manual Trigger

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deployment environment'
        required: true
        type: choice
        options:
          - development
          - staging
          - production

Input types: - string - Text input - boolean - Checkbox - choice - Dropdown - environment - Environment selector

Schedule

on:
  schedule:
    - cron: '0 0 * * *'  # Daily at midnight UTC

Cron format: minute hour day month weekday


Best Practices

Performance

Cache dependencies:

- uses: actions/setup-node@v4
  with:
    cache: 'npm'  # Speeds up npm ci

Conditional steps:

- name: Deploy
  if: github.ref == 'refs/heads/main'
  run: ./deploy.sh

Parallel jobs:

jobs:
  lint:
    runs-on: ubuntu-latest
    # ...

  test:
    runs-on: ubuntu-latest
    # Runs in parallel with lint
    # ...

  deploy:
    needs: [lint, test]
    # Waits for lint and test to complete
    # ...

Security

Minimal permissions:

permissions:
  contents: read
  pull-requests: write
  issues: write

Pin action versions:

# ✅ Good - pinned to SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4

# ⚠️ Acceptable - pinned to major version
- uses: actions/checkout@v4

# ❌ Bad - floating tag
- uses: actions/checkout@main

Never log secrets:

# ❌ Bad
- run: echo "API_KEY=${{ secrets.API_KEY }}"

# ✅ Good
- run: echo "API_KEY is set"
  if: ${{ secrets.API_KEY != '' }}

Debugging

Enable debug logging:

# In repo secrets
gh secret set ACTIONS_STEP_DEBUG --body "true"
gh secret set ACTIONS_RUNNER_DEBUG --body "true"

View logs:

gh run list --workflow=pr-review.yml
gh run view <run-id>
gh run view <run-id> --log

Download artifacts:

gh run download <run-id>


Troubleshooting

Workflow Not Triggering

Check triggers match event: - PR to non-main branch? Check branches filter - Path outside paths filter? Adjust or remove filter

Verify workflow file:

# Validate YAML syntax
cat .github/workflows/pr-review.yml | yq

Check Actions tab in GitHub UI for: - Workflow runs (even failed ones) - Skipped runs - Error messages

Permission Errors

Error: Resource not accessible by integration

Solution: Update workflow permissions:

permissions:
  contents: read
  pull-requests: write
  issues: write

Or in repo settings: Settings → Actions → General → Workflow permissions → "Read and write permissions"

Secret Not Found

Error: ANTHROPIC_API_KEY environment variable is not set

Solutions:

# Check secret exists
gh secret list

# Set if missing
gh secret set ANTHROPIC_API_KEY

For reusable workflows, secrets must be explicitly passed:

secrets:
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Rate Limits

GitHub API: 1,000 requests/hour (authenticated)

Claude API: See Anthropic docs

Mitigation: - Cache dependencies - Limit workflow runs (e.g., if: github.event_name == 'pull_request') - Use concurrency to cancel redundant runs


Concurrency Control

Prevent multiple runs on rapid pushes:

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

Effect: New PR push cancels in-progress review from previous push.


Monitoring & Costs

GitHub Actions Usage

View usage: - Organization: Settings → Billing → Actions usage - Repository: Insights → Actions usage

Free tier: - Public repos: Unlimited - Private repos: 2,000 minutes/month

Per-minute costs (if exceeding free tier): - Linux: $0.008/minute - Windows: $0.016/minute - macOS: $0.08/minute

Claude API Costs

Current pricing (Claude Sonnet 4): - Input: $3 / million tokens - Output: $15 / million tokens

Per review estimate: - Input tokens: ~3,000 (diff + checklist) - Output tokens: ~1,500 (review) - Cost: ~$0.01-0.02 per review

Monthly estimate (50 PRs × 2 updates): - 100 reviews/month - Cost: ~$1-2/month



Last Updated: 2025-11-24 Maintained By: Infrastructure team