Turn your Strava and Garmin activities into GitHub-style contribution heatmaps.
Automatically generates a free, interactive dashboard updated daily on GitHub Pages.
No coding required.
- View the Interactive Activity Dashboard
- Once setup is complete, this dashboard link will automatically update to your own GitHub Pages URL.
bash <(curl -fsSL https://raw.githubusercontent.com/aspain/git-sweaty/main/scripts/bootstrap.sh)You will be prompted for:
- Setup mode:
- Local: setup script will create the fork, clone the repo, and complete the rest of the setup.
- Online (default): no local clone; setup script will configure a fork or an existing writable repo
- GitHub Pages custom domain (if you have one, for example
strava.example.com) - Source (
stravaorgarmin) - Unit preference (
USorMetric) - Heatmap week start (
SundayorMonday)
The setup may take several minutes to complete when run for the first time. If any automation step fails, the script prints steps to remedy the failed step.
Once the script succeeds, it will provide the URL for your dashboard.
- To pull in new updates and features from the original repo, use GitHub's Sync fork button on your fork's
mainbranch. - Activity data is stored on a dedicated
dashboard-databranch and deployed from there mainis intentionally kept free of generateddata/andsite/data.jsonartifacts so fork sync process stays cleaner.- After syncing, manually run Sync Heatmaps if you want your dashboard refreshed immediately. Otherwise updates will deploy at the next scheduled run.
You can switch between strava and garmin any time.
- Re-run
./scripts/bootstrap.shand choose a different source. - If you re-run setup and choose the same source, setup asks whether to force a one-time full backfill for that run.
Everything in this section is optional. Defaults work without changes.
Base settings live in config.yaml, and config.local.yaml overrides them when present.
Auth + source settings:
source(stravaorgarmin)strava.client_id,strava.client_secret,strava.refresh_tokengarmin.token_store_b64,garmin.email,garmin.passwordgarmin.strict_token_only(whentrue, Garmin sync requiresgarmin.token_store_b64and does not fall back to email/password auth)
Sync scope + backfill behavior:
sync.start_date(optionalYYYY-MM-DDlower bound for history)sync.lookback_years(optional rolling lower bound; used only whensync.start_dateis unset)sync.recent_days(sync recent activities even while backfilling)sync.resume_backfill(persist cursor so backfills continue across scheduled runs)sync.per_page(page size used when fetching provider activities; default200)sync.prune_deleted(remove local activities no longer returned by the provider; pruning only happens on runs that perform a full backfill scan)
Activity type behavior:
activities.types(featured order in UI, and acts as allowlist whenactivities.include_all_typesisfalse)activities.include_all_types(whentrue, include all seen sport types; whenfalse, include onlyactivities.types)activities.exclude_types(explicit type exclusions, even wheninclude_all_typesistrue)activities.type_aliases(map raw provider type names to canonical type names before grouping/filtering)activities.group_aliases(map canonical type names to explicit grouped labels)activities.group_other_types(whentrue, non-featured types are grouped into broader buckets; repo default isfalse)activities.other_bucket(fallback group name when grouped type matching has no hit)
Display + rate-limit settings:
units.distance(miorkm)units.elevation(ftorm)heatmaps.week_start(sundayormonday)rate_limits.*(Strava API pacing caps used by sync; ignored for Garmin)
- Raw activities are stored locally for processing but are not committed (
activities/raw/is ignored). This prevents publishing detailed per-activity payloads and GPS location traces. - If neither
sync.start_datenorsync.lookback_yearsis set, the sync workflow backfills all available history from the selected source (i.e. Strava/Garmin). - Strava backfill state is stored in
data/backfill_state_strava.json; Garmin backfill state is stored indata/backfill_state_garmin.json. If a backfill hits API limits (unlikely), this state allows the daily refresh automation to pick back up where it left off. - The Sync action workflow includes a toggle labeled
Reset backfill cursor and re-fetch full history for the selected sourcewhich forces a one-time full backfill. This is useful if you add/delete/modify activities which have already been loaded. - The GitHub Pages site is optimized for responsive desktop/mobile viewing.
- If a day contains multiple activity types, that day’s colored square is split into equal segments — one per unique activity type on that day.
