Skip to content

About

GitHub Action that tracks star counts across your repositories: charts, badges, reports and email notifications when they change

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

16 stars

Watchers

1 watching

Forks

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

Latest commit

 

History

593 Commits

Folders and files

GitHub Star Tracker star mark

GitHub Star Tracker

A GitHub Action that tracks star counts across all your repositories on a schedule, generates visual reports with charts and badges, and emails you once enough has changed to be worth reading.

CI Codecov License: AGPL-3.0 Product Hunt

Documentation · Getting Started · Configuration · Examples · Troubleshooting

Important

Coming from Star History, Starchart.cc or similar? You're in the right place.

GitHub announced that access to the stargazers API is being restricted to repository admins and collaborators. Tools that chart stars for repositories they don't own will start receiving empty responses or 403 errors.

GitHub Star Tracker keeps working. It runs inside your workflow, with your token, against your repositories: exactly the access GitHub is keeping. Star counts, reports, badges, CSV and notifications are unaffected in every case.

The one thing that depends on your role rather than your token's scopes is the stargazer endpoint, and it is what star-history charts and stargazer tracking are reconstructed from. If you track a repository you do not administer (an organization repo where you are a read-only member, or any repo reached through a fine-grained token with no explicit grant on its organization), those two fall back to the stored per-run snapshots. Known Limitations has the detail.


Table of Contents


What You Get

Every writing run commits these artefacts to a dedicated data branch, with a new snapshot appended even when no star moved. The runs that commit nothing are a read-only run, which renders and reports in full but never pushes, and a run whose filters matched no repository, which never opens the branch.

  • Animated SVG charts: star history, per-repo trends, top repos comparison, and growth forecasts, with automatic dark/light mode support:

    Star History Top Repositories Growth Forecast
  • Shields.io-style badge: embeddable star count that updates automatically:

    Stars
  • Markdown report: summary tables, delta indicators, every New Repository and Removed Repository since the baseline, stargazer details, and forecast tables.

  • CSV & JSON data: machine-readable exports for dashboards, spreadsheets, or downstream pipelines.

The HTML report is the one thing that is not committed. It is written outside the data branch worktree for the email body, and reaches you through the report-html and report-html-path outputs instead. Its charts are QuickChart images whose URLs carry each chart's data, repository names included, so on a private repository those names reach quickchart.io whenever the email's images load.


Features

  • Animated SVG charts: star history, per-repo trends, comparisons, and growth forecasts, with an optional Trend Line over the observed curve
  • Dark/light mode: SVG charts auto-adapt to the viewer's color scheme via prefers-color-scheme
  • Historical snapshots: configurable retention (default: 52 runs) with JSON persistence
  • Smart filtering: by visibility, Owner, min stars, exact names or regex, archived, forks
  • Stargazer tracking: see who starred your repos, with avatars and dates
  • Email notifications: built-in SMTP, with a fixed or adaptive threshold and a Notification Mode that decides whether losses count
  • GitHub Enterprise: GHES support, auto-detected or explicit API URL
  • Multi-language: English, Spanish, Catalan, Italian
  • CSV export: machine-readable output for data pipelines
  • Action outputs for workflow chaining: lost-stars, new-stargazers, new-stars, notification-sent, report, report-csv, report-html, report-html-path, should-notify, stars-changed and total-stars
  • No install step: its runtime dependencies are bundled into the committed dist/, so the runner clones and executes, with no npm install in your workflow (ADR 0003)
  • Enforced coverage: the build gate is 85% on lines, functions, branches and statements, over an extensive unit test suite
  • Built around GitHub's 2026 stargazers restriction: star counts, reports, badges and notifications never touch the restricted endpoint, and the stargazer-based charts and tracking keep working wherever your token's user is an admin or collaborator, as the note at the top explains

Quick Start

1. Create a Personal Access Token

  1. Go to GitHub Settings > Tokens
  2. Generate a classic token with repo or public_repo scope, or a fine-grained token with Metadata: Read-only on the repositories you want tracked
  3. Add it as a repository secret named STAR_TRACKER_TOKEN

Note

The default GITHUB_TOKEN is not sufficient to list your repositories, but it is what pushes the data branch: with actions/checkout's default persist-credentials: true, git sends the workflow's own token, so the PAT only reads. It needs Contents: Read and write on the workflow's repository only if your checkout sets persist-credentials: false. See the PAT guide for details, including why a fine-grained token can leave the stargazer sections empty.

2. Add the Workflow

Create .github/workflows/star-tracker.yml:

name: Track Stars

on:
  schedule:
    - cron: '0 0 * * *' # Daily at midnight
  workflow_dispatch:

permissions:
  contents: write # git pushes the data branch with this workflow's token

concurrency:
  group: star-tracker
  cancel-in-progress: false

jobs:
  track:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - name: Track stars
        id: tracker
        uses: fbuireu/github-star-tracker@v1
        with:
          github-token: ${{ secrets.STAR_TRACKER_TOKEN }}
  • contents: write is what the push needs: actions/checkout keeps the workflow's own token in the checkout, and git sends that one when the action pushes the data branch. A read-only run never pushes and needs only contents: read.
  • concurrency queues a manual run behind a scheduled one instead of letting them overlap. Two writing runs that overlap both branch from the same data-branch commit, and the second push is refused, ending that run red after its report and email have gone out.
  • id: tracker is what later steps read the outputs through, as steps.tracker.outputs.<name>.

3. Run and View

  • Manual run: Actions > Track Stars > Run workflow
  • View report: Check the star-tracker-data branch in your repository

The first run creates that branch and has nothing to compare against, so every repository arrives as new with a delta of zero and stars-changed is true. With email configured and the default notification-threshold, the first email goes out listing all of them, and with track-stargazers on every existing stargazer is reported as new too. Later runs report only what changed. How It Works has the detail.


Configuration

Set options directly in the workflow or via a YAML config file. See the Configuration guide for full details.

- name: Track stars
  id: tracker
  uses: fbuireu/github-star-tracker@v1
  with:
    github-token: ${{ secrets.STAR_TRACKER_TOKEN }}
    visibility: 'public' # public | private | all | owned
    locale: 'es' # en | es | ca | it
    include-charts: true
    track-stargazers: true
    min-stars: '5'
    exclude-repos: 'test-repo,/^demo-.*/'
    compare-against: 'last-run' # last-run | 24h | 7d | 30d
    notification-threshold: '500' # 0 | N | auto
    notification-mode: 'gains' # net | gains
All Inputs
Input Default Description
github-token - Required. PAT with repo or public_repo scope
chart-animation true Animate SVG charts (true) or render them static (false) for email/static contexts
chart-begin-at-zero false Start the Y-axis at zero (true) or zoom into the data range (false)
chart-curve monotone Curve when smoothing: monotone (no overshoot, best for stars), catmull-rom, cubic-bezier, rounded-step. Email approximates non-monotone curves
chart-custom-milestones - Comma-separated star counts for the milestone reference lines, replacing the built-in defaults (e.g. 250, 750, 2500). Requires chart-milestones
chart-line-color #dfb317 Hex color of primary chart line/fill/points (not comparison). Accepts hex with or without a leading #
chart-line-width 2.5 Stroke width (px, >0) of data lines in all charts
chart-max-points 30 Curve granularity: points across the full span (capped at 365); 0 reconstructs at weekly resolution. Not a time window (see chart-range)
chart-milestones true Show milestone reference lines on the main star-history chart (true) or hide them (false)
chart-range all Time window plotted: 30d, 90d, 1y or all
chart-show-points true Draw a marker on each data point (true) or hide them for a cleaner dense line (false)
chart-smoothing true Smooth curve (true) or straight segments to show spikes; applies to email charts too
chart-theme auto Color theme for the SVG charts: auto (follows prefers-color-scheme), light or dark
chart-trend-line false Overlay a dashed moving-average trend line on the main chart
chart-y-axis-side left Y-axis labels side: left or right
compare-against last-run Snapshot used as comparison baseline: last-run, 24h, 7d or 30d. A window takes the newest snapshot at least that old, with 6 hours of slack so a late scheduled run still finds its own. If history is shorter than the window, the oldest stored snapshot is used and the report's date shows how far back it really goes
config-path star-tracker.yml Path to YAML config file
data-branch star-tracker-data Branch for tracking data
email-from localized Sender name or address; defaults to a localized sender name
email-theme auto Color theme for the HTML email and its charts: auto (same as chart-theme), light or dark. Email charts are images, so a dark-mode reader needs this to resolve to dark
email-to - Recipient address
exclude-orgs - Owners to exclude (exact name or /regex/)
exclude-repos - Repos to exclude (exact name or /regex/)
github-api-url - GitHub API base URL (for GHES), https only. Auto-detected on GHES runners
include-archived false Include archived repos
include-charts true Generate star trend charts
include-forks false Include forked repos
locale en en, es, ca, or it
max-history 52 Max snapshots to keep
min-stars 0 Min stars to track
notification-mode net How the threshold measures that change: net (absolute change, so a large drop also fires) or gains (upward movement only)
notification-threshold 0 0 (every run with changes), N (accumulated change since the last notification), or auto (adaptive)
only-orgs - Only track repos under these Owners (exact name or /regex/)
only-repos - Only track these repos (exact name or /regex/). When set, it skips the archived, fork, exclude and min-stars filters; only only-orgs still narrows first
read-only false Make this a Read-Only Run: it still fetches, reports, sets outputs and emails, it just never commits or pushes. Use it for another workflow that shares a data branch with the workflow that writes it
send-on-no-changes false Email even with no changes
smart-sampling false Sample stargazer pages for high-star repos (avoids rate limits)
smart-sampling-pages 30 Max evenly-spaced stargazer pages per sampled repo
smart-sampling-threshold 1500 Star count above which a repo is sampled
smtp-host - SMTP hostname (enables email)
smtp-password - SMTP password
smtp-port 587 SMTP port: 465 means implicit TLS, anything else STARTTLS
smtp-username - SMTP username
top-repos 10 Top repos in charts/forecasts
track-stargazers false Track individual stargazers
velocity-metrics false Add a growth-velocity section (stars/day, % growth, days to next milestone) to the report
visibility all public, private, all, or owned

[!IMPORTANT] notification-threshold decides when you get an email; compare-against decides what period the report body covers. The two are independent: the threshold accumulates from the Notification Baseline, while the report diffs against a stored snapshot. So a threshold that takes several runs to trip still produces a report covering only the compare-against window. Set them to match if you want the email body to span what the threshold accumulated.

See notification-threshold for how the counter behaves on a fresh data branch, when you raise the value, and on a read-only run.

Every tracking option in this table can also go in the YAML config file, with its key written with either dashes or underscores (include-charts and include_charts are both accepted). The rest are workflow-only and the file ignores them: the credentials and plumbing (github-token, github-api-url, config-path, every smtp-* input, email-from and email-to) and send-on-no-changes. email-theme is a tracking option and works in the file.

Outputs
Output Description
lost-stars Stars lost vs. the comparison baseline. Per run, not cumulative
new-stargazers New stargazers vs. the stored stargazer list, which every writing run rewrites - not driven by compare-against, and 0 when track-stargazers is off
new-stars Stars gained vs. the comparison baseline. Per run, not cumulative
notification-sent true / false - whether an email was actually delivered. should-notify is the decision; this is the outcome
report Full Markdown report
report-csv CSV report (for data pipelines)
report-html HTML report (for email)
report-html-path File path to the HTML report (for large reports / custom mailers)
should-notify true / false - the cumulative threshold signal, driven by notification-threshold and notification-mode (and requires an actual change)
stars-changed true / false - anything changed vs. the comparison baseline (per run)
total-stars Total star count

To email on every N stars, use notification-threshold: 'N' with notification-mode: 'gains' and gate the step on if: steps.tracker.outputs.should-notify == 'true'. Gating on new-stars >= N would require N stars inside a single run, which on a daily schedule almost never happens.

Outputs are set at the very end of a successful run. A run that fails sets none of them, even when it fails after its email went out, as a refused push of the data branch does, so a later step gated on an output does not run.

API Reference: Complete inputs, outputs, and data formats


How It Works

---
config:
  look: handDrawn
  layout: dagre
---
flowchart TD
    trigger(["Workflow trigger"])
    config["Load the configuration: inputs, then star-tracker.yml, then defaults"]
    client["Build the GitHub client from github-token and github-api-url"]
    fetch["List repositories, GET /user/repos, paginated"]
    filter["Resolve the Tracked Set from the filters"]
    empty["Empty report and HTML report, every output zeroed"]
    probe{"Data branch on the remote?"}
    attach["Fetch it, add a worktree"]
    orphan["Create it as an orphan branch with an empty commit"]
    read["Read stars-data.json"]
    measure["Measure the Run: Baseline Snapshot, deltas, new Snapshot, threshold"]
    wantStargazers{"include-charts or track-stargazers?"}
    stargazers["Fetch stargazers with starred_at, one repository at a time"]
    wantDiff{"track-stargazers?"}
    diff["Diff against stargazers.json"]
    reconstruct["Reconstruct the star history, compute the forecast"]
    render["Render Markdown, HTML, CSV, badge and SVG charts"]
    due{"SMTP set, and a Notification due or send-on-no-changes?"}
    send["Send the email"]
    settle["Advance or hold the Notification Baseline"]
    htmlReport["Write the HTML report to RUNNER_TEMP"]
    publish["Write every artefact into the worktree, prune old charts"]
    readOnly{"read-only?"}
    push["Commit and push the data branch"]
    outputs["Set every output"]
    green(["Run ends green"])
    red(["Run fails: Star Tracker failed"])

    trigger --> config --> client --> fetch --> filter
    filter -->|no repositories matched| empty --> green
    filter --> probe
    probe -->|yes| attach --> read
    probe -->|"no, writing run"| orphan --> read
    read --> measure --> wantStargazers
    wantStargazers -->|yes| stargazers --> wantDiff
    wantStargazers -->|no| reconstruct
    wantDiff -->|yes| diff --> reconstruct
    wantDiff -->|no| reconstruct
    reconstruct --> render --> due
    due -->|yes| send --> settle
    due -->|no| settle
    settle --> htmlReport --> publish --> readOnly
    readOnly -->|no| push --> outputs
    readOnly -->|yes| outputs
    outputs --> green

    config -.->|"bad visibility or data-branch, unreadable file"| red
    client -.->|"no token, or an API URL that is not https"| red
    fetch -.->|"any failed request, a 403 rate limit included"| red
    probe -.->|"no checkout, a failed probe, or absent on a read-only run"| red
    attach -.->|"git fetch or worktree add fails"| red
    orphan -.->|"worktree add, checkout --orphan or the empty commit fails"| red
    read -.->|unreadable history| red
    diff -.->|"stargazers.json is not JSON"| red
    push -.->|"push refused, after the email"| red

    classDef start stroke:#1565c0,stroke-width:3px
    classDef setup stroke:#e65100,stroke-width:3px
    classDef measuring stroke:#7b1fa2,stroke-width:3px
    classDef rendering stroke:#2e7d32,stroke-width:3px
    classDef delivering stroke:#c2185b,stroke-width:3px
    classDef fatal stroke:#d73a49,stroke-width:3px,stroke-dasharray:6 4
    class trigger,green start
    class config,client,fetch,filter,empty setup
    class probe,attach,orphan,read,measure,wantStargazers,stargazers,wantDiff,diff,reconstruct measuring
    class render rendering
    class due,send,settle,htmlReport,publish,readOnly,push,outputs delivering
    class red fatal
Loading

The dashed edges end a run red, and nothing after them runs: no later step, and no output is set. Two of them come after a probe that succeeded: fetching the data branch and adding its worktree fails, or creating the orphan does, on a network error or on a leftover .<data-branch> path git cannot reuse. Everything else that can go wrong only thins the run, and it still ends green: a repository whose stargazers cannot be read loses its reconstructed chart and its new-stargazer diff, and a failed email holds the Notification Baseline for the next run. Note where the email sits. It goes out before the data branch is pushed, so a refused push ends red a run that has already emailed you.

How It Works: The full execution pipeline, phase by phase, with what is fatal, degraded or only a warning in each phase. Architecture: the DDD(ish) layering, one ubiquitous language and a pure core, and how much of the tactical catalogue is taken

How the charts read dates

The charts plot the real historical curve: every star is placed on the date it was actually given. Each stargazer carries a starred_at timestamp (GitHub's application/vnd.github.star+json media type), and the action reconstructs the cumulative star count over real time from those dates, so the timeline runs from a repo's very first star up to now, regardless of when you started running the action.

The per-run snapshots on the data branch are still kept for the report's delta tables and notifications ("how many stars changed against the comparison baseline", which compare-against selects), but the charts themselves no longer depend on them.

One caveat: GitHub caps the stargazers listing at roughly 40,000 per repo (oldest first), so on a very large repo the most recent stars fall outside the Reachable Stargazers and the Covered Stars behind the curve stop short of the true total. The reachable portion is drawn accurately and the recent tail is bridged with a straight ramp up to the current total, so the early curve stays honest and the chart never goes flat at the end. Pair this with smart-sampling to keep the request cost bounded on big repos.

The line style is configurable via chart-curve (monotone by default, plus catmull-rom, cubic-bezier and rounded-step), along with theme, colors, milestones, point markers, the time window and more. See the examples gallery for a rendered comparison of every option.


Embedding in Your README

Star Badge

![Stars](https://raw.githubusercontent.com/YOUR_USER/YOUR_REPO/star-tracker-data/stars-badge.svg)

Star History Chart

![Star History](https://raw.githubusercontent.com/YOUR_USER/YOUR_REPO/star-tracker-data/charts/star-history.svg)

Tip

SVG charts automatically adapt to dark and light mode. No extra configuration needed: they use prefers-color-scheme to match the viewer's theme.

Viewing Reports: All access methods (data branch, badges, outputs, email)


Documentation

Guide Description
Getting Started Setup from token to first run
How It Works Execution flow, phase by phase
Architecture DDD(ish) layers, and where the DDD stops
Configuration All options and settings
API Reference Inputs, outputs, and data formats
Examples Real-world workflow configurations
Star Trend Charts Chart types, embedding, and customization
Email Notifications Built-in SMTP and external action setup
Viewing Reports Data branch, badges, outputs, raw data
Data Management Storage, rotation, and manual management
Internationalization Multi-language support
Personal Access Token Classic and fine-grained token setup
Technical Stack Technologies and design decisions
Known Limitations Constraints and workarounds
Troubleshooting Common issues and solutions

For why the action is built the way it is, one decision per file, see the architecture decision records. They include the AGPL-3.0-only licence and what it asks of anyone redistributing or hosting a modified version.


Support & Contributing

If you find this project useful, consider supporting its development:

Sponsor Buy Me A Beer


Use of AI

This project uses AI assistance primarily for documentation purposes. AI tools (GitHub Copilot, Claude) were used to:

  • Write and improve documentation (README, wiki pages)
  • Generate boilerplate code and configuration files
  • Assist with code reviews and suggestions

The core logic, architecture decisions, and implementation were developed by the maintainer. All AI-generated content has been reviewed and validated.


AGPL-3.0 © Made with 🤘🏼 by Ferran Buireu

About

GitHub Action that tracks star counts across your repositories: charts, badges, reports and email notifications when they change

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

16 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages