17 Commits
Author SHA1 Message Date
tiennm99 537c87c458 feat(web): show cards inline for quick viewing and accept the visitor's own token
The web UI is for a quick look at a profile, not for hosting embeddable
images. Cards are inlined into /u/<user> as data: images, the per-card
route is gone, and the page no longer offers copy links or Markdown
snippets. The page itself stays shareable.

Visitors can again paste their own token, with a button that opens
GitHub's new-token page with the needed scopes ticked. A pasted token
takes precedence over sign-in, follows the same ownership, privacy and
cooldown rules, is never stored or logged, and is not revoked. The server
still uses no token of its own.
2026-10-07 19:15:15 +07:00
tiennm99 2def27f49a feat(web): require GitHub sign-in to generate cards
The web UI now runs every job on an OAuth token from "Sign in with
GitHub". The ticked options decide the requested scopes (read:user, plus
repo for private repos, plus read:org for org repos); a grant wider than
requested is refused. The token lives only on the job and is revoked when
the job ends, on every path including shutdown.

The server no longer holds a GitHub token of its own, and the pasted-token
field and /generate are gone. -serve requires -oauth-client-id,
-oauth-client-secret and -public-url (GHGLANCE_OAUTH_* and
GHGLANCE_PUBLIC_URL), and compose.yml requires them too. The CLI and the
Action keep -token unchanged.
2026-10-07 17:50:33 +07:00
tiennm99 ad775adffa feat(web): add web UI and Coolify compose deployment
`ghglance -serve :8080` runs a web UI from the same binary: submit a
username, a background job renders every card in every theme, and
/u/<user> shows them again with a theme picker and embed URLs.

- Cards are stored on disk with atomic symlink publishing and deleted
  after -retention (default 24h); -cooldown limits token-less regeneration.
- Token-less jobs use a public-only server token with private and org
  scope forced off; a submitter's own token is used for that job only and
  never stored or logged. The form links to GitHub's new-token page with
  the needed scopes pre-ticked.
- The CLI and web share one fetch helper; CLI output is unchanged.
- compose.yml deploys to Coolify with a data volume and /healthz check.
2026-10-07 11:08:07 +07:00
tiennm99 3fc8a59de6 chore: rename the project to ghglance
The Marketplace rejects ghstats as an action name, so the repo, Go module,
binary, and action share the name ghglance instead. The demo workflow keeps
reading the existing GHSTATS_TOKEN repo secret.
2026-10-03 13:38:15 +07:00
tiennm99 7c081ac591 feat: let commits-per-repo 0 mean every commit
The pagination guard broke on seen >= maxPerRepo, so a cap of 0 stopped
before the first page and rendered empty productive-time,
productive-weekday and most-commit-language cards. That inverted the
meaning 0 carries for top-repos, where it already means unlimited.

Treat a cap of 0 or less as no cap. Verified against a real repo: a cap
of 100 still stops at 100, while 500 and 0 both walk the full 382-commit
history.
2026-08-13 14:19:41 +07:00
tiennm99 30ba70d8ea feat: count org repos and fix contribution-window truncation
Split each contribution year into quarterly windows.
commitContributionsByRepository caps at 100 repos per query and drops
the remainder without signalling it, which a prolific year hits: 2026
returned exactly 100 where the quarterly union returns 108. Warn when a
window still comes back at the ceiling instead of reporting a complete
list.

Add -include-org-repos / include_org_repos so org-owned repos the user
administers count toward stars, repo count, repos-per-language and
top-starred. Off by default, since enabling it moves those totals for
every existing caller. Commit-driven cards already covered org repos
through the seed list. Org repos carrying only read or write access are
dropped via viewerPermission.

Rename Profile.PublicRepos to RepoCount and drop "public" from the card
label: the count tracks the fetch filters and includes private repos
whenever the token can see them.
2026-08-13 13:20:41 +07:00
tiennm99 92387eddf5 feat(card): configurable start of week (#25)
* feat(card): configurable start of week for heatmap + weekday cards

New -start-of-week CLI flag (and start_of_week action input) rotates the
contribution-heatmap rows and the productive-weekday bars so users whose
calendars start on Monday (or any other day) get matching output. Default
stays Sunday to preserve existing renders.

* docs: note start-of-week in design-guidelines, codebase-summary, deployment-guide

- design-guidelines: heatmap row order + productive-weekday bar order now derive from Profile.WeekStart
- codebase-summary: list new weekday_start_test.go cases + TestParseWeekday
- deployment-guide: mention start_of_week as an optional action input in the workflow template
2026-04-21 20:57:21 +07:00
tiennm99 0e0e4fc500 fix(card): productive titles render at 15 px for every timezone (#24)
Two cuts together keep the productive-time and productive-weekday
titles at the same 15 px the rest of the gallery uses:

1. utcOffsetLabel is now compact. Integer offsets drop the '.00'
   padding ("UTC+7" vs the old "UTC+7.00") and non-integer offsets use
   the colon form ("UTC+5:30", "UTC+5:45"). Saves 3 chars for the
   common integer case, so "Commits by Hour (last year, UTC+7)" lands
   at 34 chars — inside the 15 px budget.

2. weekdayTitle no longer embeds the UTC label. Day-of-week aggregates
   by whole days; clock precision isn't informative there, and
   dropping it shortens the title to 30 chars so the full 15 px lands
   for every timezone.

Quarter-hour-zone users (Kathmandu UTC+5:45) see the hour title drop
to 14 px — 37 chars still exceeds the budget — but that's a rare
case and only 1 px off.

TestUTCOffsetLabel updated to the new format. TestFitTitleFontSize
pinned the new titles.
2026-04-19 11:49:40 +07:00
tiennm99 729f1a0611 Revert "chore: rename module + references to tiennm99/ghstats-cards"
This reverts commit accc4b6. 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.
2026-04-18 23:51:33 +07:00
tiennm99 accc4b62b2 chore: rename module + references to tiennm99/ghstats-cards
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.
2026-04-18 23:40:29 +07:00
tiennm99 27c8014270 feat(client): thread context.Context and honor rate-limit backoff
- 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.
2026-04-18 22:43:50 +07:00
tiennm99 d3fc27f33f feat: flip include-forks / include-private defaults to true
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".
2026-04-18 22:20:46 +07:00
tiennm99 2943db667c feat: seed-based repo scanning + fork/private visibility flags
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.
2026-04-18 22:01:26 +07:00
tiennm99 50b29b4a78 feat: add all-time variants of productive, language, contribution cards
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.
2026-04-18 21:40:46 +07:00
tiennm99 7eb83be9de refactor: split language card into repos-per-language and most-commit-language
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.
2026-04-18 18:57:42 +07:00
tiennm99 ff4975fae2 feat: implement profile summary cards with GraphQL fetch and Action wrapper
- 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.
2026-04-18 18:39:51 +07:00
tiennm99 a91d964a1d feat: scaffold ghstats — Go CLI for GitHub profile SVG cards 2026-04-18 18:22:20 +07:00