github-actions-dashboard

command module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

README

GitHub Actions Dashboard

Monitor GitHub Actions across repositories installed in a GitHub App. The shared dashboard provides repository statistics, workflow history, intermittently failing checks and a workflow-by-commit matrix with branch, workflow and event filters.

Dashboard overview Repository workflow matrix

Local demo

Requires Go 1.27.2 and Docker Compose. Frontend assets are checked in, so running the app does not require Node or a CDN connection.

docker compose up -d --wait postgres
go run . -config config/demo.yml

Open http://127.0.0.1:8080. The demo seeds synthetic data into PostgreSQL and disables OAuth. It refuses non-loopback binds and a configured public URL. Use it only locally. Seeding is idempotent; restarting updates the synthetic timestamps without duplicating rows.

Stop the app with Ctrl+C and PostgreSQL with docker compose down. The database volume is retained. PostgreSQL is published only on 127.0.0.1:5432; newly initialized volumes require the configured local-dev-password for TCP access. Existing volumes retain their pg_hba.conf authentication rules. If an older volume used trust, change its TCP host rules to scram-sha-256 and reload PostgreSQL; removing the Compose setting does not update existing database files.

scripts/dev-setup.sh demo starts the database and builds a binary, then prints the command to run it. scripts/dev-setup.sh oauth selects config/local.dev.yml instead.

Real GitHub data

Configure a GitHub App with Actions, Checks and Metadata read permissions and subscribe to workflow_run, check_run, installation and installation_repositories. Set the webhook URL to /api/github/hook and the OAuth callback to /login/callback on the externally reachable dashboard URL.

export DASHBOARD_GITHUB_APP_INTEGRATION_ID=123456
export DASHBOARD_GITHUB_APP_PRIVATE_KEY="$(cat private-key.pem)"
export DASHBOARD_GITHUB_APP_WEBHOOK_SECRET=your-webhook-secret
export DASHBOARD_GITHUB_OAUTH_CLIENT_ID=your-client-id
export DASHBOARD_GITHUB_OAUTH_CLIENT_SECRET=your-client-secret
export DASHBOARD_ALLOWED_USERS=your-github-login
# Alternatively/additionally: DASHBOARD_ALLOWED_ORGANIZATIONS=your-org
export DASHBOARD_PUBLIC_URL=https://your-dashboard.example

go run . -config config/local.dev.yml

Private-key configuration is PEM contents, not a filename. An explicit user or organization allowlist is required outside demo mode. Allowed users can see every tracked repository, aggregate and realtime message; there is no per-user repository isolation. Organization membership is verified at login and remains valid for the session lifetime. See authentication for permissions, cookies, access checks and session-store limits.

History is ingested from future webhooks; there is no initial workflow-history backfill. Only completed workflow and check events are persisted. Repository removal and app uninstall delete tracked data, including dependent runs/checks. Installation tombstones prevent delayed completed events from recreating removed data. Existing rows gain installation ownership on subsequent webhooks; uninstall cannot identify historical rows that still lack an installation ID.

Configuration and deployment

Use config/dashboard.example.yml as a reference, config/local.dev.yml for local OAuth testing or config/production.yml for deployment. PostgreSQL is the only implemented database driver. The schema is initialized automatically, including additive installation-tracking migrations. Back up existing databases before upgrading a deployed instance.

Large existing databases should create the two covering indexes added for the repository and flaky-check queries before upgrading, otherwise startup builds them without CONCURRENTLY and blocks webhook writes:

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_workflow_runs_repo_stats ON workflow_runs(repository_id) INCLUDE (id, status, conclusion);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_check_runs_flaky ON check_runs(completed_at) INCLUDE (repository_id, name, conclusion) WHERE conclusion IN ('success', 'failure');

Relevant environment values:

Variable Purpose
DASHBOARD_PUBLIC_URL Externally reachable URL for OAuth callbacks
DASHBOARD_ALLOWED_USERS Comma-separated usernames allowed to view the shared dashboard
DASHBOARD_ALLOWED_ORGANIZATIONS Comma-separated organizations whose active members may sign in
DASHBOARD_DATABASE_URL PostgreSQL URL or lib/pq connection string
DASHBOARD_DATABASE_CLOUD_SQL_INSTANCE Cloud SQL instance connection name
DASHBOARD_DATABASE_NAME, DASHBOARD_DATABASE_USER, DASHBOARD_DATABASE_PASSWORD Cloud SQL database credentials
DASHBOARD_ENV_PREFIX Replaces DASHBOARD_ for supported credential/database/access/public-URL variables

Repository, analytics and flaky-check results are cached in memory for cache.stats_ttl (default 1m), so dashboard data can lag webhooks by up to that long. Concurrent requests share one query, expired results are served while a refresh runs in the background, and each query is cancelled after two minutes. cache.max_entries bounds the number of cached parameter combinations; a negative stats_ttl disables caching.

A database URL takes precedence over automatic Cloud SQL connection-string construction. Cloud SQL uses /cloudsql/<instance> Unix sockets, which must be provided by the deployment environment.

options.default_time_window must be a positive Go duration, such as 24h or 168h (seven days); 7d is invalid. options.enable_realtime: false disables the WebSocket endpoint and browser connection. Database-backed routes return 503 until schema initialization succeeds; background retries share the stable pool used by webhook handlers. Shutdown cancels retries and closes realtime connections.

Use HTTPS and secure cookies in production. Sessions are held in memory, so restarts invalidate them; multiple replicas need shared session storage or routing affinity. Handled webhooks are acknowledged only after the verified payload is committed to PostgreSQL. Delivery IDs deduplicate identical redeliveries; conflicting content under the same ID returns 409. Missing IDs return 400, payloads above 10 MiB return 413, and persistence failures return 503. Four workers claim deliveries with 60-second leases and 30-second handler deadlines. Pending work and expired leases resume after restart. Processing is at least once, so handlers must be idempotent. Successful payloads are cleared, while IDs/hashes remain for deduplication; failed payloads and errors remain in the inbox for diagnosis. Automatic retry of handler failures is the next implementation step. Shutdown stops claims and finishes active work before closing PostgreSQL; unfinished persisted work remains available to the next process. See webhook operations. Cloud Run and other deployments must provision the database connection and secrets separately. make ko-local builds a local image; the repository does not provide a deploy target.

Pages and APIs

Dashboard pages and data APIs require an authorized session outside demo mode. API requests return JSON; HX-Request: true returns HTML partials for repositories, workflows, analytics and flaky checks. Trends always returns JSON.

Route Parameters / behavior
/ Repository overview, analytics and recent workflow runs
/repository/{owner}/{repo} workflow, branch, event, view=grid|cards, limit (commit columns; default 50, maximum 500)
/api/repositories All tracked repositories with all-time statistics
/api/workflows repository_id, status, conclusion, since (relative Go duration), limit (1–500; default 50), offset (0–100000; default 0)
/api/analytics repository_id, days (1–365; default 30)
/api/flaky-tests repository_id, days (1–365; default 30), min_runs (1–10000; default 5); top 20 results
/api/trends repository_id, days (1–365; default 30); daily aggregates
/api/health Public database readiness check, 200 or 503
/api/github/hook Public POST receiver; verifies GitHub webhook signatures
/ws Authorized realtime connection when enabled

Invalid API parameters return 400. since must be a positive Go duration; repository_id must be a nonnegative integer. Workflow JSON includes limit and offset; fetch the next page by adding limit to offset, stopping when fewer than limit runs are returned. Runs sort by creation time and ID descending. Offsets can shift as new webhooks arrive; this API does not provide snapshot isolation.

Omitting repository_id aggregates across all tracked repositories. Repository statistics are all-time; analytics and trends use the requested date window. Success rate is successful completed runs divided by completed runs with an outcome, excluding skipped, cancelled, neutral and empty outcomes. Total runs also includes unfinished runs. Flake rate is failed checks divided by successful plus failed checks, expressed as a percentage. Results require both success and failure; last_failure is the latest failing completion, not the latest successful check. This is a check-level intermittency heuristic, not proof that individual tests flake.

Development and testing

  • make test: Go tests with the race detector; database cases skip without a test URL.
  • make test-integration: PostgreSQL cases plus Go tests; requires a dedicated test database.
  • npm ci && make test-js: frontend regressions, requiring Node 20 or newer.
  • make test-e2e: Chromium against an isolated loopback demo; install Chromium first.
  • make assets: regenerate checked-in HTMX and Tailwind assets from locked dependencies.
  • make lint: golangci-lint.
  • make verify: module verification, tidy check, vet and reachable vulnerability checks.

See testing for disposable-database setup, browser installation and failure artifacts. CI runs Go, database, frontend and browser tests, checks asset reproducibility and builds the ko image. Releases use GoReleaser, SBOMs and cosign signatures; see Releases and verification. See the improvement plan for review order and remaining project boundaries.

Releases and verification

Releases are built by GoReleaser from v* tags. Each release publishes binary archives, checksums.txt, a checksums.txt.sigstore.json signature bundle, and a container image at ghcr.io/cpanato/github-actions-dashboard with an SBOM. The image and checksums are signed with cosign keyless signing from the release workflow.

Verify the container image (replace vX.Y.Z with the release tag):

cosign verify ghcr.io/cpanato/github-actions-dashboard:vX.Y.Z \
  --certificate-identity-regexp '^https://github.com/cpanato/github-actions-dashboard/\.github/workflows/release\.yaml@refs/tags/v.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Verify the release archives by checking the signed checksums, then the archives against them:

cosign verify-blob checksums.txt \
  --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/cpanato/github-actions-dashboard/\.github/workflows/release\.yaml@refs/tags/v.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

sha256sum --ignore-missing -c checksums.txt

License

Apache License 2.0. The vendored HTMX script retains its MIT license alongside the asset; compiled Tailwind CSS retains its upstream license header.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
testdb
Package testdb creates disposable schemas for PostgreSQL integration tests.
Package testdb creates disposable schemas for PostgreSQL integration tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL