We raised $3M for agent-native runners
GitHub CI/CD pipeline

GitHub Actions CI/CD from push to deploy

A GitHub Actions CI/CD pipeline takes a commit from push to production in one workflow file. This guide builds it stage by stage: triggers, build, test, caching, environments and approvals, secrets and OIDC credentials, and the deploy step. Each stage is fast by default and links to the guide that teaches it, and the guide ends with one complete workflow to copy.

Run Actions fasterHow StarSling works

Topics covered: This guide covers triggers and path filters, parallel build and test jobs, dependency caching, test sharding, environments and approvals, secrets and OIDC credentials, deploy steps for AWS, Azure, Google Cloud, GitHub Packages and GitHub Pages, and a complete workflow file.

A GitHub Actions CI/CD pipeline is one workflow file. Push and pull request events trigger it, build and test run as parallel jobs with dependencies cached on the lockfile and tests sharded across a matrix, and a deploy job that needs both runs on main through a protected environment, signing in to the cloud with short-lived OIDC credentials. Path filters, a concurrency group, job timeouts and actions pinned to commit SHAs keep it fast and safe by default. StarSling runs the same workflow on faster drop-in Ubuntu runners, and its agents open pull requests that keep the pipeline fast.

Real results

TL;DR

  • A GitHub Actions CI/CD pipeline is one workflow file in .github/workflows/: triggers decide when it runs, jobs build and test the commit, and a deploy job ships it once both pass.
  • Fast by default means four habits from the first commit: build and test run as parallel jobs, dependencies come from a cache keyed on the lockfile, long test suites shard across a matrix, and a new push cancels the pull request run it supersedes.
  • Production deploys run in a protected environment with required reviewers, and sign in to the cloud with short-lived OIDC credentials scoped to the deploy job.
  • Copy the complete workflow to start, or hand the prompt below to your coding agent to bring an existing pipeline up to this shape.

Start here: hand this to your agent

Paste this prompt into a coding agent pointed at your repository. It maps the pipeline you have today, then opens one small PR that brings each stage up to the fast default this guide describes, with its reasoning for every change.

Prompt for your coding agent
Inspect .github/workflows/*.yml and .github/workflows/*.yaml and map this repository's CI/CD pipeline from push to deploy: which events trigger it, which jobs build, test and deploy, what each job caches, which environment and approval gate each deploy, and how each deploy authenticates. Then bring each stage up to a fast, safe default, ordered by how much time or risk each change removes: run build and test as parallel jobs, cache dependencies keyed on the lockfile, shard the longest test job with a matrix, add path filters to workflows that run on changes they cannot affect (gate required checks with a job-level if: condition), add a concurrency group that cancels superseded pull request runs, set timeout-minutes on every job, set least-privilege permissions, pin third-party actions to full-length commit SHAs, and move long-lived cloud keys to OIDC with id-token: write on the deploy job alone. Keep deploy targets, required checks and environment protection rules as they are unless you explain the change. Open one small PR with before/after reasoning for each change.

The GitHub Actions CI/CD pipeline, stage by stage

Every stage has a fast default, linked to the guide that teaches it in depth. Start with the defaults, then tune whichever stage owns the most wall-clock time once the pipeline runs.

StageWhat it doesFast by default
TriggerStarts the pipeline on a push, a pull request or a manual run.Path filters skip docs-only commits to main, and a concurrency group cancels superseded pull request runs.
CheckoutFetches the commit under test.A shallow checkout of one commit, which is the actions/checkout default.
InstallRestores the project's dependencies.A dependency cache keyed on the lockfile, through the setup action's cache input.
BuildCompiles and packages the app.Build once and upload the output as an artifact, and the deploy job ships those exact files (avoid duplicate compilation).
TestRuns the suite against the commit.Runs beside the build job, with the suite sharded across a matrix when it is the long pole.
GuardrailsBounds every job and every action it runs.Bounded job timeouts with timeout-minutes, and third-party actions pinned to full-length commit SHAs.
ApproveHolds production until a person signs off.A production environment with required reviewers and a deployment branch rule, set up as in environments and approvals.
DeployShips the build to its target.Short-lived OIDC credentials, with id-token: write granted to the deploy job alone.

Triggers: run on the events that matter

A workflow starts on events. For CI/CD the usual set is three: push to the default branch builds, tests and deploys; pull_request builds and tests every proposed change; and workflow_dispatch adds a Run workflow button for a manual redeploy.

Two settings keep the pipeline fast from the first commit. A path filter such as paths-ignore on push skips the build and deploy for commits to main that touch only Markdown or docs, and when a trigger sets both branch and path filters, GitHub runs the workflow only when both match. A concurrency group keyed on the branch, with cancel-in-progress set for pull requests, cancels the run a newer push supersedes, so runners spend their minutes on the latest commit.

The pull_request trigger runs on every change, so build and test report on every pull request and work as required status checks. To skip expensive jobs on docs-only pull requests too, the path filter guide gates them with a job-level if: that keeps required checks reporting.

Build and test as parallel jobs

Jobs in a workflow run in parallel by default, so a build job and a test job finish in the time of the longer one. Declare job dependencies with needs only where they are real: the deploy job needs both, and nothing else waits.

The build job uploads its output as an artifact, and the deploy job downloads it, so production receives the exact files built from the tested commit.

When the test job is the long pole, shard it. A matrix runs one copy of the job per shard, and each copy hands its index to the test runner's --shard flag, which Vitest, Jest and Playwright all accept.

Give every job a timeout with timeout-minutes. GitHub's default is 360 minutes; a 15 or 20 minute bound ends a hung job quickly and frees its runner for the next one.

Caching dependencies

Dependency installs are the step a cache speeds up most. The setup actions for Node.js, Python, Java, Go, Ruby and .NET carry built-in caching: cache: pnpm on actions/setup-node restores the pnpm store keyed on pnpm-lock.yaml, so the cache refreshes when dependencies change and restores on every other run.

For other paths, such as build output or a compiler cache, actions/cache takes a path and a key you build with hashFiles(). Each repository gets 10 GB of cache storage by default, and GitHub removes entries nobody has read in seven days.

Environments and approvals

An environment is where deployment rules live. Add environment: production to the deploy job, then configure the environment under the repository's settings.

Required reviewers pause the job until one of up to six listed people or teams approves it. A wait timer holds it for a set number of minutes. Deployment branch rules let main, or branches matching a pattern you choose, deploy to the environment.

Environment secrets reach only jobs that reference the environment, and only after its protection rules pass, so the production credential stays with the approved deploy job.

Secrets and OIDC credentials for deploys

Store credentials as secrets: repository secrets for the whole pipeline, environment secrets for one environment. Secrets reach the runs triggered from your own repository, and a pull request run from a fork receives the GITHUB_TOKEN with read-only permissions.

For cloud deploys, OpenID Connect replaces the stored key. Grant the deploy job id-token: write, configure your cloud account to trust GitHub's OIDC provider, and the job exchanges a token GitHub issues for each run for a short-lived cloud credential scoped to that run.

Keep the workflow's default permissions at contents: read and grant id-token: write on the deploy job alone. That permission lets a job request and use an OIDC token, and the job's repository access stays at contents: read.

Pin every third-party action to a full-length commit SHA, which GitHub's security guidance calls the only way to use an action as an immutable release, and keep the version tag as a trailing comment.

To check a repository's workflows against these settings, run the free /ci-secure skill: it reports the attack paths an outsider can walk end to end, including fork code run with privileges and cache poisoning.

Deploy steps for common targets

The deploy step changes with the target and the job around it stays the same. Each row signs in with a short-lived credential: an OIDC token for the clouds and Pages, GITHUB_TOKEN for GitHub's container registry.

TargetSign in and deploy withGitHub's walkthrough
AWSaws-actions/configure-aws-credentials with role-to-assume and aws-region, then the AWS CLI or your deploy tool.OpenID Connect in Amazon Web Services
Azureazure/login with client-id, tenant-id and subscription-id, then the Azure CLI.OpenID Connect in Azure
Google Cloudgoogle-github-actions/auth with workload_identity_provider and service_account, then gcloud.OpenID Connect in Google Cloud Platform
Container image on GitHub Packagesdocker/login-action with registry ghcr.io and GITHUB_TOKEN, on a job granted packages: write.Publishing Docker images
GitHub Pagesactions/deploy-pages on a job granted pages: write and id-token: write.Using custom workflows with GitHub Pages

The complete GitHub Actions CI/CD workflow

One file covers the whole pipeline. It builds and tests every pull request in parallel, shards the test suite four ways, and on a push to main or a manual run deploys the build to an S3 bucket through an approved production environment with OIDC credentials. Every action is pinned to a full-length commit SHA.

.github/workflows/ci-cd.yml
name: CI/CD

on:
  push:
    branches: [main]
    paths-ignore: ["**.md", "docs/**"]
  pull_request:
  workflow_dispatch:

# A new push to a pull request cancels the run it supersedes.
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
      - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: dist
          path: dist

  test:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
      - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm test --shard=${{ matrix.shard }}/${{ strategy.job-total }}

  deploy:
    if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
    needs: [build, test]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    environment: production
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
        with:
          name: dist
          path: dist
      - uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6.3.0
        with:
          role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }}
          aws-region: ${{ vars.AWS_REGION }}
      - run: aws s3 sync dist "s3://${{ vars.DEPLOY_BUCKET }}" --delete
  • Create a production environment with required reviewers, and set AWS_DEPLOY_ROLE_ARN, AWS_REGION and DEPLOY_BUCKET as its variables.
  • Set packageManager in package.json; pnpm/action-setup reads the pnpm version from it.
  • Vitest and Jest both accept --shard=<index>/<count>; size the shard list to your suite. For one merged Vitest report, add --reporter=blob to each shard and merge with vitest --merge-reports, as the shard tests guide shows.
  • Deploying somewhere else? Swap the last two steps for a row from the deploy table above.

Make the pipeline fast

Once the pipeline runs, speed comes from two places: less work per run, and faster hardware for the work that remains.

Less work: profile a run, find the stage that owns the most wall-clock time, and apply its fix from the GitHub Actions CI best-practices catalog. Build only affected projects in a monorepo, cut queue time when jobs wait to start, and right-size runners job by job. The fast GitHub Actions guide walks through the whole order of operations.

Faster hardware: StarSling runners are drop-in Ubuntu runners for GitHub Actions. Change runs-on: ubuntu-latest to a StarSling runner label such as starsling-ubuntu-24.04 on the build and test jobs, and the same workflow runs on faster machines with its triggers, environments and secrets as they are.

StarSling agents keep the pipeline improving after the swap. On paid plans they analyze your workflows, run logs and machine telemetry, then open pull requests with the fixes this guide teaches: caching, test sharding, parallel jobs and workflow structure. You review and merge each one.

Sources: GitHub's documentation

Every GitHub Actions behavior on this page is from GitHub's own documentation:

FAQ

What is GitHub Actions CI/CD?

GitHub Actions CI/CD is a pipeline defined in a YAML workflow file in your repository's .github/workflows directory. Events such as a push or a pull request trigger it, jobs build and test the commit on runners, and a deploy job ships the result to an environment once the build and tests pass.

Can one GitHub Actions workflow handle both CI and CD?

Yes. Build and test jobs run on every push and pull request, and a deploy job that needs both runs only on pushes to the default branch, through a protected environment. The complete workflow on this page has that shape.

How do I deploy from GitHub Actions with OIDC?

Grant the deploy job the id-token: write permission, configure your cloud provider to trust GitHub's OIDC provider, and sign in with the provider's action: aws-actions/configure-aws-credentials, azure/login or google-github-actions/auth. The job exchanges the token GitHub issues for each run for a short-lived cloud credential.

How do I require approval before a production deploy?

Reference an environment in the deploy job with environment: production, and add required reviewers to that environment in the repository settings. The job pauses until one reviewer approves it, and the environment's secrets reach the job only after approval.

How do I make a GitHub Actions CI/CD pipeline faster?

Run build and test as parallel jobs, cache dependencies keyed on the lockfile, shard long test suites with a matrix, skip docs-only changes with path filters, and cancel superseded pull request runs with a concurrency group. Then move CPU-bound jobs to faster runners: StarSling runners take a one-line runs-on change.

Should I pin GitHub Actions to a commit SHA?

Yes, for third-party actions. GitHub's security guidance calls a full-length commit SHA the only way to use an action as an immutable release. Keep the version tag in a trailing comment so readers can see which release the SHA points at.

Get started

Run your CI/CD pipeline on faster runners

Keep GitHub Actions workflows. Move supported Ubuntu jobs to faster runners, then let optimization PRs improve the workflow over time.

Last reviewed October 6, 2026