Huge thanks to Star History. GH Stars is a self-hosted solution to GitHub's stargazer API access restriction: your history and chart stay in your own repository.
A repository-owned, embeddable GitHub star-history chart. It uses the workflow's GITHUB_TOKEN to backfill the stargazer timestamps that GitHub makes available to repository collaborators, then records a daily total so future unstars are reflected accurately.
Create .github/workflows/star-history.yml in the repository you want to track:
name: Update star history
on:
schedule:
- cron: '17 3 * * *'
workflow_dispatch:
permissions:
contents: write
jobs:
update:
concurrency:
group: star-history
cancel-in-progress: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: nicoloboschi/gh-stars@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
line-color: '#14b8a6' # optional
- name: Commit chart
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add .github/star-history/data.json .github/star-history/chart.svg
git diff --cached --quiet || git commit -m 'chore: update star history'
git pushRun the workflow once with Actions → Update star history → Run workflow to create the initial backfill and SVG.
Embed the chart in your README:
| Input | Required | Default | Purpose |
|---|---|---|---|
github-token |
yes | — | ${{ secrets.GITHUB_TOKEN }} from the caller workflow. |
repository |
no | Current repository | Repository to monitor, as OWNER/REPOSITORY. Use this when the workflow lives in a separate repository. |
history-path |
no | .github/star-history/data.json |
Where to write the durable data. |
svg-path |
no | .github/star-history/chart.svg |
Where to write the embeddable chart. |
line-color |
no | #0969da |
Hex or named CSS color for the line and fill. |
The first execution fetches all current stargazers with their starred_at dates. That gives an immediate historical curve, with one unavoidable GitHub limitation: stars removed before installation cannot be recovered. Subsequent executions retain daily observed totals, including decreases from unstars. The SVG keeps a bounded number of line points through trend-preserving downsampling, selects readable English month or year axis labels based on the span, and embeds its display fonts so it renders consistently in README images.
If chart commits would trigger other CI or deploy systems in the tracked repository, keep the workflow there but commit the generated files to a small storage repository instead. The workflow's built-in GITHUB_TOKEN can read the repository it runs in, but it cannot push to another repository. Create a fine-grained personal access token named GH_STARS_STORAGE_TOKEN, restrict it to the storage repository only, and grant it Contents: Read and write. Add it as a secret in the tracked repository.
Replace OWNER/star-history-data and OWNER/PROJECT below. The storage repository must be public if you want to embed its raw SVG in a public README.
name: Update star history
on:
schedule:
- cron: '17 3 * * *'
workflow_dispatch:
permissions:
contents: read
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
path: source
- uses: actions/checkout@v5
with:
repository: OWNER/star-history-data
token: ${{ secrets.GH_STARS_STORAGE_TOKEN }}
path: star-history-data
- uses: nicoloboschi/gh-stars@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
history-path: star-history-data/projects/OWNER--PROJECT/data.json
svg-path: star-history-data/projects/OWNER--PROJECT/chart.svg
- name: Commit chart to storage repository
working-directory: star-history-data
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add projects/OWNER--PROJECT
git diff --cached --quiet || git commit -m 'chore: update OWNER/PROJECT star history'
git pushEmbed it from the storage repository:
The action can also run entirely from a utility repository. Set repository to the project being observed and use a separate read-only token for that project. Create GH_STARS_SOURCE_TOKEN as a fine-grained token restricted to the source repositories, with Metadata: Read-only access (or use a GitHub App installed on those repositories). The workflow's own GITHUB_TOKEN still writes the JSON and SVG to the utility repository.
Create one workflow per monitored project in the utility repository; using the same concurrency group keeps their commits serialized:
name: Track project stars
on:
schedule:
- cron: '17 3 * * *'
workflow_dispatch:
permissions:
contents: write
jobs:
update:
concurrency:
group: gh-stars-storage
cancel-in-progress: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: nicoloboschi/gh-stars@v1
with:
github-token: ${{ secrets.GH_STARS_SOURCE_TOKEN }}
repository: OWNER/PROJECT
history-path: projects/OWNER--PROJECT/data.json
svg-path: projects/OWNER--PROJECT/chart.svg
line-color: '#14b8a6'
- name: Commit chart
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add projects/OWNER--PROJECT
git diff --cached --quiet || git commit -m 'chore: update OWNER/PROJECT star history'
git pushThe repository, history-path, and svg-path inputs are the only configuration needed for multi-project use: assign each project a separate directory. Keep the source token read-only and the utility repository's built-in token write-only to that repository.
Requires Node 20 or later.
npm testPush a semantic version tag such as v1.2.0. The release workflow runs the test suite, creates the GitHub release with generated notes, and advances the matching major tag (v1). Once published in GitHub Marketplace, those releases remain under the existing GH Stars listing.