The dracula sample under output/dracula/ was a reference render for the
README. Now that demo/ auto-generates every card × every theme on each
push, the sample is redundant — and keeping it trackers means every
behavioral tweak shows up as a diff in committed SVGs.
- Delete output/dracula/*.svg.
- .gitignore: collapse `output/*` + `!output/dracula/` to a plain `output/`.
- demo.yml: drop `output/**` from paths-ignore (no longer part of any push).
- README / docs: point readers at demo/ instead of output/dracula/.
Five new cards, all derived from data FetchProductive / FetchProfile already
pull, so zero additional API calls:
- contributions-heatmap: 7×53 calendar grid with a 5-bucket intensity ramp
mixed from each theme's Background→Accent so palettes with no dedicated
heat ramp still render sensibly.
- streak: current streak, longest streak with date ranges, active/total days.
- contributions-by-year: one bar per active year, peak year highlighted.
- productive-weekday + -all-time: 7-bar day-of-week mirror of the hour-of-day
cards; FetchProductive now also fills Weekday / WeekdayAllTime histograms
during the same commit-history pass.
- top-starred-repos: top 5 owned non-fork repos by stargazer count; threads
Stars through RepoInfo.
Card count: 9 → 14. Registered in allCards grouped by recency (last-year
block, then all-time block). Render test extended to cover all new files
and realistic daily-series inputs.
- README points to demo/ as an auto-generated theme preview.
- Bump "60+ / 61 themes" references to the actual 65.
- Record Phase 7.5 in roadmap and list demo.yml + demo/ in layout.
Runs on push to main (skipping its own demo/output/markdown commits),
builds the ghstats binary, renders every theme for the repo owner,
and writes a demo/README.md gallery back to main.
Adds an update-major-tag job that runs after test/docker/binaries all
pass. Extracts the major component from the pushed tag (v1.2.3 → v1)
and force-moves that tag to the current commit, then pushes.
Consumers using `uses: tiennm99/ghstats@v1` now pick up each new
minor/patch release automatically without a workflow edit on their
end. The v1 tag only moves if every artifact job succeeded, so it
never points at a commit whose binaries or Docker image failed to
publish.
This reverts commit 399a3dc. Repo stays as tiennm99/ghstats; the
Marketplace display name ("ghstats-cards" in action.yml) is the only
place the new name remains, since that field requires uniqueness on
the Marketplace.
plans/reports/* were added alongside the rename in the same commit
and are preserved by not deleting them in this revert.
Matches the Marketplace name; repo is being renamed in lockstep.
- go.mod module path: github.com/tiennm99/ghstats →
github.com/tiennm99/ghstats-cards
- Import paths across every .go file updated.
- README badges, install snippets, and the 'go install' line point
to the new URL/path.
- docs/deployment-guide.md workflow template, Docker image path, and
release edit URL updated.
Breaking for consumers pinned to the old URL; they need to swap
tiennm99/ghstats → tiennm99/ghstats-cards in workflows and switch
Docker pulls to ghcr.io/tiennm99/ghstats-cards. GitHub's HTTP
redirect covers git clones but GHCR does NOT redirect — users must
update image URIs manually.
- Marketplace / Release / License badges at the top.
- Explicit Marketplace + source links under the intro so visitors
arriving from the Marketplace page can jump to the repo and vice
versa.
Also set repo topics via `gh repo edit`: ghstats, ghstats-cards,
github-action, github-marketplace, profile-readme, profile-stats,
stats-cards, svg-cards, go, github-stats.
"ghstats" is already taken by an existing action/user/org on the
GitHub Marketplace, blocking publication. Rename the action's Marketplace
display name to ghstats-cards (still more descriptive of what it
produces). The repo URL, Docker image tag, and import path stay
tiennm99/ghstats — only the Marketplace listing label changes.
- Fetcher signatures across codebase-summary and system-architecture
now show the ctx-first arguments and document the rate-limit retry
loop in Client.query.
- Attribution pseudo-code hoists the per-repo total out of the commit
loop to match the current implementation (I6).
- Failure-modes table enumerates primary rate-limit retry, per-year
nil-user warn, and -timeout / Ctrl-C cancellation.
- design-guidelines notes the single-slice donut special case.
- deployment-guide's release section documents the new test gate and
the SHA-pinned Docker/GHA actions; troubleshooting adds the
rate-limit-reset-too-long error. Rate-limit section describes the
sleep-and-retry policy and -timeout flag.
- project-roadmap records Phase 6 (code-review remediation) as done,
renumbers later planned phases, links the new review report.
- I4 — TestRenderAll now seeds Name/Company with XML-significant chars
that actually hit the render pipeline via cardTitle(). Previous test
checked Bio which is no longer rendered, so the assertion was vacuous.
- New TestDonutSingleSlice guards against the I1 empty-arc regression:
asserts the single-slice path emits <circle> primitives and not the
degenerate A-command path.
- New TestDonutEmpty covers the zero-stats fallback.
- New TestUTCOffsetLabel pins the UTC±N.NN format across UTC,
Asia/Saigon, Asia/Kolkata (half-hour), Asia/Kathmandu (quarter-hour).
Catches %+.2f regressions.
- N2 — Client.query, FetchProfile, FetchContributionsAllTime, and
FetchProductive now take context.Context. main.go builds a root
context with a -timeout flag (default 30m) and cancels on
SIGINT/SIGTERM so Ctrl-C aborts in-flight pagination cleanly.
- N6 — on HTTP 429 or 403 with X-RateLimit-Remaining: 0, the client
now parses Retry-After / X-RateLimit-Reset, sleeps (capped at 5
minutes), and retries once. Longer reset windows surface as errors
so the caller can reschedule the Action rather than burn 55 min of
runner time idle.
N8 — the field is set from contributionsCollection.contributionCalendar,
a rolling last-year window, not a lifetime total. Rename + updated
comment makes the semantics explicit so future readers don't confuse
it with TotalCommitsAllTime.
- I1 — donut chart with a single slice (100%) now renders via two
concentric <circle> elements instead of a degenerate SVG arc that
drew nothing. Reproduced with a standalone probe; regression test
added separately.
- I2 — FetchContributionsAllTime logs a warn to stderr when a year's
query returns nil user data so callers notice partial results
instead of rendering an empty all-time card silently.
- I6 — attributeCommit() receives the repo's byte total precomputed
once per repo rather than re-summing language edges for every
commit in the inner loop.
- I3 — update FetchOptions doc to describe zero-value vs CLI-flag defaults.
- I5 — release workflow gates docker/binaries on a test job; tags no
longer ship broken artifacts.
- N1 — replace handwritten joinErrs with strings.Join.
- N3 — truncate() now backs up to a UTF-8 rune boundary so error
messages never end on a split codepoint.
- N4 — pin Docker base images (golang:1.26-alpine, alpine:3.21) to
SHA256 digests.
- N5 — pin third-party GitHub Actions to commit SHAs with version
comments for readability.
- N9 — drop the "(non-fork)" qualifier from the stats card label; the
underlying GraphQL doesn't actually filter forks, so the phrasing
was misleading.
Most users reach for ghstats to show their real activity. Making the
two visibility toggles opt-out rather than opt-in gives accurate stats
out of the box.
- -include-forks false → true
- -include-private false → true
- include_forks input false → true
- include_private input false → true
Private-repo data silently no-ops when the token lacks repo scope, so
the default is safe for default github.token runs — it just won't
surface more than read:user already allows. Users who want the old
public-only behavior pass -include-private=false or set the Action
input to "false".
Files now land at output/<theme>/profile-details.svg etc., without
leading 0-8 prefixes. README authors embed cards by name, so the
lexicographic-sort rationale for the prefix no longer applies.
- All Filename() methods + the allCards ordering comment updated.
- Tests updated to expect the 9 unnumbered filenames.
- README, deployment-guide, codebase-summary, roadmap references
refreshed.
- Dracula sample SVGs regenerated under new names.
Replace the top-10-starred sampling with a seed list built from
contributionsCollection.commitContributionsByRepository, unioned across
every active contributionYear. Commit-history probes now land only on
repos where the user actually committed, covering all owned repos plus
forks and repos owned by others when allowed.
New visibility knobs (default off — public-facing READMEs stay
safe):
- -include-forks / include_forks : include forked repos
- -include-private / include_private: include private repos (requires
PAT with repo scope)
Compatibility:
- -top-repos default changed 10 → 0 (unlimited); still usable as a cap
for fast local runs.
- commitHistoryQuery now takes $owner so probes can target forks or
repos outside the user's ownership.
- FetchProfile now accepts FetchOptions; PublicRepos counts only repos
that pass the visibility filter.
Seed-list approach mirrors github-profile-summary-cards' own repo
sourcing but keeps our byte-weighted commit attribution.
Four time-bounded cards ("last year") now have all-time counterparts, and
the stats card gains a lifetime commits row.
New cards:
- 6-most-commit-language-all-time.svg (byte-weighted, all lifetime commits)
- 7-productive-time-all-time.svg (hour histogram over all lifetime commits)
- 8-contributions-all-time.svg (area chart spanning every active year)
Data pipeline:
- Drop the "since" filter from commitHistoryQuery; FetchProductive now
paginates unbounded commits and splits each commit into last-year and
all-time buckets in a single pass — no extra API calls.
- New contributionYearQuery iterates user.contributionYears to
concatenate calendar data and accumulate TotalCommitsAllTime.
- -commits-per-repo default bumped 100 → 500 to give all-time depth.
Polish:
- Productive-time title embeds the configured tz as "UTC±N.NN" (e.g.
UTC+7.00) on both last-year and all-time cards.
- Contribution x-axis flipped to mm/yy with an "mm/yy" footer caption
paralleling productive-time's "hour of day".
- Contribution x-axis label stride now targets ~6 labels regardless of
bucket count so the all-time chart (~100 months) stays readable while
the underlying curve still samples every month.
Narrow the output/ gitignore so output/dracula/*.svg ships with the
repo — these are the preview images rendered into the README.
Other themes stay ignored; rebuild them locally as needed.
New 5-contributions.svg renders the last year's contribution calendar
as a monthly smooth-filled area chart. Pure Go SVG; no extra API calls
— one additional contributionCalendar.weeks block in the existing
profile GraphQL query carries the data.
- Y-axis mirrored on both sides with nice ticks.
- X-axis labels in YY/MM format, every other month to avoid overlap.
- Smooth curve via Catmull-Rom interpolation converted to cubic Bezier
(d3.curveCatmullRom default tension 0.5).
- Missing months between first and last are inserted as zero-count so
the chart stays time-continuous.
Replace the 2x3 grid with one row per stat: icon + label + right-aligned
value. Icons (star, commit, PR, issue, code-review, repos) mirror the
Octicons used by github-profile-summary-cards' stats card.
Each commit now contributes a full "vote" partitioned across the repo's
languages proportional to linguist byte counts, instead of crediting only
the primary language. A 60% Go / 40% Python repo adds 0.6 to Go and 0.4
to Python per commit.
- RepoInfo gains []LangEdge capturing the full byte breakdown already
returned by profileQuery.
- FetchProductive distributes each commit via a fixed-point scaleFactor
(int64 preserved, percentages unchanged in the card).
- Fallback to primary language only when linguist reports zero bytes
(empty repo).
Caveat: linguist excludes prose languages (Markdown, AsciiDoc, reST) from
its byte output, so Markdown-heavy repos still skew toward the detected
code fraction. Fixing that case requires per-commit file classification
via REST /commits/{sha} + go-enry — tracked as future work.
- Title becomes "login (Name)" (or just "login") instead of
"Name's Profile Details".
- Row labels replaced with Octicon glyphs (repos, company, location,
link, clock, people) rendered in the theme's muted color.
- Joined and account-age rows merge into a single "YYYY-MM-DD (N years
ago)" line; years round down to whole years with month/day fallbacks.
- Drop profile-summary-for-github from README credits — no code from
that project is used. github-profile-summary-cards is the only direct
reference.
- Replace the previous 30-theme list with the complete 61-theme palette
from github-profile-summary-cards (snake_case ids, e.g. github_dark,
nord_bright, solarized_dark).
- Add Stroke and StrokeOpacity fields to Theme; card frame now uses the
theme's own stroke instead of a hardcoded white overlay so light themes
render a visible border.
- Drop the github-readme-stats credit from README; all palette data now
comes from one upstream source.
- Productive time is now a 24-hour bar chart with axes and nice tick labels
instead of a 7x24 heatmap. Model Productive field reshaped from
[7][24]int to [24]int.
- Language cards render as donut charts with a left-side legend instead of
a stacked bar. Slices beyond top-6 collapse into an "Other" row.
- Add niceTicks helper (1/2/5 * 10^k ladder, d3-style) for axis ticks.
- Legacy language_bar.go removed.
Align card set with github-profile-summary-cards' 5-card layout:
0-profile-details.svg (unchanged)
1-repos-per-language.svg (new) owned repos grouped by primary language
2-most-commit-language.svg (new) last-year commits attributed to each repo's primary language
3-stats.svg (renumbered)
4-productive-time.svg (renumbered)
- FetchProductive now fills p.CommitsByLanguage from the same commit history
it uses for the heatmap, so no extra API calls are introduced.
- TopRepos carries primary language so productive-time can aggregate by lang.
- LangStat.Bytes renamed to Value (repo count or commit count, context-dependent).
- Shared bar+legend renderer extracted to language_bar.go.
- Ignore generated output/ directory.
- Add GraphQL client fetching profile, stats, language aggregation, and
per-repo commit histograms for the productive-time heatmap.
- Render real SVG cards (profile details, top languages, stats grid,
weekday×hour heatmap) with XML escaping and thousands-formatted numbers.
- Expand theme palette to 30 built-ins ported from github-readme-stats;
add -list-themes, multi-theme rendering, and 'all' shortcut.
- Package as Docker-based GitHub Action (action.yml, Dockerfile,
entrypoint.sh) with optional auto-commit of generated cards.
- Release workflow publishes GHCR image and cross-platform binaries on
v* tags.
- Unit tests cover rendering, XML escape, number formatting, language sort.