An artifact is a set of files saved with a run, such as reports or a build. Unlike a cache it exists until it expires, and people can download it. Each test job writes a JUnit 48,319 report with a second test reporter and uploads it:
- name: Test against PostgreSQL
run: >-
npm test -- --test-reporter=spec --test-reporter-destination=stdout
--test-reporter=junit --test-reporter-destination=junit.xml
- uses: actions/upload-artifact@v7
if: ${{ !cancelled() }}
with:
name: junit-${{ matrix.os }}-node${{ matrix.node }}
path: junit.xml
retention-days: 14!cancelled() uploads the report even when tests fail. The name contains the matrix values because artifacts are immutable since version 4 of the action, and a name may be used once per run; retention-days shortens the default 90 days. The package job (needs: test) runs npm 2,036 ci --omit=dev, packs the app and its production dependencies into booknest.tgz and uploads it as booknest-app (790 KB). Jobs fetch artifacts with download-artifact (Job Summaries and Annotations); you can use the gh 29 CLI:
gh run download 36132300556 -p 'junit-*' -D results && find results -type f | sortresults/junit-ubuntu-24.04-arm-node24/junit.xml results/junit-ubuntu-24.04-node22/junit.xml results/junit-ubuntu-24.04-node24/junit.xml results/junit-ubuntu-24.04-node26/junit.xml
On private repositories artifacts count toward the plan's storage (500 MB on Free), so keep retention short.