Back to Blog
Lesson 25 of the CI/CD: Continuous Integration from Scratch course
DevOpsJuly 31, 20264 min read

Artifact Management: Persisting Build Outputs in GitHub Actions

Master artifact management to save build outputs and test reports in GitHub Actions. Learn how to upload and download files for inspection after your CI runs.

devopsgithub-actionsci-cdautomationbuild-engineering
Close-up of colorful programming code on a computer screen, showcasing digital technology.

Previously in this course, we explored advanced pipeline caching to speed up our builds by reusing dependencies. Today, we shift our focus from performance to persistence: how do we save the actual results—our binaries, logs, or test reports—so they don't vanish the moment our CI job finishes?

Understanding Artifacts and Storage

In GitHub Actions, each job runs in a fresh, ephemeral virtual machine. When the job finishes, the runner is destroyed, and the file system is wiped clean. If you generate a compiled binary, a PDF report, or a set of logs during your build, you lose them instantly.

Artifacts are the mechanism GitHub provides to "extract" files from that temporary environment and store them in the cloud, attached to your specific workflow run. Think of them as a temporary storage bridge for your build outputs that allows you to inspect, debug, or deploy your code later.

Uploading Build Artifacts

To persist files, we use the official actions/upload-artifact action. This action looks at a path in your runner's workspace, zips the contents, and uploads them to the GitHub storage service.

Here is a concrete example. Suppose your application generates a dist/ folder containing your compiled code and a test-results/ folder containing an HTML coverage report.

YAML
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Build and Test
        run: |
          mkdir -p dist test-results
          echo "Binary content" > dist/app.bin
          echo "Test report content" > test-results/report.html

      - name: Upload Build Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-outputs
          path: |
            dist/
            test-results/
          retention-days: 5

Key configuration points:

  • name: The identifier for your artifact in the UI.
  • path: Supports wildcards or multiple paths (as shown above).
  • retention-days: Defaults to 90 days, but you can lower this to save space if you only need the data for immediate debugging.

Downloading Artifacts

Once uploaded, you can access these files in two ways:

  1. Manual Download: Navigate to your repository on GitHub, click the Actions tab, select the specific workflow run, and scroll down to the "Artifacts" section. Clicking the artifact name will trigger a browser download of the zip file.
  2. Programmatic Download: If you have a subsequent job in the same workflow that needs these files, use the actions/download-artifact action. This is the foundation of multi-stage pipelines where one job builds the binary and another deploys it.

Hands-on Exercise: Saving Your Test Logs

Let’s advance our project by ensuring we never lose a test report.

  1. Open your current workflow file (.github/workflows/main.yml).
  2. Add a step after your test execution step that uses actions/upload-artifact.
  3. Set the path to the directory where your tests output their logs or reports.
  4. Commit and push your changes.
  5. Trigger the workflow, then head to the GitHub UI to verify that the artifact appears at the bottom of the summary page.

Common Pitfalls

  • Case Sensitivity: Ensure your path matches the actual case of your files and folders on Linux runners.
  • Large Artifacts: If you upload gigabytes of data, your pipeline will slow down significantly due to network transfer times. Use artifacts for essential build outputs, not for caching dependencies (use the cache action for that instead).
  • Path Nesting: When you download an artifact, it extracts into the current working directory. If you upload a directory, it will recreate that directory structure upon download. Always test your download step to ensure files land where your scripts expect them to be.

FAQ

Q: Are artifacts private? A: Yes, artifacts are subject to the same repository access permissions as your code.

Q: Can I use artifacts to pass data between different workflows? A: No. Artifacts are scoped to a single workflow run. To pass data between different runs or repositories, you would typically use an external storage provider or a container registry.

Q: Why is my artifact missing from the UI? A: Check if the upload-artifact step actually ran. If a previous step failed and you didn't include if: always() or handle the failure, the upload step might have been skipped.

Recap

We’ve learned that while runners are ephemeral, our data doesn't have to be. By leveraging the actions/upload-artifact and actions/download-artifact actions, we ensure that our CI process produces durable, inspectable build outputs that act as a source of truth for our build status.

Up next: We will learn how to coordinate these artifacts across multiple jobs using the needs keyword in our multi-job workflows.

Similar Posts