From GitHub Actions to Authenticated URL: A Better Way to Share CI Artifacts

GitHub Actions stores build artifacts in a zip archive. To access them:

TL;DR
Replace `actions/upload-artifact` (requires GitHub login, expires in 90 days) with `dsp publish` (permanent URL, company SSO, PM-accessible). One step change to your GitHub Actions workflow.

The problem with GitHub Actions artifact storage

GitHub Actions stores build artifacts in a zip archive. To access them:

  1. Go to the Actions run in GitHub
  2. Find the artifact section
  3. Click Download
  4. Extract the zip
  5. Open the HTML file locally

This works for engineers. It's unusable for everyone else.

Your PM can't access private repository Actions without a GitHub account. Your QA manager shouldn't need to navigate GitHub's CI interface to read a test report. Your VP shouldn't be extracting zip files to see a build summary.

And after 90 days, the artifact is gone. Any link shared in a Slack thread, Notion document, or Jira ticket goes dead.


The one-line change

Before:

- uses: actions/upload-artifact@v4
  with:
    name: test-report
    path: ./playwright-report/

After:

- name: Publish report
  run: |
    npm install -g @display-dev/cli
    dsp publish ./playwright-report/ --name "test-run-${{ github.run_id }}"
  env:
    DISPLAYDEV_API_KEY: ${{ secrets.DISPLAYDEV_API_KEY }}

The URL is permanent. It requires a company email to access. It renders in the browser without downloading.


Full workflow example

name: Test and Publish
 
on: [push, pull_request]
 
jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Lint
        run: npm run lint
      
      - name: Run tests
        run: npm test -- --coverage
        continue-on-error: true
      
      - name: Build
        run: npm run build
      
      - name: Publish test coverage report
        if: always()
        run: |
          npm install -g @display-dev/cli
          COVERAGE_URL=$(dsp publish ./coverage/lcov-report/ \
            --name "coverage-${{ github.sha }}" \
            --title "Coverage: ${{ github.ref_name }}")
          echo "COVERAGE_URL=$COVERAGE_URL" >> $GITHUB_ENV
        env:
          DISPLAYDEV_API_KEY: ${{ secrets.DISPLAYDEV_API_KEY }}
      
      - name: Comment on PR
        if: always() && github.event_name == 'pull_request'
        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: `📊 **Coverage report:** ${process.env.COVERAGE_URL}`
            })

Use cases this covers

Test reports: Playwright, Jest HTML, Allure, Cypress — any tool that outputs a static HTML report directory.

Build outputs: Preview builds for non-engineers. QA can review what's actually shipping without needing a local development environment.

Coverage reports: Istanbul/c8 HTML reports, accessible to engineers reviewing coverage without running tests locally.

Scheduled report generation: Nightly builds, weekly analytics exports, automated competitive monitoring reports — anything a scheduled workflow generates and stakeholders need to read.


Viewer experience

The PR comment appears with a URL. The QA lead clicks it. Google login — one click. They see the full interactive test report in their browser. No GitHub account. No zip file. No 90-day expiry.

The URL persists in Slack threads, Notion documents, and Jira tickets indefinitely.


Cost comparison

GitHub Actions artifact storageDisplay
Viewer needs GitHub account
Viewer needs private repo access
Artifact expiry90 daysNever
Browser-renderable (no download)
Company SSO
Monthly cost$0 (included)$49 flat

FAQ

Can I publish only on failure?+

Yes. Use if: failure() in the step condition. The artifact is only published when the workflow fails.

How long are Display artifacts retained?+

Indefinitely, until you delete them. No expiry.

What about other CI systems — GitLab, CircleCI, Jenkins?+

The display CLI works anywhere Node.js runs. For GitLab CI: yaml publish: stage: report script: - npm install -g @display-dev/cli - dsp publish ./report/ --name "ci-$CI_PIPELINE_ID" variables: DISPLAYDEV_API_KEY: $DISPLAYDEV_API_KEY Same pattern for CircleCI and Jenkins — install the CLI, call dsp publish.

Publish your first artifact in 15 seconds.

Free tier. No credit card. One-time password auth for viewers on free, Google + Microsoft SSO on Teams ($49/month flat).

Get started free →See pricing