Skip to content

tiennm99/ghstats

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

ghstats

Generate SVG cards summarizing a GitHub user's profile — written in Go.

Marketplace Release License

ghstats is a single-binary CLI (and a GitHub Action wrapping it) that fetches data for a GitHub user and writes a themed set of SVGs you can embed in your profile README.

Marketplace listing: ghstats-cards · Source: tiennm99/ghstats

Cards rendered:

# Card What it shows
0 Profile details Login (Name) title + Octicon-labelled rows for company, location, link, join date (with age), followers/following, public repos
1 Repos per language Donut + legend: how many owned non-fork repos use each language as primary
2 Most commit language (last year) Donut + legend: last-year commits byte-weighted across each repo's language breakdown
3 Stats Star, commit (lifetime + last-year), PR, issue, PR-review, contributed-to totals
4 Productive time (last year) 24-hour bar chart with axes, title includes UTC±N.NN
5 Productive weekday (last year) 7-bar day-of-week chart, peak day highlighted
6 Contributions (last year) Smooth monthly area chart, Y-axis mirrored both sides, mm/yy labels
7 Contributions heatmap Classic 7×53 calendar grid with theme-derived intensity ramp and legend
8 Top starred repos Top 5 owned non-fork repos by ⭐, language dot + proportional bar
9 Streak Current streak, longest streak, active days / total days with date ranges
10 Most commit language (all time) Same as #2 but over lifetime commits
11 Productive time (all time) Same as #4 but over lifetime commits
12 Productive weekday (all time) Same as #5 but over lifetime commits
13 Contributions (all time) Area chart across every active year, auto-thinned x-axis labels
14 Contributions by year One bar per active year, peak year highlighted

Preview — dracula theme

Live render against the author's profile, committed by .github/workflows/demo.yml on every push to main. Rendered with start_of_week: monday so the heatmap rows and weekday bars start on Mon. Other 64 themes in the demo gallery.

profile-detailsstats
repos-per-languagetop-starred-repos
contributions-by-yearcontributions-heatmap
streak
Last yearAll time
most-commit-languagemost-commit-language-all-time
contributionscontributions-all-time
productive-weekdayproductive-weekday-all-time
productive-timeproductive-time-all-time

In the wild

  • tiennm99/tiennm99 — author's profile README, refreshed daily via tiennm99/ghstats@v1. Two-per-row layout, dracula theme.

Use as a GitHub Action (recommended)

Drop this in .github/workflows/ghstats.yml in your profile repo (the one named after your username):

name: ghstats

on:
  schedule:
    - cron: "0 0 * * *" # daily
  workflow_dispatch:

permissions:
  contents: write

jobs:
  cards:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: tiennm99/ghstats@v1
        with:
          user: ${{ github.repository_owner }}
          token: ${{ secrets.GHSTATS_TOKEN }}   # classic PAT with read:user + repo
          themes: dracula,github_dark,tokyonight
          tz: Asia/Saigon
          include_forks: "true"
          include_private: "true"
          commit_changes: "true"

Then embed the cards in your README.md:

![profile](./output/dracula/profile-details.svg)
![repos-per-language](./output/dracula/repos-per-language.svg)
![most-commit-language](./output/dracula/most-commit-language.svg)
![stats](./output/dracula/stats.svg)
![productive-time](./output/dracula/productive-time.svg)
![productive-weekday](./output/dracula/productive-weekday.svg)
![contributions](./output/dracula/contributions.svg)
![contributions-heatmap](./output/dracula/contributions-heatmap.svg)
![top-starred-repos](./output/dracula/top-starred-repos.svg)
![streak](./output/dracula/streak.svg)
![most-commit-language-all-time](./output/dracula/most-commit-language-all-time.svg)
![productive-time-all-time](./output/dracula/productive-time-all-time.svg)
![productive-weekday-all-time](./output/dracula/productive-weekday-all-time.svg)
![contributions-all-time](./output/dracula/contributions-all-time.svg)
![contributions-by-year](./output/dracula/contributions-by-year.svg)

Action inputs

Input Default Description
user GitHub username (required)
token ${{ github.token }} PAT with read:user + repo for private repo stats
out output Output directory
themes dracula Comma-separated theme ids, or all
tz UTC IANA tz for the productive-time card (e.g. Asia/Saigon)
start_of_week sunday First day of week for heatmap rows and weekday bars (sundaysaturday)
top_repos 0 Optional cap on seed repos probed for commit history (0 = unlimited)
commits_per_repo 500 Max commits sampled per repo (covers last-year and all-time aggregates)
include_forks true Include forked repos in stats and commit probing
include_private true Include private repos (requires PAT with repo scope; silently no-op otherwise)
commit_changes false Commit generated cards back to the repo
commit_message chore: update ghstats cards Commit message
commit_branch (current ref) Target branch for auto-commit
author_name github-actions[bot] Commit author
author_email …@users.noreply.github.com Commit email

Use as a CLI

go install github.com/tiennm99/ghstats@latest

Or build from source:

git clone https://github.com/tiennm99/ghstats
cd ghstats
go build -o ghstats .

Then:

export GITHUB_TOKEN=ghp_xxx
ghstats -user tiennm99 -themes dracula,github_dark -tz Asia/Saigon -out output
Flag Default Description
-user (required) GitHub username
-token $GITHUB_TOKEN Personal access token
-out output Output directory (<out>/<theme>/…svg)
-themes dracula Comma-separated theme ids, or all
-tz Local IANA timezone for productive-time cards
-start-of-week sunday First day of week for heatmap rows and weekday bars (sundaysaturday)
-top-repos 0 Optional cap on seed repos probed (0 = unlimited)
-commits-per-repo 500 Max commits sampled per repo
-include-forks true Include forked repos in the stats
-include-private true Include private repos (requires repo PAT scope; silently no-op otherwise)
-list-themes Print available theme ids and exit

How attribution works

Repo sampling uses a seed list built from contributionsCollection.commitContributionsByRepository, unioned across every active contribution year. This catches every repo you've committed in — not just your top-starred ones.

Commit-to-language is byte-weighted: each commit credits every language in the repo, proportional to linguist's byte share. A commit to a 60% Go / 40% Python repo adds 0.6 to Go and 0.4 to Python, regardless of which file was touched. Caveats:

  • Linguist excludes prose (Markdown, AsciiDoc, reST) from byte counts, so heavily-Markdown repos skew toward whatever small code fraction linguist did detect.
  • For per-file accuracy, a future -accurate-languages mode is planned (per-commit REST + go-enry).

Cost per run (current defaults, typical user):

  • ~1 profile query + ~1 query per active year + ~50 commit-history pages ≈ 50-70 GraphQL calls.
  • Zero REST calls. Well under the 5000 points/hr budget.

Themes

Run ghstats -list-themes for the full list (65 themes ported from github-profile-summary-cards). Built-ins include default, dark, dracula, github, github_dark, tokyonight, onedark, nord_dark, nord_bright, gruvbox, radical, synthwave, monokai, solarized, solarized_dark, transparent, and more. Preview every one against real profile data in the demo gallery.

Output

output/
  dracula/
    profile-details.svg
    repos-per-language.svg
    most-commit-language.svg
    stats.svg
    productive-time.svg
    productive-weekday.svg
    contributions.svg
    contributions-heatmap.svg
    top-starred-repos.svg
    streak.svg
    most-commit-language-all-time.svg
    productive-time-all-time.svg
    productive-weekday-all-time.svg
    contributions-all-time.svg
    contributions-by-year.svg

output/ is entirely gitignored — it's regenerated on each run. For a reference render of every card in every theme, see the CI-built demo/ gallery instead.

Tokens & permissions

The default ${{ github.token }} can read public user data but will not see your private-repo commits. For accurate stats, create a classic personal access token with read:user and repo, save it as a repo secret (e.g. GHSTATS_TOKEN), and pass it via the token input. include_private defaults to true so those commits are counted automatically once the token has repo scope; pass include_private: "false" if you want to keep private work out of the rendered cards even when the token can see it.

Credits & inspiration

License

Apache-2.0 — see LICENSE.

Packages

 
 
 

Contributors