Push To Display
← Back to blog

Build a GitHub Actions Dashboard on Any Screen — No Code Required

Build a GitHub Actions Dashboard on Any Screen

Your CI/CD pipeline already knows everything — build status, test results, deploy outcomes, release metadata. The problem isn't missing information. It's that this information is scattered across too many surfaces:

  • GitHub Actions tab (hidden behind 3 clicks)
  • Slack bot notifications (muted within a week)
  • Email digests (never opened)
  • Deploy logs in Vercel/AWS/your hosting provider
  • Monitoring dashboards you check after something breaks

Five tools. Five context switches. And still nobody notices a failed deploy until a customer reports it.

What if you consolidated all of that into a single screen — one glance, always current, zero interaction required?

This tutorial shows you how to push GitHub Actions workflow results to a physical display — phone, tablet, or wall-mounted TV — using one YAML file. Every pipeline stage lands on its own panel, updating in real time. Your team sees the full picture without opening a browser.

What you'll build

A 2×2 grid display where each panel shows a different pipeline stage, updating in real time as your workflow runs:

PanelContentVisual
Panel 1 (top-left)Build statusGreen on success, red on failure
Panel 2 (top-right)Test resultsTest count + pass/fail breakdown
Panel 3 (bottom-left)Deploy statusEnvironment + URL + timestamp
Panel 4 (bottom-right)Release infoVersion tag + author + changelog

Every push to main triggers the workflow. Each stage pushes its result to the corresponding panel. Your team sees the pipeline status at a glance — no browser tabs, no refreshing.

Prerequisites

  1. Install Push To Display on any device — iOS, Android, or open the web dashboard
  2. Set your board layout to 2×2 Grid (Settings → Layout → 2×2 Grid)
  3. Create an API key from the web portal (Settings → API Keys → Generate)
  4. Add the key as a GitHub repository secret named PUSHTODISPLAY_API_KEY

The complete workflow

Create .github/workflows/display-dashboard.yml in your repository:

name: CI/CD → Display Dashboard

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run build
        id: build
        run: |
          echo "Building project..."
          # Replace with your actual build command:
          # npm run build / dotnet build / make build
          echo "Build completed successfully"

      - name: Push build result to display
        if: always()
        uses: pushtodisplay/action@v1
        with:
          api-key: ${{ secrets.PUSHTODISPLAY_API_KEY }}
          panel-id: 1
          background: ${{ steps.build.outcome == 'success' && '#0d7a3e' || '#c0392b' }}
          blocks: |
            [
              {
                "text": "${{ steps.build.outcome == 'success' && 'Build Passed ✓' || 'Build Failed ✗' }}",
                "size": "large",
                "weight": "bold",
                "color": "#ffffff"
              },
              {
                "text": "${{ github.event.head_commit.message }}",
                "size": "small",
                "color": "#ffffffcc"
              },
              {
                "text": "${{ github.sha }}  •  ${{ github.actor }}",
                "size": "small",
                "color": "#ffffff99"
              }
            ]

  test:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        id: tests
        run: |
          echo "Running tests..."
          # Replace with your actual test command:
          # npm test / dotnet test / pytest
          echo "tests_passed=42" >> "$GITHUB_OUTPUT"
          echo "tests_failed=0" >> "$GITHUB_OUTPUT"
          echo "tests_total=42" >> "$GITHUB_OUTPUT"

      - name: Push test results to display
        if: always()
        uses: pushtodisplay/action@v1
        with:
          api-key: ${{ secrets.PUSHTODISPLAY_API_KEY }}
          panel-id: 2
          background: ${{ steps.tests.outcome == 'success' && '#0d7a3e' || '#c0392b' }}
          blocks: |
            [
              {
                "text": "${{ steps.tests.outcome == 'success' && 'Tests Passed' || 'Tests Failed' }}",
                "size": "large",
                "weight": "bold",
                "color": "#ffffff"
              },
              {
                "text": "${{ steps.tests.outputs.tests_passed }}/${{ steps.tests.outputs.tests_total }} passed",
                "size": "medium",
                "color": "#ffffffcc"
              },
              {
                "text": "${{ steps.tests.outputs.tests_failed }} failed",
                "size": "small",
                "color": "${{ steps.tests.outputs.tests_failed == '0' && '#ffffff99' || '#ff6b6b' }}"
              }
            ]

  deploy:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to production
        id: deploy
        run: |
          echo "Deploying..."
          # Replace with your actual deploy command:
          # vercel --prod / kubectl apply / aws deploy
          echo "url=https://myapp.example.com" >> "$GITHUB_OUTPUT"
          echo "environment=production" >> "$GITHUB_OUTPUT"

      - name: Push deploy status to display
        if: always()
        uses: pushtodisplay/action@v1
        with:
          api-key: ${{ secrets.PUSHTODISPLAY_API_KEY }}
          panel-id: 3
          full-panel: ${{ steps.deploy.outcome == 'failure' && 'true' || 'false' }}
          background: ${{ steps.deploy.outcome == 'success' && '#1a5276' || '#c0392b' }}
          blocks: |
            [
              {
                "text": "${{ steps.deploy.outcome == 'success' && 'Deployed ✓' || 'Deploy Failed ✗' }}",
                "size": "large",
                "weight": "bold",
                "color": "#ffffff"
              },
              {
                "text": "${{ steps.deploy.outputs.environment }}",
                "size": "medium",
                "weight": "semibold",
                "color": "#ffffffcc"
              },
              {
                "text": "${{ steps.deploy.outputs.url }}",
                "size": "small",
                "color": "#ffffff99"
              }
            ]

  announce:
    runs-on: ubuntu-latest
    needs: deploy
    steps:
      - name: Push release summary to display
        uses: pushtodisplay/action@v1
        with:
          api-key: ${{ secrets.PUSHTODISPLAY_API_KEY }}
          panel-id: 4
          background: "#2c2c54"
          align-y: center
          blocks: |
            [
              {
                "text": "🚀 Shipped",
                "size": "large",
                "weight": "bold",
                "color": "#ffffff"
              },
              {
                "text": "${{ github.event.head_commit.message }}",
                "size": "medium",
                "color": "#ffffffcc"
              },
              {
                "text": "by @${{ github.actor }}  •  ${{ github.event.head_commit.timestamp }}",
                "size": "small",
                "color": "#ffffff99"
              }
            ]

How it works

The architecture is simple: no polling, no dashboard to refresh.

git push → GitHub Actions runs workflow
  → Each job calls pushtodisplay/action@v1
    → Action POSTs to PushToDisplay API
      → API routes content via SignalR to your device
        → Panel updates instantly on your screen

The panel-id parameter targets a specific quadrant of the 2×2 grid. Each job updates only its panel — the other three stay untouched. This gives you a persistent, always-current view of your entire pipeline.

Why consolidate workflow status onto one screen?

Most teams piece together CI/CD visibility from multiple disconnected tools — GitHub's workflow UI, Slack integrations, status badges in READMEs, and custom dashboards that need maintenance. The result: nobody has a single source of truth for "is our pipeline healthy right now?"

Consolidating onto one physical display solves this differently than another web dashboard:

  • Ambient awareness — The screen is always on, always visible. No tab to open, no notification to dismiss.
  • Multi-source, single surface — Build results, test metrics, deploy status, and release metadata live side by side instead of across 4 browser tabs.
  • Passive consumption — Your team absorbs pipeline health peripherally, the same way you'd glance at a wall clock.
  • No maintenance — Unlike Grafana dashboards or custom status pages, there's no infrastructure to host or queries to maintain. The workflow file IS the dashboard config.

Key patterns in this workflow

Color-coded status

The background field uses conditional expressions to show green for success and red for failure:

background: ${{ steps.build.outcome == 'success' && '#0d7a3e' || '#c0392b' }}

Your team reads the board at a glance — green means go, red means investigate.

Full-panel mode for critical failures

When a deploy fails, it needs attention. Setting full-panel: true makes the failure message take over the entire panel space with larger, more prominent text:

full-panel: ${{ steps.deploy.outcome == 'failure' && 'true' || 'false' }}

Dynamic content from step outputs

Pull real data from your pipeline — test counts, deploy URLs, commit messages — directly into the display:

"text": "${{ steps.tests.outputs.tests_passed }}/${{ steps.tests.outputs.tests_total }} passed"

Rich multi-block content

Each panel shows multiple lines with different sizes and emphasis. The blocks array lets you compose a visual hierarchy — bold header, medium-weight details, and muted metadata.

Consolidate multiple workflows onto one board

The example above covers a single workflow, but the real power is consolidation across multiple sources. Each panel is independently addressable — different workflows, different repos, even different CI providers can all push to the same board.

Multi-repo setup: Have your frontend, backend, and infrastructure repos each push to a different panel on the same board. One screen shows the health of your entire stack.

Aggregate CI and CD: Trigger on workflow_run to collect results from separate build and deploy workflows into one board — even if they run on different schedules.

Mix sources: Use the REST API alongside the GitHub Action. Push Vercel deploy previews from a webhook, Datadog alerts from a serverless function, and GitHub Actions results from your workflow — all converging on the same 4-panel grid.

More customization ideas

Single-screen focus: Switch to Full Screen layout and target Panel 1 only. Every push replaces the previous message — great for a single big status display.

Environment color coding: Use different background colors per environment:

  • #1a5276 — production (blue)
  • #1e8449 — staging (green)
  • #7d3c98 — preview (purple)

Compact density: Add density: compact for boards that accumulate multiple messages in list-flow mode — useful for a running log of deploys throughout the day.

Get started

  1. Install the app on any spare device (iOS · Android)
  2. Set layout to 2×2 Grid
  3. Create an API key from the dashboard
  4. Add the secret to your GitHub repo
  5. Copy the workflow above into .github/workflows/display-dashboard.yml
  6. Push to main and watch the panels light up

Stop checking 5 tools to answer "is our pipeline healthy?" — consolidate it into one screen your whole team can see.


Want more? Check out the GitHub Action docs, the CLI quick start, or the MCP Server for AI agent integration.