Skip to content

Latest commit

 

History

History
384 lines (268 loc) · 17.9 KB

File metadata and controls

384 lines (268 loc) · 17.9 KB

Deployment of Copilot Metrics Viewer

There are a few ways to deploy the Copilot Metrics Viewer, depending on the type of metrics (Organization/Enterprise) and the level of control required.

The app runs in a Docker container, so it can be deployed anywhere containers are hosted (AWS, GCP, Azure, Kubernetes, etc.).

Architecture

The application supports two operating modes:

Direct API Mode (no database)

The simplest setup — the web app fetches metrics directly from GitHub's Copilot Usage Metrics API on each page load, returning the latest 28-day rolling window.

Component Description Required
Web App Nuxt 3 dashboard (port 3000/80) Yes

Historical Mode (with database)

Adds a PostgreSQL database and sync service for persistent storage. This enables metrics beyond the 28-day API window, per-user time-series history, and full historical team views.

Component Description Required
Web App Nuxt 3 dashboard (port 3000/80) Yes
PostgreSQL Database for historical metrics storage Yes
Sync Service Scheduled job that downloads metrics from GitHub API to PostgreSQL Yes

The sync service runs daily (2 AM UTC by default) and downloads the latest Copilot usage metrics from GitHub's API. The web app reads from the database for fast, reliable dashboard rendering.

How Team Metrics Work

GitHub's Copilot Usage Metrics API does not provide team-level endpoints. Instead, this application derives team metrics by:

  1. Downloading per-user daily metrics from the organization/enterprise endpoint
  2. Resolving team membership via the GitHub Teams API
  3. Filtering and aggregating per-user data in-memory for each team

This approach works in Historical mode only:

  • Direct API mode: Team-scoped views are not available (no per-user data stored to filter)
  • Historical mode: Team data covers the full stored history, enabling long-term team trend analysis

Deployment options

Review available Nuxt Deployment Options.

Warning

Copilot Metrics Viewer requires a backend, hence it cannot be deployed as a purely static web app.

Authentication with GitHub

The Metrics Viewer can be integrated with GitHub application authentication, which authenticates the user and verifies their permissions to view the metrics. This option is recommended since it doesn't use Personal Access Tokens. The downside of using a GitHub application is that it can only authorize users to view metrics at the organization level (no support for Enterprise).

For Enterprise level authentication review Github OAuth Apps.

With a Personal Access Token, user credentials are not verified, and the application simply renders Copilot metrics fetched using the PAT stored in the backend.

Authentication for Copilot Metrics Viewer

By default Azure Deployments deploy a web app available on the public Internet without authentication (unless GitHub app is used).

Application can be easily secured in azure using built-in features like Authentication settings on ACA/AppService (EasyAuth on Azure). Azure Container Apps and App Services allow for adding IP restrictions on ingress. Both can also be deployed using private networking architectures.

Options below provide most basic and cost effective ways of hosting copilot-metrics-viewer.

Scenario 1: One-click Azure Deployment

The simplest way to deploy is to use the "one-click" option that creates resources in Azure. The deployment includes:

  • Azure Container App with a consumption environment
  • Azure Container App Job (sync service, daily schedule)
  • Azure Database for PostgreSQL Flexible Server (Burstable B1ms)
  • Azure Log Analytics Workspace

Azure ARM Deployment

Application will use a pre-built docker image hosted in GitHub registry: ghcr.io/github-copilot-resources/copilot-metrics-viewer.

Prerequisites: Contributor permission to a resource group in Azure and a subscription with the Microsoft.App and Microsoft.DBforPostgreSQL resource providers enabled.

Important

Estimated cost for running this in Azure is about $15/month (Container Apps ~$1 + PostgreSQL Burstable B1ms ~$12 + Log Analytics ~$2).

  1. Option 1 - Using a Personal Access Token in the Backend:

    Deploy to Azure

  2. Option 2 - Using GitHub App Registration and GitHub Authentication:

    When using this method, register your app in Github first.

    Deploy to Azure

Important

Important: After deploying Option 2, the redirect URI needs to be updated with the URL of the deployed container app.

Go to: https://github.com/organizations/<your-org>/settings/apps/<your-app> or in the UI to the settings of the registered application and add the following redirect URL: https://<your-container-app-name-and-region>.azurecontainerapps.io/auth/github

Deployment with private networking

Caution

When deploying to a private network, specify a subnet (at least /23) for the Azure Container Apps Environment. App deployment does not create any DNS entries for the application, in order to create a private DNS Zone linked to provided Virtual Network, follow up the deployment with DNS deployment targeting same resource group:

DNS Zone deploy to Azure

Scenario 2: Azure Deployment with azd

If more control over the deployed container image is needed, an infrastructure-as-code option has been provided using Azure Bicep. The application can be deployed using the Azure Developer CLI (azd).

In this scenario, the container is built from the source code locally, which provides additional opportunities to modify, scan, etc.

Prerequisites:

  • Contributor permission to a subscription in Azure with the Microsoft.App and Microsoft.DBforPostgreSQL resource providers enabled.
  • Permissions for creating role assignments.
  • Azure CLI (az), Azure Developer CLI (azd) and Docker installed locally.

Important

Estimated cost for running this in Azure is about $25/month (Container Apps ~$1 + Container Registry ~$5 + PostgreSQL ~$12 + monitoring ~$5).

The deployment creates:

  • Azure Resource Group
  • Azure Container App with a consumption environment
  • Azure Container App Job (sync service)
  • Azure Container Registry
  • Azure Database for PostgreSQL Flexible Server
  • Azure Log Analytics Workspace
  • Azure Application Insights
  • Azure Key Vault

AZD Deployment

Run azd up and follow the prompts. You will be asked for:

  • GitHub PAT token (or OAuth client credentials)
  • GitHub scope (organization/enterprise)
  • Organization/enterprise name
  • PostgreSQL admin password

Scenario 3: Docker Compose

The recommended way to run the application locally or in any Docker-capable environment. Docker Compose manages the web app, PostgreSQL database, and sync service together.

Quick Start with Mock Data

No GitHub token needed — great for trying out the dashboard:

docker compose up web
# Open http://localhost:3000/orgs/your-org?mock=true

Running with Real GitHub Data (Direct API — no database)

The simplest setup — metrics come directly from the GitHub API (28-day rolling window):

export NUXT_GITHUB_TOKEN=github_pat_...    # Fine-grained PAT with "Copilot metrics" permission
export NUXT_PUBLIC_GITHUB_ORG=your-org
export NUXT_PUBLIC_IS_DATA_MOCKED=false

docker compose up web
# Open http://localhost:3000/orgs/your-org

Note

Team-scoped views (e.g., /orgs/your-org/teams/your-team) require Historical mode with PostgreSQL. Without the database, team metrics cannot be computed because team data is derived by filtering per-user records stored in the database. The Teams Comparison tab is available to browse teams, but individual team drill-down requires Historical mode.

Running with Historical Mode (database + sync)

Adds PostgreSQL for persistent storage — enables metrics beyond 28 days, per-user time-series history, and full historical team views:

export NUXT_GITHUB_TOKEN=github_pat_...    # Fine-grained PAT with "Copilot metrics" permission
export NUXT_PUBLIC_GITHUB_ORG=your-org
export NUXT_PUBLIC_IS_DATA_MOCKED=false
export ENABLE_HISTORICAL_MODE=true

# Start web app + database
docker compose up web db

# In a separate terminal, run initial sync to populate the database
docker compose run --rm sync

# Open http://localhost:3000/orgs/your-org

The sync service downloads all available historical data on first run. Subsequent runs (or the daily schedule) only sync the latest day.

Enterprise Scope

export NUXT_GITHUB_TOKEN=github_pat_...
export NUXT_PUBLIC_SCOPE=enterprise
export NUXT_PUBLIC_GITHUB_ENT=your-enterprise
export NUXT_PUBLIC_IS_DATA_MOCKED=false
export ENABLE_HISTORICAL_MODE=true

docker compose up web db
docker compose run --rm sync

Services Overview

Service Purpose Profile
db PostgreSQL 15 for metrics storage default
web Main Nuxt 3 dashboard (port 3000) default
sync Standalone sync service (API → PostgreSQL) default
playwright E2E tests with mock data (3 browsers) test
sync-seed Seeds DB with mock data for storage pipeline test
playwright-storage E2E tests reading from DB, no token needed test

Running E2E Tests

Mock data tests (all tests across 3 browsers):

docker compose run --rm playwright
# Results saved to ./test-results/

Storage pipeline tests (full sync → DB → dashboard):

# Phase 1: Seed the database with mock data
docker compose run --rm sync-seed

# Phase 2: Verify dashboard reads from DB (no GitHub token)
docker compose run --rm playwright-storage

Stopping and Cleaning Up

docker compose down              # Stop all services
docker compose down -v           # Stop and remove volumes (delete all data)

Scenario 4: Kubernetes

Kubernetes manifests are provided in the k8s/ directory:

  • k8s/deployment.yaml — Web app Deployment + Service with health probes
  • k8s/cronjob.yaml — Sync service CronJob (daily at 2 AM)

Prerequisites

  • A PostgreSQL database (managed service recommended: AWS RDS, Azure Database for PostgreSQL, Google Cloud SQL)
  • Container images from GHCR:
    • ghcr.io/github-copilot-resources/copilot-metrics-viewer:latest
    • ghcr.io/github-copilot-resources/copilot-metrics-viewer-sync:latest

Setup

  1. Create the secrets:
kubectl create secret generic copilot-metrics-secrets \
  --from-literal=github-token="ghp_your_token_here" \
  --from-literal=session-password="at_least_32_characters_long_random_string" \
  --from-literal=database-url="postgresql://user:pass@your-db-host:5432/copilot_metrics"
  1. Edit the manifests to set your organization/enterprise name.

  2. Apply:

kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/cronjob.yaml

Health Check Configuration

The web app provides dedicated health check endpoints:

  • /api/live — Liveness probe (application is alive and responsive)
  • /api/ready — Readiness probe (application ready to serve traffic)
  • /api/health — General health status

These endpoints respond in ~200ms without making external API calls and do not require authentication.

Note

Using these dedicated health endpoints instead of the root / path avoids triggering GitHub API calls during health checks.

Admin Sync API

When running in Historical mode, the web app exposes a manual sync endpoint for backfilling or repairing data. If the app is configured with NUXT_GITHUB_TOKEN, the Authorization header is optional (the server uses its own token).

Note: The GitHub Copilot Metrics API provides historical data well beyond the 28-day rolling window. The 1-day endpoint supports dates going back many months, so sync-date, sync-range, and sync-gaps can all backfill historical data. The 28-day limit only applies to sync-last-28 (which uses the bulk download endpoint).

POST /api/admin/sync

Common parameters (body JSON or query string — Content-Type: application/json is optional):

Parameter Description
scope organization or enterprise
githubOrg Organization slug (org scope)
githubEnt Enterprise slug (enterprise scope)
action One of the actions below (default: sync-date)

Actions

sync-date — Download and store metrics for a single day (supports any historical date).

curl -X POST http://localhost:3000/api/admin/sync \
  -H "Content-Type: application/json" \
  -d '{"action":"sync-date","scope":"organization","githubOrg":"your-org","date":"2026-01-15"}'
# → {"action":"sync-date","result":{"success":true,"date":"2026-01-15","metricsCount":1}}

sync-last-28 — Download the latest 28-day report and store any new days. Most efficient for keeping the database current (1 API call for 28 days).

curl -X POST http://localhost:3000/api/admin/sync \
  -H "Content-Type: application/json" \
  -d '{"action":"sync-last-28","scope":"organization","githubOrg":"your-org"}'
# → {"action":"sync-last-28","success":true,"totalDays":28,"savedDays":27,"skippedDays":1,"errors":[]}

sync-range — Download and store all days in a date range (one API call per day). Use for initial historical backfill.

curl -X POST http://localhost:3000/api/admin/sync \
  -H "Content-Type: application/json" \
  -d '{"action":"sync-range","scope":"organization","githubOrg":"your-org","since":"2026-01-01","until":"2026-03-31"}'

sync-gaps — Like sync-range but skips dates already present in the database. Uses bulk download for recent gaps and the 1-day endpoint for older gaps.

curl -X POST http://localhost:3000/api/admin/sync \
  -H "Content-Type: application/json" \
  -d '{"action":"sync-gaps","scope":"organization","githubOrg":"your-org","since":"2026-01-01","until":"2026-04-20"}'
# → {"action":"sync-gaps","gapsDetected":82,"gapsFilled":80,"outsideWindow":0,"failureCount":2,"results":[...]}

Environment Variables Reference

Variable Description Required
NUXT_GITHUB_TOKEN GitHub PAT with Copilot metrics permission Yes (unless OAuth)
NUXT_PUBLIC_SCOPE organization or enterprise (legacy team-organization/team-enterprise have been removed; existing values are auto-normalized) Yes
NUXT_PUBLIC_GITHUB_ORG GitHub organization slug For org scope
NUXT_PUBLIC_GITHUB_ENT GitHub enterprise slug For enterprise scope
NUXT_SESSION_PASSWORD Session encryption key (min 32 chars) Yes
DATABASE_URL PostgreSQL connection string Historical mode only
ENABLE_HISTORICAL_MODE true to read metrics from database Historical mode only
SYNC_ENABLED true for sync service, false for web app Historical mode only
SYNC_DAYS_BACK Days to sync (default: 1 for daily, 28 for bulk) Sync only
NUXT_PUBLIC_USING_GITHUB_AUTH true to enable GitHub OAuth Optional
NUXT_OAUTH_GITHUB_CLIENT_ID GitHub App client ID For OAuth
NUXT_OAUTH_GITHUB_CLIENT_SECRET GitHub App client secret For OAuth

Github App Registration

While it is possible to run the API Proxy without GitHub app registration and with a hardcoded token, it is not the recommended way.

To register a new GitHub App, follow these steps:

Tip

Navigate using link: replace <your_org> with your organization name and open this link: https://github.com/organizations/<your_org>/settings/apps

or navigate using UI:

  1. Go to your organization's settings.

  2. Navigate to "Developer settings".

  3. Select "GitHub Apps".

  4. Click "New GitHub App".

  5. Set a unique name.

  6. Provide a home page URL: your company URL or just http://localhost.

  7. Add a callback URL for http://localhost:3000/auth/github. (We'll add the real redirect URL after the application is deployed.)

  8. Uncheck the "Webhook -> Active" checkbox.

  9. Set the permissions:

    • Select Organization permissions.
    • Under Members, select Access: Read-only.
    • Under Copilot Metrics, select Access: Read-only.
    • Under Copilot Seat Management, select Access: Read-only.
  10. Click on 'Create GitHub App' and, in the following page, click on 'Generate a new client secret'.

  11. Note the Client ID and Client Secret (copy it to a secure location). This is required for the application to authenticate with GitHub.

  12. Install the app in the organization:

    • Go to "Install App".
    • Select your organization.