An open API service indexing awesome lists of open source software.

https://github.com/ewels/contributor-graphs

Contributor timelines for any git or GitHub repo: a publication-ready SVG and an interactive HTML page
https://github.com/ewels/contributor-graphs

cli contributors data-visualization git github open-source rust svg timeline visualization

Last synced: about 17 hours ago
JSON representation

Contributor timelines for any git or GitHub repo: a publication-ready SVG and an interactive HTML page

Awesome Lists containing this project

README

          




contributor-graphs


Contributor timelines for any git or GitHub repository: a publication-ready
SVG and a self-contained interactive HTML page.


CI
License: Apache-2.0
Docs

The x-axis is time (first commit to today); each row is a contributor. Bars
are shaded by monthly commit activity, so it's easy to see who was active when
and how a project grew over the years.





The interactive contributor-graphs page for nf-core/rnaseq



Documentation & live examples →

## Features

- **Works with anything:** a local path (`.`), a GitHub slug (`nf-core/rnaseq`),
or any git URL. Remote repos are cloned (history only) into a local cache.
- **GitHub enrichment:** resolves real names, `@usernames` and avatars via the
GitHub API, using your `gh` CLI token automatically to avoid rate limits.
- **Identity merging:** folds together the many name and email spellings a
single person accumulates over the years, with a manual override file for the
stragglers.
- **Affiliation grouping:** auto-detects organisations from GitHub profile
companies (e.g. _SciLifeLab_, _Seqera_) and colours by them. Optionally
**collapse the whole chart to one row per affiliation**, so each bar is an
organisation rather than a person. Supply your own grouping file for control.
- **Noise filters:** exclude bots, set a minimum-commit threshold, cap to the
top _N_ contributors. In the HTML these are live controls.
- **Interactive HTML:** search, sort, filter by group, switch between
per-contributor and per-affiliation rows, drag-to-zoom the timeline, hover
for detail + activity sparkline, dark mode, and SVG/PNG export. Everything is
embedded in one file; no server needed.

## Install

Grab a prebuilt binary, install with Cargo, or use Docker. The
[GitHub CLI](https://cli.github.com) (`gh`) is optional but recommended for
enrichment without rate limits.

**Prebuilt binary:** download the archive for your platform from the
[releases page](https://github.com/ewels/contributor-graphs/releases), unpack
it, and put `contributor-graphs` on your `PATH`. No toolchain required.

**Cargo** (needs [Rust](https://rustup.rs)):

```bash
cargo install --git https://github.com/ewels/contributor-graphs # latest
cargo install contributor-graphs # once on crates.io
```

**Docker:** published to the GitHub Container Registry:

```bash
docker run --rm -v "$PWD:/work" -e GITHUB_TOKEN \
ghcr.io/ewels/contributor-graphs nf-core/rnaseq
```

## Usage

```bash
# A GitHub repo by slug: clones history, enriches, writes two files
contributor-graphs nf-core/rnaseq

# A local checkout
contributor-graphs . -o docs/

# A full git URL
contributor-graphs https://github.com/MultiQC/MultiQC

# Several sources pooled into one timeline (any mix of slugs, paths, URLs)
contributor-graphs nf-core/rnaseq nf-core/sarek MultiQC/MultiQC --title "nf-core + MultiQC"
```

This writes `.svg` and `.html` into the output directory.

### Multiple sources

Pass more than one source to pool every commit into a single timeline. Author
identities are resolved across the whole pool, so someone who appears in several
repositories shows up as one row. Commits that appear in more than one source
(overlapping histories — e.g. a repo and a fork, or a branch grafted onto a
rewrite) are de-duplicated by commit SHA; disjoint sources (separate repos for
an org-wide view) simply concatenate. Use `--title` to name the combined chart.

### Authentication

To enrich with usernames and avatars (and dodge GitHub's anonymous rate limit),
the tool reads a token from `$GITHUB_TOKEN` or `$GH_TOKEN`, falling back to
`gh auth token` if neither is set. Locally, just be logged in:

```bash
gh auth login
```

In CI, the `$GITHUB_TOKEN` that GitHub Actions injects is picked up
automatically, with no extra setup:

```yaml
- run: contributor-graphs ${{ github.repository }} -o site/
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

Pass `--no-github` to skip all network calls and render from git data alone.

## Common options

| Flag | Description |
| ----------------------------------- | -------------------------------------------------------------------- |
| `-o, --output-dir ` | Where to write outputs (default: `.`) |
| `--title ` | Override the chart title |
| `-b, --branch ` | Which branch/ref to read (default: `HEAD`) |
| `--since ` / `--until ` | Restrict the commit window |
| `--min-commits ` | Hide contributors below `N` commits in the SVG (default: 1) |
| `--min-span-days ` | Drop one-off/short-burst contributors (first-to-last span) from SVG |
| `--max-contributors ` | Cap SVG rows to the top `N` by commits (default: 40) |
| `--include-bots` | Keep bot accounts (excluded by default) |
| `--exclude ` | Drop contributors matching a name/login (repeatable) |
| `--by-affiliation` | Collapse each row to a whole affiliation, not one person |
| `--unaffiliated-label ` | Bucket name for people with no affiliation (default: `Unaffiliated`) |
| `--sort ` | `first` · `last` · `commits` · `duration` · `name` |
| `--groups ` | Manual affiliation mapping (see below) |
| `--identities ` | Manual identity-merge file (see below) |
| `--no-affiliation` | Disable auto group detection from profiles |
| `--no-name-merge` | Don't merge identities that share an author name |
| `--accent ` | Bar accent colour (default: `#2f6feb`) |
| `--theme ` | Theme id: `auto`, `light`, `dark`, `wikipedia`, or a custom one |
| `--themes ` | Define extra themes / configure the page's theme menu |
| `--lock-theme` | Hide the page's theme switcher and pin to one theme |
| `--width ` | Static SVG width (default: 1100) |
| `--format ` | Which outputs to write (default: both) |
| `--open` | Open the HTML in your browser when done |

Run `contributor-graphs --help` for the full list.

### Themes

The interactive page has a single Theme dropdown (top right) offering Light,
Dark, and **Wikipedia**; it opens on your OS light/dark preference unless you
pick one, and the choice is remembered per browser. The Wikipedia theme borrows
the look of Wikipedia's "band members over time" timelines: a Linux Libertine
heading over a plain sans-serif body, Wikipedia colours, square controls, and a
distinct solid bar per contributor instead of activity-heat shading.

`--theme ` sets the SVG's look and the page's initial theme; `auto` (the
default) renders the SVG light and lets the page follow the viewer's OS.

#### Custom themes

Define your own themes in a JSON file and pass it with `--themes`. Each theme
inherits from `extends` (a built-in or another custom theme; default `light`)
and overrides only what it needs:

```json
{
"default": "seqera",
"available": ["seqera", "dark"],
"lock": false,
"themes": {
"seqera": {
"label": "Seqera",
"extends": "light",
"accent": "#0d6273",
"bg": "#f4f8f8"
}
}
}
```

- `default` — the theme the page opens with (also settable with `--theme`).
- `available` — which themes appear in the menu, in order (default: all).
- `lock` (or `--lock-theme`) — hide the menu and pin to a single theme, so you
can ship one custom look with no switching.

A theme may set any of: `label`, `extends`, `dark` (bool, for `color-scheme`
and avatar shading), `flat` (bool, solid band bars + sans-serif chart font),
`radius` (px), `font_sans`, `font_display`, and the colours `bg`, `card`,
`border`, `border_strong`, `text`, `muted`, `faint`, `accent`, `accent_soft`,
`grid_year`, `grid_month`, `track`, `ctx_area`, `ctx_line`. Custom themes work
in both the SVG (`--theme `) and the interactive page.

### Grouping by affiliation

Affiliations are detected automatically from the `company` field of each
contributor's GitHub profile. Variant spellings are merged, so `seqeralabs`,
`Seqera Labs` and `Seqera` all count as one group. The most common groups get
distinct colours; the long tail shares a neutral grey, and bots are dropped.

For full control, supply a tab-separated file. Each row is `matchergroup`,
where _matcher_ is a name, email, or GitHub login:

```tsv
# groups.tsv
ewels Seqera
phil.ewels@seqera.io Seqera
Alexander Peltzer Boehringer Ingelheim
qbicsoftware QBiC
```

```bash
contributor-graphs nf-core/methylseq --groups groups.tsv
```

Manual mappings take precedence over auto-detected affiliations.

To make the affiliations the _subject_ of the chart, pass `--by-affiliation`:
one bar per organisation, with every member's commits merged into it. People
with no detected affiliation are pooled into a single "Unaffiliated" row
(rename it with `--unaffiliated-label`). In the interactive HTML this is the
**Rows** dropdown, so you can flip between people and organisations live.

```bash
contributor-graphs nf-core/rnaseq --by-affiliation
```

### Merging identities

Most duplicate identities (same email, or same name across emails) merge
automatically. To force-merge the stragglers, supply a file where each row
lists the canonical display name followed by any aliases (names, emails,
logins):

```tsv
# identities.tsv
Alexander Peltzer apeltzer a.peltzer@gmail.com Alex Peltzer
Patrick Hüther phue patrick.huether@example.org
```

```bash
contributor-graphs nf-core/methylseq --identities identities.tsv
```

## How it works

1. `git log` extracts every commit's author name, email, and timestamp
(honouring `.mailmap`).
2. Commits are clustered into identities by shared email, then by shared
author name.
3. For GitHub repos, each identity is resolved to a login + avatar (noreply
emails offline; the rest via the commits API), then profiles are fetched for
real names and companies. Clusters that resolve to the same login merge.
4. Per-contributor stats and per-month activity bins are computed.
5. The SVG and HTML are rendered. Avatars are embedded as data URIs so both
files are fully self-contained.

## Releasing

Releases are cut by publishing a GitHub Release whose tag is the version
(e.g. `v0.1.0`). That triggers the workflows to build cross-platform binaries,
attach them to the release, build the multi-arch Docker image, and publish to
crates.io.

crates.io publishing uses [Trusted Publishing](https://crates.io/docs/trusted-publishing)
(OIDC, so no API token is stored in the repo). One-time setup:

1. Publish the first version manually (Trusted Publishing can't attach to a
crate that doesn't exist yet): create a short-lived token at crates.io →
Account Settings → API Tokens, then `cargo publish` (run `cargo publish
--dry-run` first). Revoke the token afterwards.
2. On crates.io → the crate → Settings → Trusted Publishing, add a GitHub
publisher: owner `ewels`, repo `contributor-graphs`, workflow `release.yml`.
3. From then on, bump `version` in `Cargo.toml`, then publish a GitHub Release. The workflow mints a short-lived token via OIDC
and publishes automatically.

## License

[Apache-2.0](https://github.com/ewels/contributor-graphs/blob/main/LICENSE)