GitHub Actions Cheatsheet

Deployment

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.

Deployment Environments

Environments add protection rules, required reviewers, and scoped secrets to deployment jobs.

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com   # shown in the Actions UI and PR deployments
    steps:
      - run: ./deploy.sh
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}   # from 'production' environment

Configure environments at: Repo → Settings → Environments.

Environment Protection Rules

RuleWhat it does
Required reviewersJob pauses and notifies reviewers; must be approved to proceed
Wait timerDelays job start by N minutes (1–43,200)
Deployment branchesRestrict which branches/tags can deploy
Deployment policiesCustom rules (Enterprise)

Branch Protection via Environments

# Only allow main and release/* to deploy to production
# Set in: Settings → Environments → production → Deployment branches
# Pattern: main, release/**

In the workflow, this blocks the job from running on other branches — no code change needed.

Concurrency — Prevent Duplicate Deployments

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false    # queue, don't cancel (safe for deploys)

Or cancel for preview/staging:

concurrency:
  group: deploy-preview-${{ github.event.pull_request.number }}
  cancel-in-progress: true

Deploy on Push to Main

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - run: ./scripts/deploy.sh
        env:
          API_KEY: ${{ secrets.API_KEY }}

Deploy on Release Publish

on:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.release.tag_name }}
      - run: echo "Deploying ${{ github.event.release.tag_name }}"
      - run: ./deploy.sh

Deploy to AWS (OIDC — no static keys)

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActions
          aws-region: us-east-1

      - uses: aws-actions/amazon-ecr-login@v2

      - run: |
          docker build -t $ECR_URI:${{ github.sha }} .
          docker push $ECR_URI:${{ github.sha }}
        env:
          ECR_URI: 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp

      - run: |
          aws ecs update-service \
            --cluster my-cluster \
            --service my-service \
            --force-new-deployment

Deploy to Google Cloud (OIDC)

permissions:
  id-token: write
  contents: read

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

  - uses: google-github-actions/deploy-cloudrun@v2
    with:
      service: my-service
      image: gcr.io/my-project/app:${{ github.sha }}
      region: us-central1

Deploy to Azure (OIDC)

permissions:
  id-token: write
  contents: read

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

  - uses: azure/webapps-deploy@v3
    with:
      app-name: my-app
      package: dist/

GitHub Pages Deployment

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deploy.outputs.page_url }}
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

      - uses: actions/upload-pages-artifact@v3
        with:
          path: out/

      - id: deploy
        uses: actions/deploy-pages@v4

Preview / PR Environments

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]

jobs:
  deploy-preview:
    if: github.event.action != 'closed'
    runs-on: ubuntu-latest
    environment:
      name: pr-${{ github.event.number }}
      url: https://pr-${{ github.event.number }}.preview.example.com
    steps:
      - run: ./deploy-preview.sh ${{ github.event.number }}

  teardown:
    if: github.event.action == 'closed'
    runs-on: ubuntu-latest
    steps:
      - run: ./teardown-preview.sh ${{ github.event.number }}

Post Deployment Status to PR

- uses: actions/github-script@v7
  with:
    script: |
      github.rest.repos.createDeploymentStatus({
        owner: context.repo.owner,
        repo: context.repo.repo,
        deployment_id: '${{ steps.deploy.outputs.deployment-id }}',
        state: 'success',
        environment_url: 'https://example.com',
        description: 'Deploy complete',
      });

Or use the simpler PR comment approach:

- uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: '**Deployed to production:** https://example.com',
      });

Rollback Pattern

jobs:
  deploy:
    runs-on: ubuntu-latest
    outputs:
      previous-sha: ${{ steps.get-prev.outputs.sha }}
    steps:
      - id: get-prev
        run: echo "sha=$(git rev-parse HEAD~1)" >> $GITHUB_OUTPUT
      - run: ./deploy.sh ${{ github.sha }}

  rollback:
    needs: deploy
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh ${{ needs.deploy.outputs.previous-sha }}
        env:
          ROLLBACK: 'true'

Deploy Workflow Checklist

  • Use environment protection rules for production (required reviewers + branch restriction).
  • Use OIDC over static credentials whenever the cloud provider supports it.
  • Set concurrency.cancel-in-progress: false to queue, not clobber, concurrent deploys.
  • Upload build artifacts before deploy; download in deploy job — don't rebuild.
  • Always record the deployed SHA/version in a step output or annotation.
  • Post deployment URLs to the PR for preview environments.
  • Implement a rollback job triggered on failure().