GitHub Actions: CI/CD Workflows From First Run to Secure Deployments · Pipelines, Deployments and Security

Pipelines: Job Dependencies, Outputs and Artifacts

Order jobs with needs, pass values with job outputs, and move build files between jobs with artifacts.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A three-job pipeline: build a package, test it, and deploy it, where the build's version and files flow to the later jobs.

The idea

Each job runs on its own fresh machine, so jobs share data explicitly:

  • needs: makes a job wait for others. If a needed job fails, the dependent job is skipped.
  • Job outputs pass small strings: declare outputs: on the job, read ${{ needs.<job>.outputs.<name> }}.
  • Artifacts pass files: actions/upload-artifact in one job and actions/download-artifact in another. Artifacts are also kept on the run page for download.
  • if: ${{ always() }} makes a job (such as a notification) run even when its dependencies failed.

The code

Build, test and deploy with outputs and an artifact
name: Pipeline
on:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.ver.outputs.version }}
    steps:
      - uses: actions/checkout@v4
      - id: ver
        run: echo "version=2.1.${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT"
      - run: mkdir dist && tar -czf dist/app.tar.gz src
      - uses: actions/upload-artifact@v4
        with:
          name: app
          path: dist/app.tar.gz
          retention-days: 7

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app
      - run: tar -tzf app.tar.gz

  deploy:
    needs: [build, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app
      - run: echo "Deploying ${{ needs.build.outputs.version }}"

Output

actionlint: no problems found
workflow: Pipeline
triggers: push (branches: main)
jobs, in the order they run:
  build: runs on ubuntu-latest | 4 step(s)
  test: runs on ubuntu-latest | 2 step(s) | after build
  deploy: runs on ubuntu-latest | 2 step(s) | after build, test

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Expecting files to persist between jobs. They don't; upload and download an artifact.
  • Forgetting needs for the output. A job can only read outputs of jobs it lists in needs.
  • Large or sensitive artifacts. Artifacts are readable by anyone with repository access and count against storage; set retention-days.
  • Using the same artifact name from a matrix. In v4 names must be unique per run; add the matrix value to the name.

Key points

  • needs orders jobs; failed dependencies skip dependents.
  • Outputs carry small values, artifacts carry files.
  • always() runs cleanup or notification jobs after failures. Further reading: GitHub Docs.

Practice questions

How would the deploy job get the compiled package from the build job?

Answer

The build job uploads it with actions/upload-artifact and the deploy job downloads it with actions/download-artifact, because jobs run on separate machines.

What is the difference between a job output and an artifact?

Answer

A job output is a small string value passed through the needs context. An artifact is a file or folder stored by GitHub and downloaded by later jobs or by people.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?