Files
ghstats/docs/deployment-guide.md
T
tiennm99 d0c75d5ff1 refactor(card): shrink cards from 500x220 to 340x200
Two ghstats cards side-by-side at 500px overflow GitHub's README column
width (~816px). Matching github-profile-summary-cards' 340x200 lets
consumers place two cards per row cleanly.

- Shared header() rect corner radius 8→6, title font 18→15, title anchor
  (25,35)→(20,30).
- All card layouts reworked:
  * profile + stats: row padding 70→55 / 24→20, icon 14→12, value
    font 13→12, stats right-anchor x=475→320.
  * donut_chart: centre (380,120)→(250,110), outerR 70→55, innerR 38→30,
    legend y0 70→55, swatch 12→10, topN 6→5.
  * productive-time: leftAxis 50→35, topPad 60→45, barGap 2→1.
  * contributions: leftPad/rightPad 35→28, topPad 60→45, chartH 120→105.

Visual parity with github-profile-summary-cards; two-per-row layouts in
profile READMEs now fit.

Also: document the one-time GitHub Marketplace publishing step in
deployment-guide (no CLI flag exists — UI checkbox only).
2026-04-18 23:26:48 +07:00

5.7 KiB
Raw Blame History

Deployment Guide

Three consumption paths: GitHub Action, prebuilt binaries, go install.

Workflow template

File: .github/workflows/ghstats.yml in your profile repo.

name: ghstats

on:
  schedule:
    - cron: "0 0 * * *"        # daily at 00:00 UTC
  workflow_dispatch:

permissions:
  contents: write              # needed for commit_changes

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

Required secrets

GHSTATS_TOKEN: a classic personal access token with at minimum:

Scope Needed for
read:user Basic profile fields, contribution calendar
repo Only if include_private: "true"

Fine-grained PATs and the default ${{ github.token }} lack the introspection scope for contribution calendars in many orgs, so a classic PAT is recommended.

Create one at https://github.com/settings/tokens → "Generate new token (classic)" → select read:user (+ repo if needed) → save as repo secret GHSTATS_TOKEN.

Embedding in README

![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)
![contributions](./output/dracula/contributions.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)
![contributions-all-time](./output/dracula/contributions-all-time.svg)

The Action commits SVGs to output/<theme>/ on the default branch. GitHub serves them from the raw URL the README references.

2. Prebuilt binaries

Each tag under v* publishes:

  • Linux amd64, arm64
  • macOS amd64, arm64
  • Windows amd64

Released via .github/workflows/release.yml which matrixes GOOS × GOARCH, strips symbols (-ldflags="-s -w"), and uploads tar.gz / zip to the GitHub Release.

Install:

# Linux x86_64 example
curl -L https://github.com/tiennm99/ghstats/releases/latest/download/ghstats_linux_amd64.tar.gz \
  | tar xz
./ghstats -user YOUR_USERNAME

3. go install

go install github.com/tiennm99/ghstats@latest

Requires Go 1.26+. Puts the binary in $(go env GOPATH)/bin.

Docker image

Published to ghcr.io/tiennm99/ghstats:<tag> on each v* release via .github/workflows/release.yml (buildx, multi-tag: exact version, major.minor, major, latest).

The Action itself uses a runner-built image by default (image: Dockerfile in action.yml). To switch to the pre-built image for faster cold starts, edit action.yml:

runs:
  using: docker
  image: docker://ghcr.io/tiennm99/ghstats:v1

Release process

  1. Tag: git tag -a v1.2.0 -m "..." && git push origin v1.2.0.
  2. release.yml runs go vet + go test as a gate before the docker and binaries jobs. If tests fail, no artifacts ship.
  3. On green, GHCR push + cross-platform binary artifacts happen automatically.
  4. Docker base images and third-party actions are SHA-pinned (with version comments) so mutable-tag changes upstream can't rewrite a released image.
  5. Marketplace publishing (one-time per repo): GitHub only exposes the "Publish this Action to the GitHub Marketplace" toggle on the Release web UI — there is no CLI flag. Open the newly created release at https://github.com/tiennm99/ghstats/releases/tag/vX.Y.Z/edit, tick the marketplace checkbox, accept the terms, and re-publish. Subsequent releases inherit marketplace visibility automatically.

Rollback

  • Revert the tag: git push --delete origin v1.2.0, delete GitHub release, delete GHCR tag.
  • Users pinned to @v1 keep working because the previous patch is still tagged.

Rate limit considerations

Scenario GraphQL calls per run Notes
Typical user, defaults 1540 Well under 5000 pts/hr
Active user (8 years, 30+ seed repos) 4080 Still comfortable
-include-private=true with 100+ work repos 80200 Fine for daily cron
Adversarial user with 500+ committed repos/year Capped by maxRepositories: 100 per year query Long tail drops silently

No REST calls today. Future -accurate-languages mode will push toward 1000+ REST per run; schedule that mode less frequently (weekly, not daily).

The client auto-handles rate-limit responses: on 429 or 403 with X-RateLimit-Remaining: 0, it sleeps up to 5 minutes (honoring Retry-After / X-RateLimit-Reset) and retries once. A reset window longer than 5 min surfaces as an error so CI can reschedule instead of burning runner time. Use the -timeout flag (default 30m) to cap total fetch duration; SIGINT/SIGTERM cancels in-flight requests cleanly.

Troubleshooting

Symptom Check
"error: fetch profile: graphql: Could not resolve to a User" Username typo
"http 401" Token expired or lacks read:user
"rate limit resets in 42m (>5m0s max wait)" Client refused to sleep through a long window; reschedule the Action
"http 403" on non-rate-limit path PAT scope too narrow
Blank contribution chart User has 0 contributions in their window; expected
Private repo data missing -include-private=true not set, or PAT lacks repo
Nothing committed by the Action Check permissions: contents: write in the workflow