GitHub Actions Cheatsheet

Reusable Workflows

Use this GitHub Actions reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

What are Reusable Workflows

A reusable workflow is a regular workflow file that exposes workflow_call as a trigger. Other workflows invoke it with uses: at the job level — not the step level. This enables DRY CI/CD across repos.

Defining a Reusable Workflow

# .github/workflows/deploy.yml  (in the called repo)
name: Deploy (reusable)

on:
  workflow_call:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: string
      image-tag:
        required: false
        type: string
        default: latest
    secrets:
      deploy-key:
        required: true
      api-token:
        required: false
    outputs:
      url:
        description: 'Deployed URL'
        value: ${{ jobs.deploy.outputs.url }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    outputs:
      url: ${{ steps.deploy.outputs.url }}
    steps:
      - id: deploy
        run: |
          ./deploy.sh "${{ inputs.environment }}" "${{ inputs.image-tag }}"
          echo "url=https://${{ inputs.environment }}.example.com" >> $GITHUB_OUTPUT
        env:
          DEPLOY_KEY: ${{ secrets.deploy-key }}

Calling a Reusable Workflow

# .github/workflows/ci-cd.yml  (in the caller repo)
name: CI/CD

on:
  push:
    branches: [main]

jobs:
  build:
    uses: ./.github/workflows/build.yml    # same-repo reference

  deploy-staging:
    needs: build
    uses: my-org/platform/.github/workflows/deploy.yml@main   # cross-repo
    with:
      environment: staging
      image-tag: ${{ needs.build.outputs.image-tag }}
    secrets:
      deploy-key: ${{ secrets.STAGING_DEPLOY_KEY }}

  deploy-production:
    needs: deploy-staging
    uses: my-org/platform/.github/workflows/deploy.yml@v2.1.0
    with:
      environment: production
    secrets: inherit    # pass ALL caller secrets automatically

Input Types

TypeValues
stringAny text
numberNumeric
booleantrue / false
inputs:
  environment:
    type: string
    required: true
  replicas:
    type: number
    default: 2
  debug:
    type: boolean
    default: false

secrets: inherit

Pass all secrets from the caller to the called workflow without listing them explicitly:

jobs:
  deploy:
    uses: ./.github/workflows/deploy.yml
    with:
      environment: production
    secrets: inherit

Use with caution — the called workflow gains access to all caller secrets.

Outputs Between Workflows

# Called workflow
jobs:
  build:
    outputs:
      version: ${{ steps.v.outputs.version }}
    steps:
      - id: v
        run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT

# Caller workflow
jobs:
  build:
    uses: ./.github/workflows/build.yml

  release:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "Version = ${{ needs.build.outputs.version }}"

Outputs must be declared at both the step, job, and workflow_call output levels to propagate.

Nesting Reusable Workflows

caller.yml
  → ci.yml (reusable)
      → lint.yml (reusable)  ← up to 4 levels deep

Max nesting depth: 4 levels. A single caller can call multiple reusable workflows in parallel.

Referencing a Reusable Workflow

Reference formExample
Same repo, branch./.github/workflows/build.yml
Same repo, specific branch/tag./.github/workflows/build.yml@v1
External repo, branchowner/repo/.github/workflows/build.yml@main
External repo, tagowner/repo/.github/workflows/build.yml@v2.1.0
External repo, SHAowner/repo/.github/workflows/build.yml@abc1234

Permissions in Reusable Workflows

The called workflow runs with the caller's GITHUB_TOKEN and inherits its permission scope. Declare permissions in the called workflow to request what it needs:

# called workflow
jobs:
  deploy:
    permissions:
      contents: read
      id-token: write
    runs-on: ubuntu-latest
    ...

Composite Actions vs Reusable Workflows

FeatureComposite ActionReusable Workflow
UnitStepJob(s)
Invoked withuses: in a stepuses: as a whole job
Can use servicesNoYes
Can use matrixNoYes
Can set job outputsNo (only step)Yes
Can specify runnerNoYes
NestingAny depth4 levels
Use caseReusable step logicFull CI/CD pipelines

Calling with Matrix

jobs:
  deploy:
    strategy:
      matrix:
        env: [dev, staging, production]
    uses: ./.github/workflows/deploy.yml
    with:
      environment: ${{ matrix.env }}
    secrets: inherit

Workflow Templates (Organization Level)

Store workflow templates in a repo named .github (at the org level):

.github/
  workflow-templates/
    ci.yml
    ci.properties.json

Properties file:

{
  "name": "Standard CI",
  "description": "Node.js CI template",
  "iconName": "example-icon",
  "categories": ["Node"]
}

Members see these templates when creating new workflows in the org.