GitHub Actions Cheatsheet
Jobs and Steps
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.
Job Definition
jobs:
build:
name: 'Build & Test' # display name (optional)
runs-on: ubuntu-latest
timeout-minutes: 30 # default: 360
continue-on-error: false # fail workflow if job fails
if: github.ref == 'refs/heads/main'
env:
NODE_ENV: test
steps:
- uses: actions/checkout@v4
- run: npm ci && npm testJob Dependencies — needs
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: npm run build
test:
needs: build # single dependency
runs-on: ubuntu-latest
steps:
- run: npm test
deploy:
needs: [build, test] # multiple — runs after BOTH succeed
runs-on: ubuntu-latest
steps:
- run: ./deploy.shDependency Graph with Conditionals
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
test:
needs: lint
runs-on: ubuntu-latest
steps:
- run: npm test
notify-failure:
needs: [lint, test]
if: failure() # runs even if lint or test failed
runs-on: ubuntu-latest
steps:
- run: ./notify.sh "Build failed"Job Outputs
jobs:
build:
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.tag }}
sha-short: ${{ steps.sha.outputs.short }}
steps:
- id: sha
run: echo "short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- id: meta
run: echo "tag=myapp:$(cat VERSION)" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Deploying ${{ needs.build.outputs.image-tag }}"Step Types
steps: # Run an Action from Marketplace - uses: actions/checkout@v4 with: fetch-depth: 0 # Run inline shell - name: Install deps run: npm ci # Multi-line shell - name: Build run: | npm run build npm run lint # Custom shell - name: Python script shell: python run: | import os print(os.environ.get('HOME')) # Conditional step - name: Deploy if: github.ref == 'refs/heads/main' && success() run: ./deploy.sh
Step Conditionals
# Status-based - if: success() - if: failure() - if: always() - if: cancelled() # Combine with event/context - if: github.event_name == 'push' && github.ref == 'refs/heads/main' # Check a previous step's outcome - id: tests run: npm test continue-on-error: true - if: steps.tests.outcome == 'failure' run: echo "Tests failed, sending alert" # Skip for draft PRs - if: github.event.pull_request.draft == false run: ./expensive-check.sh
Step conclusion values: success, failure, skipped, cancelled
Step outcome values: success, failure, skipped, cancelled
outcome= result beforecontinue-on-error;conclusion= result after.
Step Outputs
- id: release
run: |
VERSION=$(cat package.json | jq -r .version)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=v$VERSION" >> $GITHUB_OUTPUT
- run: echo "Version is ${{ steps.release.outputs.version }}"Environment Files
# Set env var for later steps echo "MY_VAR=hello" >> $GITHUB_ENV # Add directory to PATH echo "/opt/my-tool/bin" >> $GITHUB_PATH # Write step summary (Markdown rendered in Actions UI) cat << 'EOF' >> $GITHUB_STEP_SUMMARY
Test Results
| Suite | Status |
|---|---|
| Unit | Passed |
| E2E | Passed |
| EOF | |
Passing Data Between Steps
steps:
- id: build
run: |
echo "artifact=dist/app.tar.gz" >> $GITHUB_OUTPUT
- run: echo "Built ${{ steps.build.outputs.artifact }}"
- run: echo "ARTIFACT=${{ steps.build.outputs.artifact }}" >> $GITHUB_ENV
- run: echo "Path is $ARTIFACT" # via env varContainer Jobs
jobs:
test:
runs-on: ubuntu-latest
container:
image: node:20-alpine
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
env:
NODE_ENV: test
options: --cpus 2
steps:
- uses: actions/checkout@v4
- run: node --version && npm testif Expressions — Common Patterns
| Pattern | Expression |
|---|---|
| Only on main | github.ref == 'refs/heads/main' |
| Only on tags | startsWith(github.ref, 'refs/tags/') |
| On PR to main | github.event_name == 'pull_request' |
| Skip forks | github.event.repository.fork == false |
| After failure | failure() |
| Always run | always() |
| Commit msg contains | contains(github.event.head_commit.message, '[deploy]') |
| Is not a draft PR | github.event.pull_request.draft == false |
| Actor is owner | github.actor == github.repository_owner |
Timeout and Error Handling
jobs:
flaky-tests:
runs-on: ubuntu-latest
timeout-minutes: 20
continue-on-error: true # workflow succeeds even if job fails
steps:
- name: Flaky test
continue-on-error: true # step-level; doesn't fail the job
timeout-minutes: 5
run: npm run e2e
- name: Always cleanup
if: always()
run: ./cleanup.sh