https://github.com/mchmarny/devpulse
Community health analytics for GitHub organizations and repositories
https://github.com/mchmarny/devpulse
cli cmd cncf developer entitity golang sql sqlite
Last synced: about 15 hours ago
JSON representation
Community health analytics for GitHub organizations and repositories
- Host: GitHub
- URL: https://github.com/mchmarny/devpulse
- Owner: mchmarny
- License: apache-2.0
- Created: 2022-05-07T21:43:46.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2026-03-21T00:06:13.000Z (12 days ago)
- Last Synced: 2026-03-21T05:14:53.458Z (12 days ago)
- Topics: cli, cmd, cncf, developer, entitity, golang, sql, sqlite
- Language: Go
- Homepage:
- Size: 90.2 MB
- Stars: 6
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# devpulse
[](https://github.com/mchmarny/devpulse/actions/workflows/test-on-push.yaml)
[](https://goreportcard.com/report/github.com/mchmarny/devpulse)
[](https://pkg.go.dev/github.com/mchmarny/devpulse)
[](LICENSE)
Community health analytics for GitHub organizations and repositories. `devpulse` imports contribution data from the GitHub API, enriches it with developer affiliations, and surfaces project health insights through a local dashboard.
> Motivation and the drivers behind this project are covered in the blog post [here](https://blog.chmarny.com/posts/devpulse-community-health-analytics-for-the-rest-of-us/)
## Features
**Project Health**
- **Bus factor / pony factor** -- minimum developers or organizations producing 50% of contributions
- **Repository Status** -- stars, forks, open issues, language, license with 30-day sparkline
- **Stars & forks trends** -- daily star and fork counts over the last 30 days with historical backfill
- **Community Profile** -- badges for README, Contributing, Code of Conduct, and issue/PR templates

**Activity & Code**
- **Activity trends** -- monthly event volume (PRs, reviews, issues, comments, forks) with total and 3-month moving average
- **PR size distribution** -- pull requests bucketed by lines changed (S/M/L/XL) per month
- **Forks & activity** -- monthly fork count vs total event activity
- **Issue Open/Close Ratio** -- monthly opened vs closed issues

**Velocity**
- **Lead time (PR to merge)** -- average days from PR creation to merge
- **Change failure rate** -- percentage of deployments causing failures (bug issues near releases + revert PRs)
- **Release cadence** -- monthly release counts (total, stable, deployments) with merge-to-main fallback
- **Release downloads** -- monthly download trends and top releases by download count
- **Time to First Response** -- average hours to first review or comment on PRs

**Quality**
- **PR review ratio** -- PRs to reviews per month with ratio trend line
- **Review latency** -- average hours from PR creation to first review
- **Time to close** -- average days to close all issues vs bug issues near releases
- **Contributor reputation** -- two-tier scoring (shallow local, deep GitHub API) with known bot filtering

**Community**
- **Contributor retention** -- new vs returning contributors per month
- **Contributor momentum** -- rolling 3-month active contributor count with month-over-month delta
- **First-time contributor funnel** -- new contributor milestones per month (first comment, first PR, first merge)
- **Entity affiliations** -- top contributing companies/orgs with drill-down to individual developers (GitHub profile + CNCF gitdm)

**Insights**
- **LLM-generated observations** -- AI-powered analysis of repository health, trends, and action items per repo
**Dashboard**
- **Global summary banner** -- organizations, repositories, events, contributors, and last import timestamp (GMT) at a glance
- **Tabbed layout** -- Health, Activity, Velocity, Quality, Community, Insights, and Events tabs with lazy-loaded charts
- **Event search filters** -- filter by type, date range, username, or entity from the Events tab
- **Adjustable time period** -- dropdown adapts to available data range per search scope
- **Unified search** -- `org:name` or `repo:name` prefix syntax; all panels respect scope

## Install
### Homebrew (macOS / Linux)
```shell
brew tap mchmarny/tap
brew install devpulse
```
### Binary releases
Visit [latest releases](https://github.com/mchmarny/devpulse/releases/latest) page.
### Build from source
Requires [Go](https://go.dev/) 1.26+.
```shell
git clone https://github.com/mchmarny/devpulse.git
cd devpulse
make build
```
See [DEVELOPMENT.md](DEVELOPMENT.md) for details.
## Quick start
### 1. Authentication
`devpulse` uses GitHub's device flow for OAuth. By default the token requests the `repo` scope for private repository access. Use `--public` to skip the `repo` scope when you only work with public repos. The token is stored in your OS keychain.
```shell
devpulse auth # private + public repo access (default)
devpulse auth --public # public repo access only
```
Alternatively, set `GITHUB_TOKEN` to skip the auth command entirely:
```shell
export GITHUB_TOKEN=ghp_...
```
### 2. Data import
Import events, affiliations, metadata, releases, and reputation for an org:
```shell
devpulse import --org
```
Or target specific repos:
```shell
devpulse import --org --repo --repo
```
Use `--fresh` to clear pagination state and re-import from scratch:
```shell
devpulse import --org --fresh
```
Update all previously imported data (no flags needed):
```shell
devpulse import
```
Control how many repos import in parallel (default: 3):
```shell
devpulse import --concurrency 2
```
See [docs/IMPORT.md](docs/IMPORT.md) for all import options.
### 3. Reputation score
Import computes basic reputation scores automatically. For deeper scoring using GitHub API signals (profile age, org membership, PR history, etc.):
```shell
devpulse score --org # deep-score 5 lowest in org (default)
devpulse score --org --repo # scope to a specific repo
devpulse score --org --count 20 # deep-score 20 lowest
```
Run incrementally — each invocation scores the next batch of lowest-reputation contributors. See [docs/SCORE.md](docs/SCORE.md) for details.
### 4. Scheduled sync
For automated, scheduled imports, `sync` reads a config file and imports + scores one repo per run using hour-based round-robin rotation:
```shell
devpulse sync --config sync.yaml
devpulse sync --config https://raw.githubusercontent.com/org/repo/main/config/sync.yaml
# Override round-robin to sync a specific repo
devpulse sync --config sync.yaml --org NVIDIA --repo DCGM
# Sync a specific repo without a config file (uses hardcoded defaults)
devpulse sync --org NVIDIA --repo DCGM
```
Config format (all `reputation` and `insight` fields are optional with sensible defaults):
```yaml
repos:
- name: repo1
org: myorg
reputation:
scoreCount: 50 # contributors to deep-score per run (default: 50)
staleAfter: "3d" # re-score after this duration (default: 3d)
insight:
periodMonths: 3 # months of data for insights (default: 3)
staleAfter: "3d" # regenerate insights after this (default: 3d)
- name: repo2
org: myorg
- name: devpulse
org: mchmarny
```
The `--config` flag (or `DEVPULSE_SYNC_CONFIG` env var) accepts a local file path or HTTP(S) URL. Each run picks one repo from the list based on `UTC hour % total repos`, runs a full import, then deep-scores the lowest-reputation contributors. With 9 repos on an hourly schedule, each repo is imported 2-3 times per day while staying within GitHub's 5,000 requests/hour API rate limit. Reputation and insight thresholds are configured per-repo in the YAML file.
### 5. Dashboard view
```shell
devpulse server
```
Opens your browser to `http://127.0.0.1:8080`. Use `--port` to change the port or `--no-browser` to suppress auto-open.
The dashboard shows a global summary banner (orgs, repos, events, contributors, last import timestamp in GMT) and organizes insights into seven tabs: **Health**, **Activity**, **Velocity**, **Quality**, **Community**, **Insights**, and **Events**. Charts load lazily per tab.
You can run `devpulse import` in a separate terminal or cron job while the server is running — the dashboard picks up new data immediately after each import transaction commits. See [docs/SERVER.md](docs/SERVER.md) for details.
Use the search bar with prefix syntax to scope the dashboard:
| Prefix | Example | Scope |
|--------|---------|-------|
| `org:` | `org:myorg` | All repos in an organization |
| `repo:` | `repo:skyhook` | Single repository |
No prefix defaults to org search.
### 6. Programmatic query
`devpulse` also exposes data as JSON for scripting:
```shell
devpulse query events --org knative --repo serving --type pr --since 2024-01-01
devpulse query developer list --like mark
devpulse query entity detail --name GOOGLE
```
See [docs/QUERY.md](docs/QUERY.md) for all query options.
### 7. Data deletion
Remove imported data for a specific org or repo:
```shell
devpulse delete --org # delete all data for org
devpulse delete --org --repo # delete data for specific repo
devpulse delete --org --repo --force # skip confirmation prompt
```
### 8. Full reset
Delete all imported data and start fresh:
```shell
devpulse reset
```
Prompts for confirmation before deleting the database.
## Data sources
| Source | Data |
|--------|------|
| [GitHub API](https://docs.github.com/en/rest) | PRs, issues, comments, reviews, forks, repo metadata, releases |
| [cncf/gitdm](https://github.com/cncf/gitdm) | Developer-to-company affiliations |
Entity names are normalized automatically. Use `devpulse substitute` to correct misattributions:
```shell
devpulse substitute --type entity --old "INTERNATIONAL BUSINESS MACHINES" --new "IBM"
```
## Database
By default, data is stored locally in [SQLite](https://www.sqlite.org/) (`~/.devpulse/data.db`). No external services required.
### PostgreSQL
To use PostgreSQL instead, pass a `postgres://` connection URI via `--db` or `DEVPULSE_DB`:
```shell
devpulse import --db "postgres://user:pass@host:5432/dbname?sslmode=disable" --org --repo
devpulse server --db "postgres://user:pass@host:5432/dbname?sslmode=disable"
```
Or via environment variable:
```shell
export DEVPULSE_DB="postgres://user:pass@host:5432/dbname?sslmode=disable"
devpulse import --org --repo
```
Migrations run automatically on first connection. Special characters in the password must be URL-encoded (e.g., `/` → `%2F`, `@` → `%40`).
For Google Cloud AlloyDB, connect through the [AlloyDB Auth Proxy](https://cloud.google.com/alloydb/docs/auth-proxy/overview) with `--public-ip` and use `127.0.0.1` as the host.
### LLM (Insights tab)
The Insights tab uses an LLM to generate observations and action items. Configure via environment variables:
```shell
export ANTHROPIC_API_KEY="sk-ant-..." # required for Insights tab
export ANTHROPIC_BASE_URL="https://..." # optional, override API endpoint
export ANTHROPIC_MODEL="claude-..." # optional, override model selection
```
## Architecture
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for details.
## Verification
Release binaries are signed and attested in CI. No private keys — everything uses keyless [Sigstore](https://www.sigstore.dev/) OIDC via GitHub Actions.
### Verify checksum signature
```shell
cosign verify-blob \
--bundle checksums-sha256.txt.sigstore.json \
--certificate-identity-regexp 'github.com/mchmarny/devpulse' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums-sha256.txt
```
### Verify build provenance
```shell
gh attestation verify -R mchmarny/devpulse
```
### Inspect SBOM
Each binary has a corresponding SBOM (SPDX JSON) attached to the release.
## Contributing
Contributions are welcome. Please open an issue before submitting large changes. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [DEVELOPMENT.md](DEVELOPMENT.md) for setup.
1. Fork and clone the repository
2. Create a feature branch
3. Run `make qualify` (tests, lint, vulnerability scan)
4. Submit a pull request
## License
[Apache 2.0](LICENSE)