Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
![License](https://img.shields.io/badge/license-GPLv3-green)
![No Cloud](https://img.shields.io/badge/cloud-none-brightgreen)
![Release](https://img.shields.io/github/v/release/Wewoc/Garmin_Local_Archive)
[![Tests](https://github.com/Wewoc/Garmin_Local_Archive/actions/workflows/test-suite.yml/badge.svg)](https://github.com/Wewoc/Garmin_Local_Archive/actions/workflows/test-suite.yml)
[![Garmin Local Archive MCP server – quality and maintenance score on Glama](https://glama.ai/mcp/servers/Wewoc/Garmin_Local_Archive/badges/score.svg)](https://glama.ai/mcp/servers/Wewoc/Garmin_Local_Archive)

**Archive and analyze your Garmin Health data local.**
Expand Down Expand Up @@ -64,7 +65,7 @@ account. See [QUICKSTART.txt](src/docs/QUICKSTART.txt), "Try it first".

**Scope & limitations:** Local-first, personal use, no enterprise ambitions.
- Relies on python-garminconnect. Since Garmin does not offer a public API for personal use, moste non-commercial tools share this dependency. To mitigate unannounced upstream changes, GLA automatically detects and logs structural API shifts.
- Local test suites cover the full pipeline plus a separate build-output validation suite — no automated build/test CI yet; CodeQL security scanning runs via GitHub Actions on every push/PR to main
- Local test suites cover the full pipeline plus a separate build-output validation suite. The full suite also runs automatically via GitHub Actions on every push to main (Windows runner), alongside dead-code and CVE-relevance checks; CodeQL security scanning runs on every push/PR to main
- HTML dashboards require a one-time internet connection to download Plotly (~3 MB) — cached locally after that
- Per-day checkpointing: an interrupted sync resumes from the last completed day, no full re-sync required
- Historical data quality depends on Garmin servers
Expand Down Expand Up @@ -340,7 +341,7 @@ python tests/test_updater.py # T2 + T3 self-updater (v1.7.2.4,

`build_all.py` runs `test_local.py`, `test_local_context.py`, `test_dashboard.py`, `test_broker.py`, and `test_static.py` as pre-build gates — a failing test aborts the build before either target is built. `test_build_output.py` and `test_app_logic.py` run automatically after both builds complete, as post-build gates. `test_qt_app.py` and the six Chat-tab/MCP-process suites above are run manually via `pytest`.

GUI changes are verified manually before release. Full CI/CD with automated builds and release packaging is planned for a later version.
GUI changes are verified manually before release. Automated builds and release packaging run via a manually-triggered GitHub Actions workflow (`build-release.yml`) — not on every push, kept as a deliberate one-click step so not every commit cuts a release.

---

Expand Down
100 changes: 100 additions & 0 deletions src/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,105 @@
# Garmin Local Archive — Changelog

## v1.7.3.4 — CI/CD: automated tests, dead-code/CVE gates, manual release build

First real automated CI/CD for the repo — closes the "Test suite &
CI/CD" item that had sat under ROADMAP.md's "Under consideration"
since it was deliberately deferred with no timeline.

**New GitHub Actions workflows (all on `main`, ubuntu-latest unless noted):**
- `test-suite.yml` — runs `run_tests.ps1` (all 15 suites) on
`windows-latest` on every push to `main`. `run_tests.ps1` itself
never sets a non-zero exit code on a failing suite (no
`$LASTEXITCODE` check anywhere in the script) — added a post-step
that parses `test_all_log.txt`'s per-suite summary and fails the job
if any suite reports a failure, so the check is actually meaningful
rather than always green. Not a merge gate by design (pushes to
`main` are already tested locally first) — purpose is public
transparency, real results visible on every push.
- `vulture-check.yml` — gates on `check_vulture.py`'s exit code
(dead-code findings after whitelist filtering). Precondition — full
triage of the Vulture TODO list — already satisfied by v1.7.3.3.
- `cve-check.yml` — report-only (mirrors `license-scan.yml`'s
artifact-upload pattern); `check_cve_whitelist.py` always exits 0 by
design, so this can never fail the job, only surface findings. Runs
on push plus a weekly cron (new CVEs can surface without a code
change).
- `build-release.yml` — `workflow_dispatch`-only (manual, one click —
not every push should cut a release). Runs the full local build
pipeline unchanged (`build_all.py`'s tests + Target 2 + Target 3 +
post-build validation) via a new CI-only entry point,
`compiler/build_all_github.py`; only on success, publishes a GitHub
Release (tag `vX.Y.Z` from `version.py`) with the four build outputs
as assets. Release notes default to the HEAD commit's message body,
overridable via a workflow input; a second input picks
pre-release/latest.
- `license-scan.yml` trigger switched from `workflow_dispatch`-only to
push/`main`, matching the other three.

**Bug fixed (pre-existing, surfaced by the new Vulture CI gate):**
`vulture_whitelist.py`'s keys use Windows backslash path separators
(the file's own docstring says so — it was built from a local Windows
run), but Vulture on the Linux CI runner reports forward slashes.
`check_vulture.py`'s `filter_whitelisted()` did an exact-tuple match,
so every whitelist entry with a subdirectory silently failed to match
on Linux — the first CI run showed 60 "new" findings that were all
already-documented false positives. Fixed by normalizing both sides to
forward slashes before comparing; verified not to change matching
behavior on Windows (backslash→slash cancels out identically on both
sides).

**Two Windows cp1252 encoding bugs found on the first real
`build-release.yml` run (v1.7.3.3 test pre-release, since deleted):**
the release notes (derived from `git log -1 --pretty=%B`) had em-dashes
mangled to `—` because the subprocess call didn't force UTF-8
decoding, and the final success `print()` (uses a ✓) crashed with
`UnicodeEncodeError` on the Windows console — cosmetic (the Release and
all 4 assets had already published successfully) but made a fully
successful run report red. Both fixed; not otherwise caught because the
build itself had never run in CI before.

**`build_all.py` refactored (no behavior change):** script body moved
from `if __name__ == "__main__":` into a `main()` function so it can be
imported by `build_all_github.py` — a local `python build_all.py` run
is unaffected. `build_venv.py::ensure_build_venv()` now honors an
optional `GLA_BUILD_VENV_DIR` env var, falling back to the existing
hardcoded `build_manifest.BUILD_VENV_DIR` (`D:\Garmin\.venv_gla`) when
unset, so CI builds in its own venv without touching local dev
machines' path.

**Verified, not merged as permanent automation:** a `workflow_dispatch`
probe confirmed `D:\Garmin\.venv_gla` (the hardcoded local build-venv
path) works unmodified on a `windows-latest` GitHub runner — D: drive
present and writable, venv creation + `requirements.txt` +
PyInstaller install all succeeded. Same probe-first approach used to
confirm `test_qt_app.py` (pytest-qt) runs cleanly on `windows-latest`,
even without `QT_QPA_PLATFORM=offscreen` (kept set anyway in
`test-suite.yml`/`build-release.yml` for robustness) — GitHub's Windows
runners have a working desktop session, unlike Linux runners.

**New modules:**
- `.github/workflows/test-suite.yml`, `vulture-check.yml`,
`cve-check.yml`, `build-release.yml`
- `compiler/build_all_github.py`

**Changed modules:**
- `tests/check_vulture.py` — `filter_whitelisted()` normalizes path
separators before comparing (see bug above)
- `compiler/build_all.py` — body wrapped in `main()`, no behavior
change
- `compiler/build_venv.py` — optional `GLA_BUILD_VENV_DIR` env
override in `ensure_build_venv()`
- `.github/workflows/license-scan.yml` — trigger changed from
`workflow_dispatch` to push/`main`
- `version.py` — `1.7.3.3` → `1.7.3.4`

**Test result:** all 15 suites green via `test-suite.yml` on
`windows-latest` CI (includes `test_static.py`'s ruff + bandit checks,
0 errors / 0 HIGH). `vulture-check.yml` green (0 findings after
whitelist). `cve-check.yml` report-only, no blocking findings.

---

## v1.7.3.3 — Vulture Dead-Code Cleanup + Discovered Bugs

### Vulture Dead-Code Cleanup
Expand Down
9 changes: 9 additions & 0 deletions src/docs/GLA_GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,15 @@ that; see `CHANGELOG.md`). `compiler/build_gui.py` ("🦄 Garmin Local
Archiv Builder") is an optional Tkinter front end for the same
copy-then-build workflow, not a new build target.

**(v1.7.3.4)** T2+T3 can also be built via GitHub Actions
(`.github/workflows/build-release.yml`, `workflow_dispatch`-only — a
manual, one-click trigger, never automatic on push). It runs the same
`build_all.py` pipeline through a thin CI-only wrapper
(`compiler/build_all_github.py`) and, only on success, publishes a
GitHub Release with the four build outputs attached. Local builds via
`build_all.py`/`build_gui.py` are unaffected and remain the primary
path.

---

## 12. garmin_config.py — coupling risk
Expand Down
5 changes: 5 additions & 0 deletions src/docs/REFERENCE_GLOBAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ it fits directly into one WCM credential entry.
├── compiler/ ← Build scripts
│ ├── build.py
│ ├── build_all.py
│ ├── build_all_github.py ← CI-only entry point (v1.7.3.4) — calls
│ │ build_all.main() unchanged, then publishes
│ │ a GitHub Release. Never imported locally.
│ ├── build_manifest.py ← Single source of truth for all script lists
│ ├── build_standalone.py
│ └── build_venv.py ← ensure_build_venv(root, step) — shared
Expand Down Expand Up @@ -723,3 +726,5 @@ checksum file, so both distributed targets can self-update.
`compiler/build_manifest.py` is the single source of truth for all script lists.

**Post-v1.7.2 (garmin_collector-3_experiment, Bausteine 27/31/32/33):** both `compiler/build.py`'s and `compiler/build_standalone.py`'s `build_exe()` now invoke PyInstaller from a shared, isolated venv (`compiler/build_manifest.py::BUILD_VENV_DIR`, `D:\Garmin\.venv_gla`) instead of `sys.executable` — `ensure_build_venv()` creates it on first use, installs `requirements.txt` + PyInstaller into it, and reuses it afterwards. Prevents a package installed globally for a different project on the same build machine from being swept into a GLA build (root cause of a >8 GB T3 ZIP, see `CHANGELOG.md`). New `compiler/build_gui.py` ("🦄 Garmin Local Archiv Builder", `bat/run_build_gui.bat`) wraps the manual "copy working dir into a build folder, then run `build_all.py`" workflow into one Tkinter window with a live, elapsed-time-stamped log — not a new build target, just a GUI front end for the existing `build_all.py` sequence (Qt-test gate + `build_all.py`, unchanged). Verified end-to-end on a real Windows build. **(v1.7.3.2)** `ensure_build_venv()` itself used to be two identical copies, one in each of `build.py`/`build_standalone.py` — now lives once in `compiler/build_venv.py`, both scripts call `build_venv.ensure_build_venv(root, step=...)`; `step` is each script's own progress-print prefix ("1/4" vs "1/3" — the two scripts have a different total step count, unrelated to this change).

**v1.7.3.4 — GitHub Actions CI/CD:** `build_all.py`'s script body lives in a `main()` function now (was directly under `if __name__ == "__main__":`), so it can be imported — behavior is otherwise unchanged for a local `python build_all.py` run. `compiler/build_venv.py::ensure_build_venv()` reads an optional `GLA_BUILD_VENV_DIR` env var before falling back to `build_manifest.BUILD_VENV_DIR`, so CI never touches the local dev machines' hardcoded `D:\Garmin\.venv_gla`. New CI-only entry point `compiler/build_all_github.py` calls `build_all.main()` unchanged, then — only on success — publishes a GitHub Release (tag `vX.Y.Z` from `version.py`) with the four `build.py`/`build_standalone.py` outputs as assets; never imported by any local build script. Four new workflows under `.github/workflows/`: `test-suite.yml` (runs `run_tests.ps1` on every push to `main`, `windows-latest`), `vulture-check.yml` (dead-code gate, `check_vulture.py`'s exit code), `cve-check.yml` (report-only, `check_cve_whitelist.py` always exits 0 by design), `build-release.yml` (`workflow_dispatch`-only — manual, one click, not every push cuts a release). See `CHANGELOG.md` for the full list and the path-separator/encoding bugs the first real runs surfaced.
5 changes: 1 addition & 4 deletions src/docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

---

**Currently stable — v1.7.3.3**
**Currently stable — v1.7.3.4**

---

Expand Down Expand Up @@ -536,9 +536,6 @@ Local overview of archive health built from session logs — days synced vs fail
**Activities dashboard**
Training load, activity volume and sport-specific metrics (swim/bike/run) visualised over time. Activity data is already collected — it just isn't used beyond the summary.

**Test suite & CI/CD**
Core pipeline is covered by five test suites (218 + 134 + 211 + 80 checks + 8 sections for build output). Build integrity is covered by `validate_scripts()` in both build scripts and `test_build_output.py` as post-build gate. Full CI/CD with GitHub Actions for automated builds and release packaging is intentionally deferred — no timeline, no commitment, but the intention is there.

**Device-time vs. viewer-time display mode**
v1.6.5.6 chose device-local time (from `startTimestampGMT`/`Local`, archived every day) as the intraday display basis — reisetreu by construction, but it means a day recorded while traveling shows in the device's time zone, not the viewer's current one. A toggle to show viewer-clock time instead (system clock, DST-correct year-round, loses device-time fidelity for travel days) would be a small, independent add-on if it's ever wanted. GPS-derived time zone (from FIT activities) would only close the remaining gap — a travel day that also happens to be a DST transition day — and isn't worth building ahead of the FIT pipeline. Natural anchor point: the travel block already planned for `quality_context.json` (v1.8.2).

Expand Down
2 changes: 1 addition & 1 deletion src/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Imported by garmin_app_base.py and daily_update.py.
# No third-party imports, no tkinter — safe for all build targets.

APP_VERSION = "1.7.3.3"
APP_VERSION = "1.7.3.4"


def is_newer(latest: str, current: str) -> bool:
Expand Down
Loading