GitHub Actions Cheatsheet

Using Actions

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.

Referencing Actions

# From GitHub Marketplace (owner/repo@ref)
- uses: actions/checkout@v4
- uses: actions/setup-node@v4

# Pin to exact commit SHA (most secure)
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2

# From a branch (not recommended for production)
- uses: my-org/my-action@main

# From a local path (relative to workspace root)
- uses: ./.github/actions/my-local-action

# Docker Hub image
- uses: docker://node:20-alpine

# GHCR image
- uses: docker://ghcr.io/owner/image:latest

Essential Actions — Quick Reference

ActionPurposeKey with inputs
actions/checkout@v4Clone the reporef, fetch-depth, token
actions/setup-node@v4Install Node.jsnode-version, cache
actions/setup-python@v5Install Pythonpython-version, cache
actions/setup-java@v4Install Javajava-version, distribution
actions/setup-go@v5Install Gogo-version, cache
actions/setup-dotnet@v4Install .NETdotnet-version
actions/cache@v4Cache files/dirspath, key, restore-keys
actions/upload-artifact@v4Upload build outputname, path, retention-days
actions/download-artifact@v4Download artifactname, path
actions/github-script@v7Run JS with Octokitscript
actions/create-release@v1Create GitHub releasetag_name, release_name
docker/login-action@v3Login to registryregistry, username, password
docker/build-push-action@v6Build & push imagecontext, push, tags

actions/checkout

- uses: actions/checkout@v4
  with:
    ref: ${{ github.event.pull_request.head.sha }}
    fetch-depth: 0          # full history (default: 1 = shallow)
    submodules: recursive
    token: ${{ secrets.PAT }}   # override GITHUB_TOKEN for private repos
    sparse-checkout: |      # only check out listed paths
      src/
      package.json

actions/setup-node

- uses: actions/setup-node@v4
  with:
    node-version: 20
    node-version-file: .nvmrc    # or .node-version, package.json
    cache: npm                   # npm | yarn | pnpm
    cache-dependency-path: '**/package-lock.json'
    registry-url: 'https://registry.npmjs.org'

actions/setup-python

- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    python-version-file: .python-version
    cache: pip               # pip | pipenv | poetry
    cache-dependency-path: requirements*.txt

actions/github-script

- uses: actions/github-script@v7
  with:
    script: |
      const { data: pr } = await github.rest.pulls.get({
        owner: context.repo.owner,
        repo: context.repo.repo,
        pull_number: context.issue.number,
      });
      await github.rest.issues.createComment({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        body: `PR title: ${pr.title}`,
      });

Docker Build & Push

steps:
  - uses: actions/checkout@v4

  - uses: docker/setup-buildx-action@v3

  - uses: docker/login-action@v3
    with:
      registry: ghcr.io
      username: ${{ github.actor }}
      password: ${{ secrets.GITHUB_TOKEN }}

  - uses: docker/metadata-action@v5
    id: meta
    with:
      images: ghcr.io/${{ github.repository }}
      tags: |
        type=ref,event=branch
        type=semver,pattern={{version}}
        type=sha

  - uses: docker/build-push-action@v6
    with:
      context: .
      push: ${{ github.event_name != 'pull_request' }}
      tags: ${{ steps.meta.outputs.tags }}
      labels: ${{ steps.meta.outputs.labels }}
      cache-from: type=gha
      cache-to: type=gha,mode=max

Writing a Custom Action

Composite Action (action.yml)

# .github/actions/setup-app/action.yml
name: 'Setup App'
description: 'Install deps and cache'
inputs:
  node-version:
    description: 'Node version'
    default: '20'
outputs:
  cache-hit:
    description: 'Whether cache was restored'
    value: ${{ steps.cache.outputs.cache-hit }}

runs:
  using: composite
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
    - id: cache
      uses: actions/cache@v4
      with:
        path: node_modules
        key: node-${{ hashFiles('package-lock.json') }}
    - if: steps.cache.outputs.cache-hit != 'true'
      run: npm ci
      shell: bash

Use it:

- uses: ./.github/actions/setup-app
  with:
    node-version: 20

JavaScript Action (action.yml + index.js)

name: 'My JS Action'
inputs:
  message:
    required: true
runs:
  using: node20
  main: index.js
// index.js
const core = require('@actions/core');
const github = require('@actions/github');

try {
  const message = core.getInput('message');
  core.info(`Message: ${message}`);
  core.setOutput('result', message.toUpperCase());
} catch (err) {
  core.setFailed(err.message);
}

@actions/core API

core.getInput('name')           // get input
core.setOutput('key', 'val')    // set output
core.setFailed('message')       // fail the action
core.info('message')            // log info
core.warning('message')         // log warning
core.error('message')           // log error
core.setSecret('value')         // mask value in logs
core.exportVariable('K', 'V')   // set env for subsequent steps
core.addPath('/usr/local/bin')  // add to PATH
core.summary.addRaw('## Done').write()  // step summary

Controlling Permissions for Actions

permissions:
  contents: read       # checkout
  packages: write      # push to GHCR
  id-token: write      # OIDC (AWS/GCP/Azure)
  pull-requests: write # post PR comments
  issues: write
  statuses: write
  checks: write
  deployments: write
  security-events: write  # SARIF upload