GitHub Actions Quick Reference
Automate, build, test, and deploy directly from your GitHub repository.
Workflow Basics
A workflow is a YAML file stored in .github/workflows/ directory.
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
Trigger Events (on)
Push & Pull Request
on:
push:
branches: [ main, develop ]
tags: [ v* ]
paths:
- 'src/**'
- '!docs/**'
pull_request:
branches: [ main ]
types: [ opened, synchronize, reopened ]
Schedule (Cron)
on:
schedule:
- cron: '0 2 * * *' # daily at 2 AM UTC
- cron: '0 8 * * 1' # every Monday at 8 AM
Manual Trigger (workflow_dispatch)
on:
workflow_dispatch:
inputs:
environment:
description: 'Deploy environment'
required: true
type: choice
options:
- staging
- production
version:
description: 'Version to deploy'
required: false
type: string
Other Events
release– when a release is publishedissue_comment– when comments are addedworkflow_run– on completion of another workflowworkflow_call– reusable workflow
Jobs
Basic Job
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building..."
env:
NODE_ENV: production
Dependencies (needs)
jobs:
lint:
runs-on: ubuntu-latest
steps: [ ... ]
test:
needs: lint
runs-on: ubuntu-latest
steps: [ ... ]
deploy:
needs: [ lint, test ]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps: [ ... ]
Job Outputs
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
- id: get_version
run: echo "version=v1.2.3" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Deploying version ${{ needs.build.outputs.version }}"
Matrix Strategy
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
Fail‑Fast and Continue‑on‑Error
strategy: fail-fast: false # continue even if one matrix job fails max-parallel: 4 # run at most 4 jobs in parallel
Steps
Actions
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Shell Commands (run)
- run: npm install
- name: Build application
run: npm run build
- name: Multi-line script
run: |
echo "First line"
echo "Second line"
- run: echo "Running on ${{ runner.os }}"
shell: bash # default; can be pwsh, python, etc.
Conditional Steps (if)
- name: Deploy only on main branch if: github.ref == 'refs/heads/main' run: ./deploy.sh - name: Skip if PR if: github.event_name != 'pull_request' run: npm run integration-tests - name: Run step only on failure if: failure() run: echo "Previous step failed" - name: Run on success if: success() run: echo "Everything succeeded" - name: Always run if: always() run: echo "Runs even if previous step failed"
Environment Variables and Secrets
env:
NODE_ENV: production
API_URL: https://api.example.com
- name: Use secret
run: echo "Deploying with secret ${{ secrets.DEPLOY_TOKEN }}"
- name: Set environment variable dynamically
run: echo "VERSION=$(git describe --tags)" >> $GITHUB_ENV
- name: Use dynamic env var
run: echo "Version is ${{ env.VERSION }}"
Built‑in Contexts and Functions
| Context | Description | Example |
|---|---|---|
github | Info about the event, repo, actor | ${{ github.repository }} |
runner | Info about the runner (OS, temp dir) | ${{ runner.os }} |
env | Environment variables set in workflow | ${{ env.NODE_ENV }} |
secrets | Encrypted secrets from repo settings | ${{ secrets.GITHUB_TOKEN }} |
strategy | Matrix strategy values | ${{ strategy.job-index }} |
needs | Outputs from dependent jobs | ${{ needs.build.outputs.version }} |
Common Expressions
github.event_name– e.g., "push", "pull_request"github.ref– branch or tag referencegithub.sha– commit SHAgithub.actor– user who triggered the workflowgithub.run_id– unique run IDgithub.workspace– default working directory
Artifacts and Caching
Upload Artifacts
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist-files
path: dist/
retention-days: 7
Download Artifacts
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist-files
path: ./downloaded
Caching Dependencies
- name: Cache npm dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache Python packages
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
Reusable Workflows
Calling a Reusable Workflow
jobs:
call-workflow:
uses: ./.github/workflows/build.yml
with:
node-version: '18'
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
Reusable Workflow Definition (workflow_call)
on:
workflow_call:
inputs:
node-version:
required: true
type: string
secrets:
token:
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: npm test
Container Jobs
jobs:
test-in-container:
runs-on: ubuntu-latest
container:
image: node:18-alpine
env:
NODE_ENV: test
options: --memory 2GB
steps:
- uses: actions/checkout@v4
- run: npm test
Service Containers (for databases, etc.)
jobs:
integration-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: testdb
ports:
- 5432:5432
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- run: npm run test:integration
Environment Protection Rules
Deploy to specific environments with required approvals.
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.example.com
steps:
- run: ./deploy.sh
Common Workflow Examples
Node.js CI
name: Node.js CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
Docker Build and Push
name: Docker Build
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/myapp:latest
Deploy to AWS S3
name: Deploy to S3
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucket --delete
PR Comment with Test Results
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test > test-results.txt
- uses: actions/upload-artifact@v4
with:
name: test-results
path: test-results.txt
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const content = fs.readFileSync('test-results.txt', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Test results:\n\`\`\`\n${content}\n\`\`\``
});
Best Practices
- Pin actions to specific versions – e.g.,
@v4or commit SHA for security. - Use
actions/cache– speed up workflow by caching dependencies. - Keep secrets in GitHub Secrets – never hard-code tokens or passwords.
- Use
ifconditions wisely – avoid unnecessary steps. - Break down jobs – separate build, test, deploy for better parallelism.
- Use matrix for testing across versions – increases confidence.
- Set environment protection – for production deployments.
- Use
workflow_dispatch– for manual triggers with inputs. - Add meaningful names to steps and jobs – improves readability.
- Limit permissions – use
permissionsblock to restrict token access. - Pin base images – use specific tags (e.g.,
node:18-alpine) notlatest.
Troubleshooting
- Check logs – every step has detailed output.
- Use
ACTIONS_RUNNER_DEBUG= true – enables verbose debugging. - Use
github.eventcontext – to inspect the payload. - Validate YAML – use online linters or GitHub's built-in validation.
- Re-run failed jobs – with or without debug logging.
// Enable debug logging env: ACTIONS_RUNNER_DEBUG: true // Dump event data - name: Dump event run: echo "${{ toJson(github.event) }}"
Permissions
Limit the default GITHUB_TOKEN permissions:
permissions: contents: read pull-requests: write deployments: write issues: read
Workflow Status Badges
Add badge to README:

📌 Quick Reference
File location: .github/workflows/*.yml
Key sections: name, on, jobs, runs-on, steps
Triggers: push, pull_request, schedule, workflow_dispatch
Caching: actions/cache
Artifacts: upload-artifact, download-artifact
Secrets: stored in repo settings, accessed via ${{ secrets.NAME }}
Matrix: strategy.matrix for parallel testing
Best practice: pin actions, cache deps, use environment protection
Key sections: name, on, jobs, runs-on, steps
Triggers: push, pull_request, schedule, workflow_dispatch
Caching: actions/cache
Artifacts: upload-artifact, download-artifact
Secrets: stored in repo settings, accessed via ${{ secrets.NAME }}
Matrix: strategy.matrix for parallel testing
Best practice: pin actions, cache deps, use environment protection