Skip to content

Run Data Sync

Run Data Sync #357

Workflow file for this run

name: Run Data Sync
on:
workflow_dispatch:
# inputs:
# verificationCode:
# description: 'Joy run verification code'
# required: true
# type: string
schedule:
- cron: '0 0 * * *'
push:
branches:
- master
paths:
- run_page/nike_sync.py
- run_page/nike_to_strava_sync.py
- run_page/strava_sync.py
- run_page/gen_svg.py
- run_page/garmin_sync.py
- run_page/coros_sync.py
- run_page/keep_sync.py
- run_page/gpx_sync.py
- run_page/tcx_sync.py
- run_page/tcx_to_garmin_sync.py
- run_page/garmin_to_strava_sync.py
- run_page/keep_to_strava_sync.py
- run_page/oppo_sync.py
- run_page/intervals_icu_sync.py
- requirements.txt
env:
# please change to your own config.
RUN_TYPE: intervals_icu # support strava/nike/garmin/garmin_cn/keep/only_gpx/only_fit/nike_to_strava/strava_to_garmin/strava_to_garmin_cn/garmin_to_strava/garmin_to_strava_cn/codoon, Please change the 'pass' it to your own
ATHLETE: RealTiny656
TITLE: Running Calendar
MIN_GRID_DISTANCE: 10 # change min distance here
TITLE_GRID: Over 10km Runs # also here
GITHUB_NAME: tiny656
GITHUB_EMAIL: tiny656@hotmail.com
# IGNORE_BEFORE_SAVING: True # if you want to ignore some data before saving, set this to True
IGNORE_START_END_RANGE: 10 # Unit meter
# Dont making this huge, just picking points you needing. https://developers.google.com/maps/documentation/utilities/polylineutility using this tool to making your polyline
IGNORE_POLYLINE: 'ktjrFoemeU~IorGq}DeB'
IGNORE_RANGE: 10 # Unit meter
USE_CLOUDFLARE_WARP: false # If you want to use Cloudflare WARP to bypass network restrictions, set it to `true`. Currently only Garmin needs this.
SAVE_DATA_IN_GITHUB_CACHE: false # if you deploy in the vercel, check the README
DATA_CACHE_PREFIX: 'track_data'
BUILD_GH_PAGES: true # If you do not need GitHub Page please set it to `false`
SAVE_TO_PARQENT: true # If you want to save the data to the repo, set it to `true`
GENERATE_MONTH_OF_LIFE: true # If you want to generate the month of life, set it to `true`
BIRTHDAY_MONTH: 1991-09 # If you want to generate the month of life, set it to your birthday month, format is YYYY-MM
jobs:
sync:
name: Sync
runs-on: ubuntu-latest
outputs:
SAVE_DATA_IN_GITHUB_CACHE: ${{ steps.set_output.outputs.SAVE_DATA_IN_GITHUB_CACHE }}
DATA_CACHE_PREFIX: ${{ steps.set_output.outputs.DATA_CACHE_PREFIX }}
BUILD_GH_PAGES: ${{ steps.set_output.outputs.BUILD_GH_PAGES }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: true
- name: Set up Python
id: setup_python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip
cache-dependency-path: 'requirements.txt'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Install Cloudflare WARP
if: env.USE_CLOUDFLARE_WARP == 'true'
run: |
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
sudo apt-get update && sudo apt-get install -y cloudflare-warp
- name: Connect Cloudflare WARP
if: env.USE_CLOUDFLARE_WARP == 'true'
run: |
warp-cli --accept-tos registration new
warp-cli --accept-tos mode warp
warp-cli --accept-tos connect
sleep 3
echo "WARP status:"
warp-cli --accept-tos status
echo "IP info:"
curl -s https://cloudflare.com/cdn-cgi/trace | grep -E "ip|warp"
- name: Cache Data Files
if: env.SAVE_DATA_IN_GITHUB_CACHE == 'true'
uses: actions/cache@v4
with:
path: |
activities
assets
GPX_OUT
TCX_OUT
FIT_OUT
Workouts
run_page/data.db
src/static/activities.json
imported.json
key: ${{ env.DATA_CACHE_PREFIX }}-${{ github.sha }}-${{ github.run_id }}
restore-keys: |
${{ env.DATA_CACHE_PREFIX }}-${{ github.sha }}-
${{ env.DATA_CACHE_PREFIX }}-
- name: Run sync Nike script
if: env.RUN_TYPE == 'nike'
run: |
python run_page/nike_sync.py ${{ secrets.NIKE_REFRESH_TOKEN }}
- name: Run sync Nike to Strava(Run with nike data backup and show with strava)
if: env.RUN_TYPE == 'nike_to_strava'
run: |
python run_page/nike_to_strava_sync.py ${{ secrets.NIKE_REFRESH_TOKEN }} ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }}
- name: Run sync Keep script
if: env.RUN_TYPE == 'keep'
run: |
python run_page/keep_sync.py ${{ secrets.KEEP_MOBILE }} ${{ secrets.KEEP_PASSWORD }} --with-gpx
- name: Run sync Coros script
if: env.RUN_TYPE == 'coros'
run: |
python run_page/coros_sync.py ${{ secrets.COROS_ACCOUNT }} ${{ secrets.COROS_PASSWORD }}
# If you only want to sync 'type running' add args --only-run, default script is to sync all data (rides , swims, strengths trains, hikes, runs, etc.).
# python run_page/coros_sync.py ${{ secrets.COROS_ACCOUNT }} ${{ secrets.COROS_PASSWORD }} --only-run
- name: Run sync Keep_to_strava script
if: env.RUN_TYPE == 'keep_to_strava_sync'
run: |
python run_page/keep_to_strava_sync.py ${{ secrets.KEEP_MOBILE }} ${{ secrets.KEEP_PASSWORD }} ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} --sync-types running cycling hiking
# If you only want to sync 'type running' modify args --sync-types running, default script is to sync all data (rides, hikes and runs).
- name: Run sync Strava script
if: env.RUN_TYPE == 'strava'
run: |
python run_page/strava_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }}
# If you only want to sync 'type running' add args --only-run, default script is to sync all data (rides and runs).
# for garmin if you want generate 'tcx' you can add --tcx command in the args.
- name: Run sync Garmin script
if: env.RUN_TYPE == 'garmin'
run: |
python run_page/garmin_sync.py ${{ secrets.GARMIN_SECRET_STRING }}
# make garmin secret string 'python run_page/get_garmin_secret.py ${email} ${password}'
# If you only want to sync 'type running' add args --only-run, default script is to sync all data (rides and runs).
# python run_page/garmin_sync.py ${{ secrets.GARMIN_SECRET_STRING }} --only-run
- name: Run sync Garmin CN script
if: env.RUN_TYPE == 'garmin_cn'
run: |
python run_page/garmin_sync.py ${{ secrets.GARMIN_SECRET_STRING_CN }} --is-cn
# make garmin secret string 'python run_page/get_garmin_secret.py ${email} ${password} --is-cn'
# If you only want to sync 'type running' add args --only-run, default script is to sync all data (rides and runs).
# python run_page/garmin_sync.py ${{ secrets.GARMIN_SECRET_STRING_CN }} --only-run --is-cn
- name: Run sync JoyRun script
if: env.RUN_TYPE == 'joyrun'
run: |
python run_page/joyrun_sync.py ${{ secrets.JOYRUN_PHONE_NUMBER }} ${{ github.event.inputs.verificationCode }} --with-gpx
- name: Run sync Garmin CN to Garmin script
if: env.RUN_TYPE == 'garmin_sync_cn_global'
run: |
python run_page/garmin_sync_cn_global.py ${{ secrets.GARMIN_SECRET_STRING_CN }} ${{ secrets.GARMIN_SECRET_STRING }}
# make garmin secret string 'python run_page/garmin_sync_cn_global.py ${email} ${password} --is-cn'
# If you only want to sync 'type running' add args --only-run, default script is to sync all data (rides and runs).
# python run_page/garmin_sync_cn_global.py ${{ secrets.GARMIN_SECRET_STRING_CN }} ${{ secrets.GARMIN_SECRET_STRING }} --only-run
- name: Run sync Only GPX script
if: env.RUN_TYPE == 'only_gpx'
run: |
python run_page/gpx_sync.py
- name: Run sync Only FIT script
if: env.RUN_TYPE == 'only_fit'
run: |
python run_page/fit_sync.py
- name: Run sync Only TCX script
if: env.RUN_TYPE == 'only_tcx'
run: |
python run_page/tcx_sync.py
- name: Run sync Strava to Garmin(Run with strava(or others upload to strava) data backup in Garmin)
if: env.RUN_TYPE == 'strava_to_garmin'
run: |
python run_page/strava_to_garmin_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} ${{ secrets.GARMIN_SECRET_STRING }} ${{ secrets.STRAVA_EMAIL }} ${{ secrets.STRAVA_PASSWORD }}
- name: Run sync Strava to Garmin-cn(Run with strava(or others upload to strava) data backup in Garmin-cn)
if: env.RUN_TYPE == 'strava_to_garmin_cn'
run: |
python run_page/strava_to_garmin_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} ${{ secrets.GARMIN_SECRET_STRING_CN }} ${{ secrets.STRAVA_EMAIL }} ${{ secrets.STRAVA_PASSWORD }} ${{ secrets.STRAVA_JWT }} --is-cn
- name: Run sync Garmin-cn to Strava(Run with Garmin data backup in Strava)
if: env.RUN_TYPE == 'garmin_to_strava_cn'
run: |
python run_page/garmin_to_strava_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} ${{ secrets.GARMIN_SECRET_STRING_CN }} --is-cn
- name: Run sync Garmin to Strava(Run with Garmin data backup in Strava)
if: env.RUN_TYPE == 'garmin_to_strava'
run: |
python run_page/garmin_to_strava_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} ${{ secrets.GARMIN_EMAIL }} ${{ secrets.GARMIN_PASSWORD }}
- name: Run sync Tulipsport script
if: env.RUN_TYPE == 'tulipsport'
run: |
python run_page/tulipsport_sync.py ${{ secrets.TULIPSPORT_TOKEN }} --with-gpx
- name: Run sync Oppo heytap script, note currently this script is not worked
if: env.RUN_TYPE == 'oppo'
run: |
python run_page/oppo_sync.py ${{ secrets.OPPO_ID }} ${{ secrets.OPPO_CLIENT_SECRET }} ${{ secrets.OPPO_CLIENT_REFRESH_TOKEN }} --with-tcx
# If you want to sync fit activity in gpx format, please consider the following script:
# python run_page/oppo_sync.py ${{ secrets.OPPO_ID }} ${{ secrets.OPPO_CLIENT_SECRET }} ${{ secrets.OPPO_CLIENT_REFRESH_TOKEN }} --with-gpx
- name: Run sync Intervals.icu script
if: env.RUN_TYPE == 'intervals_icu'
run: |
python run_page/intervals_icu_sync.py ${{ secrets.INTERVALS_ICU_ATHLETE_ID }} ${{ secrets.INTERVALS_ICU_API_KEY }} --start-date 2026-03-27
# Default syncs running activities only. To sync all types add --all
# To change start date: --start-date 2020-01-01 (default: 2015-01-01)
- name: Run db updater script to add 'Elevation Gain' field to db
if: env.RUN_TYPE == 'db_updater'
run: |
python run_page/db_updater.py
- name: Make svg GitHub profile
if: env.RUN_TYPE != 'pass'
env:
TITLE: ${{ env.TITLE }}
TITLE_GRID: ${{ env.TITLE_GRID }}
ATHLETE: ${{ env.ATHLETE }}
MIN_GRID_DISTANCE: ${{ env.MIN_GRID_DISTANCE }}
run: |
python run_page/gen_svg.py --from-db --title "$TITLE" --type github --github-style "align-firstday" --athlete "$ATHLETE" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github.svg --use-localtime --min-distance 0.5
python run_page/gen_svg.py --from-db --title "$TITLE_GRID" --type grid --athlete "$ATHLETE" --output assets/grid.svg --special-color yellow --special-color2 red --special-distance 20 --special-distance2 40 --use-localtime --min-distance "$MIN_GRID_DISTANCE"
python run_page/gen_svg.py --from-db --type circular --use-localtime
python run_page/gen_svg.py --from-db --year $(date +"%Y") --language zh_CN --title "$(date +"%Y") Running" --type github --github-style "align-firstday" --athlete "$ATHLETE" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github_$(date +"%Y").svg --use-localtime --min-distance 0.5
# if you want use the style:the start day of each year always aligns with Monday. please use the following script:
# python run_page/gen_svg.py --from-db --title "${{ env.TITLE }}" --type github --github-style "align-monday" --athlete "${{ env.ATHLETE }}" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github.svg --use-localtime --min-distance 0.5
# python run_page/gen_svg.py --from-db --year $(date +"%Y") --language zh_CN --title "$(date +"%Y") Running" --type github --github-style "align-monday" --athlete "${{ env.ATHLETE }}" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github_$(date +"%Y").svg --use-localtime --min-distance 0.5
- name: Make month of life
if: env.GENERATE_MONTH_OF_LIFE == 'true'
env:
BIRTHDAY_MONTH: ${{ env.BIRTHDAY_MONTH }}
run: |
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol_running.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Runner Month of Life" --sport-type running
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol_walking.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Walker Month of Life" --sport-type walking
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol_hiking.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Hiker Month of Life" --sport-type hiking
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol_cycling.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Cyclist Month of Life" --sport-type cycling
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Month of Life" --sport-type all
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol_swimming.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Swimmer Month of Life" --sport-type swimming
python run_page/gen_svg.py --from-db --type monthoflife --birth "${{env.BIRTHDAY_MONTH}}" --special-color "#f9d367" --special-color2 "#f0a1a8" --output assets/mol_skiing.svg --use-localtime --athlete "${{env.ATHLETE}}" --title "Skier Month of Life" --sport-type skiing
- name: Make year summary
if: env.GENERATE_MONTH_OF_LIFE == 'true'
run: |
python run_page/gen_svg.py --from-db --type year_summary --output assets/year_summary.svg --athlete "${{ env.ATHLETE }}"
- name: Save data to parqent
if: env.SAVE_TO_PARQENT == 'true'
run: |
pip install duckdb
python run_page/save_to_parqent.py
- name: Disconnect Cloudflare WARP
if: env.USE_CLOUDFLARE_WARP == 'true'
run: |
warp-cli --accept-tos disconnect
- name: Push new runs
if: env.SAVE_DATA_IN_GITHUB_CACHE != 'true'
run: |
git config --local user.email "${{ env.GITHUB_EMAIL }}"
git config --local user.name "${{ env.GITHUB_NAME }}"
git add .
git commit -m 'update new runs' || echo 'nothing to commit'
git push || echo 'nothing to push'
- name: Set Output
id: set_output
env:
SAVE_DATA_IN_GITHUB_CACHE: ${{ env.SAVE_DATA_IN_GITHUB_CACHE }}
DATA_CACHE_PREFIX: ${{ env.DATA_CACHE_PREFIX }}
BUILD_GH_PAGES: ${{ env.BUILD_GH_PAGES }}
run: |
echo "SAVE_DATA_IN_GITHUB_CACHE=$SAVE_DATA_IN_GITHUB_CACHE" >> "$GITHUB_OUTPUT"
echo "DATA_CACHE_PREFIX=$DATA_CACHE_PREFIX" >> "$GITHUB_OUTPUT"
echo "BUILD_GH_PAGES=$BUILD_GH_PAGES" >> "$GITHUB_OUTPUT"
publish_github_pages:
if: needs.sync.result == 'success' && needs.sync.outputs.BUILD_GH_PAGES == 'true'
name: Build and publish Github Pages
uses: ./.github/workflows/gh-pages.yml
with:
save_data_in_github_cache: ${{needs.sync.outputs.SAVE_DATA_IN_GITHUB_CACHE == 'true'}}
data_cache_prefix: ${{needs.sync.outputs.DATA_CACHE_PREFIX}}
needs:
- sync