GitHub Actions Cheatsheet

Secrets and Environment

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.

Secret Types

TypeScopeSet in
Repository secretOne repoRepo → Settings → Secrets and variables → Actions
Organization secretSelected/all repos in orgOrg → Settings → Secrets and variables → Actions
Environment secretOne deployment environmentRepo → Settings → Environments → <env> → Secrets
GITHUB_TOKENCurrent repo, current runAuto-provisioned; no setup needed

Using Secrets

steps:
  - run: ./deploy.sh
    env:
      API_KEY: ${{ secrets.API_KEY }}
      DB_URL: ${{ secrets.DATABASE_URL }}

  # GITHUB_TOKEN is always available
  - run: gh pr comment --body "Done"
    env:
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Secrets are never printed in logs — they appear as ***. Never echo a secret directly; pass via environment variables.

GITHUB_TOKEN — Default Permissions

PermissionDefault
contentsread (write on default branch push)
packagesread
pull-requestsread
issuesread
id-tokennone
deploymentsread
pagesnone

Override at workflow or job level:

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

Variables (Non-Secret Configuration)

Unencrypted key-value pairs visible in logs:

# Access repo/org variables
- run: echo "APP_ENV=${{ vars.APP_ENV }}"
- run: echo "API_URL=${{ vars.API_URL }}"

Set via: Repo → Settings → Secrets and variables → Actions → Variables tab.

Scope matches secrets: repository, organization, environment.

Environment-Level Secrets

jobs:
  deploy:
    environment: production       # gates on required reviewers, rules
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}    # from the 'production' environment

OIDC — Keyless Cloud Auth

Eliminates long-lived cloud credentials. The runner gets a short-lived OIDC token that cloud providers verify.

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActions
          aws-region: us-east-1
          # No static AWS_ACCESS_KEY_ID needed

      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/123/locations/global/workloadIdentityPools/...
          service_account: ci@my-project.iam.gserviceaccount.com

      - uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

Setting Environment Variables

# In workflow/job/step env: key
env:
  NODE_ENV: production
  API_URL: https://api.example.com

# From a step (available to later steps in the same job)
- run: echo "VERSION=$(cat VERSION)" >> $GITHUB_ENV

# From a secret
- run: npm run deploy
  env:
    TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Variable Precedence (highest → lowest)

  1. Step env:
  2. Job env:
  3. Workflow env:
  4. $GITHUB_ENV (set by prior step)
  5. Runner environment defaults

Masking Values in Logs

- run: echo "::add-mask::$MY_SECRET"

Or in JS:

core.setSecret(value);

Any subsequent occurrence of that string in logs is replaced with ***.

Encrypted Secrets via CLI

# Set a repo secret
gh secret set MY_SECRET --body "my-value"
gh secret set MY_SECRET < secret.txt

# From environment variable
gh secret set MY_SECRET --env-file .env

# Set at org level
gh secret set MY_SECRET --org my-org --visibility all

# Set for a specific environment
gh secret set MY_SECRET --env production

# List secrets (names only, not values)
gh secret list
gh secret list --env production

# Delete a secret
gh secret delete MY_SECRET

Variables via CLI

# Set a variable
gh variable set APP_ENV --body "staging"
gh variable set APP_ENV --org my-org

# List variables
gh variable list

# Delete a variable
gh variable delete APP_ENV

Common Patterns

# NPM publish with token
- run: npm publish
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

# SSH deployment
- run: |
    eval "$(ssh-agent -s)"
    ssh-add - <<< "${{ secrets.SSH_PRIVATE_KEY }}"
    rsync -avz dist/ user@host:/var/www/

# Docker registry login
- uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKER_USERNAME }}
    password: ${{ secrets.DOCKER_PASSWORD }}

# GitHub Packages (GHCR) — no secret needed
- uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

GITHUB_TOKEN Scope Summary

# Minimal — read-only for most things
permissions: read-all

# Common CI pattern
permissions:
  contents: read
  checks: write          # annotate test results
  pull-requests: write   # post comments

# Release workflow
permissions:
  contents: write        # create releases, push tags
  packages: write        # push to GHCR

# Pages deploy
permissions:
  contents: read
  pages: write
  id-token: write