docs: self-host the star history chart - #717
Merged
Merged
Conversation
The README embed pointed at api.star-history.com with a sealed token. That endpoint now returns 403 for this repo: GitHub restricted the stargazers API to a repository's admins and collaborators in June 2026, and the wrapped credential no longer clears that bar. Regenerating the token would work, but the permission GitHub accepts is Contents: Read and write. That is a credential which can push to this repo, decrypted by a third party on every README view, while any v* tag publishes ghcr.io latest. Render the chart from a scheduled workflow instead: the per-run Actions token holds the same permission but expires with the job and never leaves GitHub. Charts are committed to the orphan `assets` branch because `main` requires pull requests and a daily refresh is not worth a daily PR.
Review of the initial commit surfaced four renderer defects and two workflow gaps. Renderer: the y axis produced fractional gridlines at low counts (10 stars rendered 0/2.5/5/7.5/10), day-and-month x ticks repeated the same label once the window crossed a year (Dec 31 twice at a 729 day span), downsample divided by zero at a limit below two, and the tick format switched to scientific notation past a million. nice_step is now floored at 1 with integer factors, the axis falls back to month-and-year when the window spans two calendar years, and mypy strict passes on the file. Workflow: a 200 carrying an empty or truncated array is a success to gh, so set -e let it through and the renderer published a "No stars yet" placeholder over a good chart, unattended, on a public README. The fetch step now refuses to publish on zero rows or on a count that has halved against the published chart. Runs are pinned to this repository so a fork dispatch cannot fail on the missing assets branch, and the publish step amends rather than appends so the orphan branch stays one commit instead of accruing megabytes of unread history. Adds tests covering the pure functions; all four renderer defects fail against the previous revision.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Renders the README star history chart from a scheduled workflow instead of a
hosted embed, so it no longer depends on a GitHub credential living in a
public README.
The previous embed returns HTTP 403: GitHub restricted the stargazers API to a
repository's admins and collaborators in June 2026, and the sealed token wraps
a credential that no longer clears that check. Regenerating it is possible, but
the permission GitHub accepts is
Contents: Read and write, which means acredential that can push to this repo, sitting in a public README and decrypted
by a third party on every view. The per-run Actions token holds the same
permission and expires with the job.
Closes #716
Changes
scripts/star_history.py, a stdlib-only renderer that readsstarred_attimestamps on stdin and writes light and dark SVGs. Colourscome from
website/src/css/tokens.cssso the chart matches the docs site.Star Historyworkflow, running daily at 07:23 UTC and on demand.It reads stargazers with the per-run Actions token and publishes to the
orphan
assetsbranch, amending rather than appending so the branch staysat one commit. Identical input renders byte-identical output, so a day with
no chart movement produces no commit at all.
carrying an empty array is a success to
gh, and rendering one would put a"No stars yet" placeholder over the real chart unattended.
assetsand remove the sealed token.api.star-history.comlychee exclusion, now unreferenced.The chart lives on a separate branch because
mainrequires pull requests,and a daily refresh is not worth a daily PR.
Testing
tests/test_star_history.pycovers the pure functions: integer-only starticks, unique date labels across a year boundary, downsample endpoint
preservation, and well-formed XML for empty, single-star, same-instant, and
large inputs. Four of these fail against the first revision of the renderer.
Rendered against the live stargazer data (262 stars) and checked both themes.
Both raw URLs return 200 with
image/svg+xml. Full suite is 2803 passing,actionlintis clean on the new workflow, and lychee extracts and checks allthree chart URLs rather than skipping the host.
The workflow itself cannot run until it is on the default branch, so it wants
a manual dispatch after merge to confirm the fetch step in a real run.
Type of Change
fix:)feat:)refactor:)docs:)ci:)chore:)Checklist
Closes #Ntype:*and onepriority:*labelphase:*label (or none when not roadmap work)feat/<slug>,fix/<slug>, etc.)mypy src/; the new script is also clean under it)ruff check .)ruff format --check .)media in a controlled way — N/A, README only