From 1ec527ce796a3b2f0969065f973375236ac97cd9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 19:44:53 +0000 Subject: [PATCH] Documentation chain for v1.7.3.4 (CI/CD session) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit version.py: 1.7.3.3 -> 1.7.3.4. CHANGELOG.md: new v1.7.3.4 entry covering the four new GitHub Actions workflows, the build_all.py/build_venv.py/build_all_github.py changes, the vulture_whitelist.py path-separator bug, and the two Windows cp1252 encoding bugs found on the first real build-release.yml run. ROADMAP.md: removed the "Test suite & CI/CD" paragraph under "Under consideration" (was explicitly marked "intentionally deferred", now done — CHANGELOG.md is the historical record per the project's own "no double bookkeeping" principle). "Currently stable" line updated. REFERENCE_GLOBAL.md: Build targets section and folder-structure tree updated with build_all_github.py and a summary of the new CI/CD layer. GLA_GUIDELINES.md SS11: note added on the new GitHub Actions build path, local build_all.py/build_gui.py remain the primary path. README.md: two stale claims corrected ("no automated build/test CI yet" / "Full CI/CD ... is planned for a later version" -> both are now true), Tests badge added (test-suite.yml). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01JYLx9STTgfo9KXLC2Gog3g --- README.md | 5 +- src/docs/CHANGELOG.md | 100 +++++++++++++++++++++++++++++++++++ src/docs/GLA_GUIDELINES.md | 9 ++++ src/docs/REFERENCE_GLOBAL.md | 5 ++ src/docs/ROADMAP.md | 5 +- src/version.py | 2 +- 6 files changed, 119 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0efa40a..2ef6c44 100644 --- a/README.md +++ b/README.md @@ -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.** @@ -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 @@ -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. --- diff --git a/src/docs/CHANGELOG.md b/src/docs/CHANGELOG.md index 4a08a86..b0b5c71 100644 --- a/src/docs/CHANGELOG.md +++ b/src/docs/CHANGELOG.md @@ -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 diff --git a/src/docs/GLA_GUIDELINES.md b/src/docs/GLA_GUIDELINES.md index 1836afa..6757879 100644 --- a/src/docs/GLA_GUIDELINES.md +++ b/src/docs/GLA_GUIDELINES.md @@ -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 diff --git a/src/docs/REFERENCE_GLOBAL.md b/src/docs/REFERENCE_GLOBAL.md index a70e6ed..e6e253b 100644 --- a/src/docs/REFERENCE_GLOBAL.md +++ b/src/docs/REFERENCE_GLOBAL.md @@ -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 @@ -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. diff --git a/src/docs/ROADMAP.md b/src/docs/ROADMAP.md index cb53302..7185718 100644 --- a/src/docs/ROADMAP.md +++ b/src/docs/ROADMAP.md @@ -6,7 +6,7 @@ --- -**Currently stable — v1.7.3.3** +**Currently stable — v1.7.3.4** --- @@ -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). diff --git a/src/version.py b/src/version.py index a81c16c..2205181 100644 --- a/src/version.py +++ b/src/version.py @@ -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: