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
| Type | Scope | Set in |
|---|---|---|
| Repository secret | One repo | Repo → Settings → Secrets and variables → Actions |
| Organization secret | Selected/all repos in org | Org → Settings → Secrets and variables → Actions |
| Environment secret | One deployment environment | Repo → Settings → Environments → <env> → Secrets |
GITHUB_TOKEN | Current repo, current run | Auto-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
| Permission | Default |
|---|---|
contents | read (write on default branch push) |
packages | read |
pull-requests | read |
issues | read |
id-token | none |
deployments | read |
pages | none |
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' environmentOIDC — 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)
- Step
env: - Job
env: - Workflow
env: $GITHUB_ENV(set by prior step)- 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