From ced05752ee43e38193808ff5792b574861783d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Lo=CC=81pez=20Man=CC=83as?= Date: Thu, 8 Oct 2026 19:05:51 +0200 Subject: [PATCH] ci: move coverage history off main and retry pushes Recording coverage on main raced when two merges landed close together: each run checked out its own triggering commit, both appended to the end of coverage/history.csv, and git pull --rebase conflicted. Every bot commit on main also marked all open pull requests out of date under strict branch protection. The history now lives on the coverage-history branch, checked out after the concurrency wait. push_coverage_history.sh retries a rejected push by resetting to the branch tip, re-applying our rows with a new merge subcommand and re-rendering COVERAGE.md. The first run creates the branch, seeded from the last version of coverage/ in main's history, so coverage/ is removed from main here. Mirrors googlemaps/android-maps-compose#1006. Only unit coverage is recorded. --- .github/scripts/coverage_history.py | 42 +++++- .github/scripts/push_coverage_history.sh | 88 +++++++++++++ .github/workflows/coverage-history.yml | 158 ++++++++++++++++++----- AGENTS.md | 10 +- coverage/COVERAGE.md | 51 -------- coverage/history.csv | 127 ------------------ 6 files changed, 264 insertions(+), 212 deletions(-) create mode 100755 .github/scripts/push_coverage_history.sh delete mode 100644 coverage/COVERAGE.md delete mode 100644 coverage/history.csv diff --git a/.github/scripts/coverage_history.py b/.github/scripts/coverage_history.py index a8a4d64aa..461129609 100755 --- a/.github/scripts/coverage_history.py +++ b/.github/scripts/coverage_history.py @@ -27,6 +27,8 @@ summary print the parsed coverage as JSON (debugging / ad-hoc use) append append one row per module to the append-only history CSV + merge re-apply locally added rows onto a refreshed history CSV, used + when a push to the coverage-history branch is rejected render regenerate the human-readable COVERAGE.md from that CSV compare render a markdown coverage diff against the last recorded entry, for posting on a pull request @@ -354,6 +356,37 @@ def render_current(rows: list[dict], suite: str, heading: str, note: str) -> lis return lines +def row_key(row: dict) -> tuple[str, str, str]: + """Identity of a history row: one module, one suite, one commit.""" + return (row.get("commit", ""), row.get("suite", ""), row.get("module", "")) + + +def cmd_merge(args: argparse.Namespace) -> None: + """Re-apply locally added rows on top of a refreshed history file. + + Two runs appending to the end of the CSV cannot be merged by git: both + touch the same region and the rebase conflicts. The rows are independent + records, though, so the correct resolution is simply "keep both". This + reads the rows we added, rebases them onto whatever is now on the branch, + and drops any that are already there. + """ + base = read_history(args.csv) + ours = read_history(args.ours) + + present = {row_key(row) for row in base} + added = [row for row in ours if row_key(row) not in present] + if not added: + print("No local rows to re-apply.") + return + + with open(args.csv, "a", newline="", encoding="utf-8") as handle: + writer = csv.DictWriter(handle, fieldnames=CSV_FIELDS, lineterminator="\n") + if not base: + writer.writeheader() + writer.writerows(added) + print(f"Re-applied {len(added)} row(s) onto {args.csv}") + + def cmd_render(args: argparse.Namespace) -> None: rows = read_history(args.csv) if not rows: @@ -508,7 +541,7 @@ def delta(current: float, module: str, field: str) -> str: lines += [ "", f"Line and branch coverage from {args.suite} test reports. History " - f"is recorded in `coverage/history.csv` after each merge to " + f"is recorded on the `coverage-history` branch after each merge to " f"`{args.base}`.", "", ] @@ -563,6 +596,13 @@ def main() -> None: ) append.set_defaults(func=cmd_append) + merge = subparsers.add_parser( + "merge", help="re-apply locally added rows onto a refreshed history CSV" + ) + merge.add_argument("--csv", default=DEFAULT_CSV, help="refreshed file to merge into") + merge.add_argument("--ours", required=True, help="our copy, holding the new rows") + merge.set_defaults(func=cmd_merge) + render = subparsers.add_parser("render", help="regenerate COVERAGE.md from the CSV") render.add_argument("--csv", default=DEFAULT_CSV) render.add_argument("--out", default=DEFAULT_MARKDOWN) diff --git a/.github/scripts/push_coverage_history.sh b/.github/scripts/push_coverage_history.sh new file mode 100755 index 000000000..238dc2fdd --- /dev/null +++ b/.github/scripts/push_coverage_history.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Commits and pushes the coverage history to its own branch. +# +# Usage: push_coverage_history.sh "" +# +# The history deliberately does not live on main. Branch protection there uses +# strict required status checks, so every commit pushed to main marks all open +# pull requests out-of-date and forces contributors to update their branch and +# re-run CI. +# +# If the branch moved under us, the update is redone on top of the new tip +# rather than rebased. Both sides append to the end of history.csv, so a rebase +# conflicts every time; the rows are independent records, so the right +# resolution is to keep both and regenerate the report. + +set -euo pipefail + +MESSAGE="${1:?commit message required}" +DATA_DIR="${DATA_DIR:-coverage-data}" +DATA_BRANCH="${DATA_BRANCH:-coverage-history}" +SCRIPT="${SCRIPT:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/coverage_history.py}" + +# Every generated file published on the data branch. COVERAGE.md is derived +# from history.csv, so it is regenerated, not merged. +FILES=(history.csv COVERAGE.md) + +cd "$DATA_DIR" + +git config user.name 'googlemaps-bot' +git config user.email 'googlemaps-bot@google.com' + +# Stage first and compare the index: a newly generated file is untracked, and +# "git diff" does not see untracked files, so checking before staging would +# skip the commit the first time a new output appears. +git add -- "${FILES[@]}" + +if git diff --cached --quiet -- "${FILES[@]}"; then + echo "Coverage history unchanged; nothing to commit." + exit 0 +fi + +git commit -m "$MESSAGE" + +for attempt in 1 2 3; do + if git push origin "HEAD:$DATA_BRANCH"; then + echo "Pushed to $DATA_BRANCH." + exit 0 + fi + + echo "Push rejected (attempt $attempt); redoing the update on the new tip." + + # Keep our rows, take the branch as it now stands, then put ours back. + OURS="${RUNNER_TEMP:-/tmp}/coverage-history-ours.csv" + cp history.csv "$OURS" + + git fetch origin "$DATA_BRANCH" + git reset --hard "origin/$DATA_BRANCH" + + python3 "$SCRIPT" merge --csv history.csv --ours "$OURS" + python3 "$SCRIPT" render --csv history.csv --out COVERAGE.md + + git add -- "${FILES[@]}" + + if git diff --cached --quiet -- "${FILES[@]}"; then + echo "Our rows are already on $DATA_BRANCH; nothing left to push." + exit 0 + fi + + git commit -m "$MESSAGE" +done + +echo "Could not push to $DATA_BRANCH after 3 attempts." >&2 +exit 1 diff --git a/.github/workflows/coverage-history.yml b/.github/workflows/coverage-history.yml index a2a761b80..1f2d40b43 100644 --- a/.github/workflows/coverage-history.yml +++ b/.github/workflows/coverage-history.yml @@ -12,9 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Records unit test coverage in coverage/history.csv after every merge to main, -# so coverage can be tracked over time. Release-please commits and the -# workflow's own commits are skipped. +# Records unit test coverage after every merge to main. +# +# The history is written to the "coverage-history" branch, never to main. +# Branch protection on main uses strict required status checks, so any commit +# pushed to main marks every open pull request out-of-date and forces +# contributors to update their branch and re-run CI. Keeping the data on its +# own branch means recording coverage costs contributors nothing. +# +# The first run creates the branch, seeded with the history that used to live +# in coverage/ on main, so the trend stays continuous. name: Record coverage history on: @@ -25,31 +32,117 @@ on: permissions: contents: read -# Serialize runs so two quick merges cannot race on pushing the history file. +# Serialize runs so two merges cannot race on pushing the history branch. concurrency: group: coverage-history cancel-in-progress: false +env: + # Branch holding history.csv and COVERAGE.md. + DATA_BRANCH: coverage-history + jobs: record: - # Skip release-please's release commits and this workflow's own commits. - # (The commit below carries [skip ci] as well, and pushes made with - # GITHUB_TOKEN do not trigger workflows, so this is belt and braces.) + # Skip release-please's release commits. The history no longer lands on + # main, so this workflow cannot retrigger itself. if: >- - !startsWith(github.event.head_commit.message, 'chore(main): release') && - !startsWith(github.event.head_commit.message, 'chore: update coverage history') + !startsWith(github.event.head_commit.message, 'chore(main): release') runs-on: ubuntu-latest permissions: contents: write steps: - # Credentials are deliberately persisted here: the final step pushes the - # updated history back to main, so persist-credentials must stay enabled. - name: Checkout Repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false + + - name: Check for the coverage history branch + id: branch + run: | + set +e + git ls-remote --exit-code --heads origin "$DATA_BRANCH" > /dev/null + STATUS=$? + set -e + case "$STATUS" in + 0) echo "exists=true" >> "$GITHUB_OUTPUT" ;; + 2) echo "exists=false" >> "$GITHUB_OUTPUT" + echo "$DATA_BRANCH does not exist yet; it will be created." ;; + *) echo "Could not query $DATA_BRANCH (git exit $STATUS)." >&2 + exit 1 ;; + esac + + # Credentials are deliberately persisted on this checkout only: the steps + # below push the updated history to the data branch. + - name: Checkout coverage history branch + if: steps.branch.outputs.exists == 'true' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ env.DATA_BRANCH }} + path: coverage-data token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO || secrets.GITHUB_TOKEN }} + # First run only. Full history is fetched so the seed step can find the + # last version of coverage/ on main, even after it has been removed there. + - name: Checkout main to seed the coverage history branch + if: steps.branch.outputs.exists == 'false' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: coverage-data + fetch-depth: 0 + token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO || secrets.GITHUB_TOKEN }} + + - name: Seed the coverage history branch from main + if: steps.branch.outputs.exists == 'false' + working-directory: coverage-data + run: | + # The newest commit that touched coverage/history.csv. If that commit + # removed the file, its parent holds the last version. + SOURCE=$(git rev-list -1 HEAD -- coverage/history.csv) + if [ -n "$SOURCE" ] && ! git cat-file -e "$SOURCE:coverage/history.csv" 2>/dev/null; then + SOURCE="$SOURCE^" + fi + + git checkout --orphan "$DATA_BRANCH" + git rm -rfq . + git clean -fdxq + + if [ -n "$SOURCE" ]; then + echo "Seeding from coverage/ at $(git rev-parse --short "$SOURCE")." + git show "$SOURCE:coverage/history.csv" > history.csv + git show "$SOURCE:coverage/COVERAGE.md" > COVERAGE.md 2>/dev/null || rm -f COVERAGE.md + else + echo "No earlier coverage history found; starting empty." + fi + + cat > README.md <<'README' + # Coverage history + + Generated data. Do not edit by hand, and do not open pull requests against + this branch. + + | File | Contents | + | --- | --- | + | `history.csv` | One row per module per merged commit (unit tests). | + | `COVERAGE.md` | Human-readable report, regenerated from `history.csv`. | + + Both are written by `.github/scripts/coverage_history.py` on `main`, run by the + `Record coverage history` workflow after every merge to `main`. + + This data lives on its own branch rather than on `main` on purpose. Branch + protection on `main` uses strict required status checks, so every commit + pushed there marks all open pull requests out-of-date and forces contributors + to update their branch and re-run CI. Keeping coverage data here means + recording it costs contributors nothing. + README + + git config user.name 'googlemaps-bot' + git config user.email 'googlemaps-bot@google.com' + git add -- README.md + [ -f history.csv ] && git add -- history.csv + [ -f COVERAGE.md ] && git add -- COVERAGE.md + git commit -qm 'chore: seed coverage history from main' + - name: Set up JDK 21 uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: @@ -62,33 +155,36 @@ jobs: - name: Create dummy secrets.properties run: echo "MAPS_API_KEY=dummy" > secrets.properties - - name: Run unit tests and generate coverage reports - run: ./gradlew koverXmlReportDebug --stacktrace - - - name: Append to coverage history - env: - COMMIT_SHA: ${{ github.sha }} + - name: Resolve commit metadata + id: meta run: | SUBJECT=$(git log -1 --pretty=%s) # Squash-merged PRs end their subject with "(#123)". The number is # recorded in its own column, so drop it from the subject text. PR=$(printf '%s' "$SUBJECT" | sed -n 's/.*(#\([0-9]\{1,\}\))$/\1/p') SUBJECT=$(printf '%s' "$SUBJECT" | sed 's/ (#[0-9]\{1,\})$//') + { + echo "pr=$PR" + echo "subject<> "$GITHUB_OUTPUT" - python3 .github/scripts/coverage_history.py append --suite unit \ - --commit "$COMMIT_SHA" --pr "$PR" --subject "$SUBJECT" + - name: Run unit tests and generate coverage reports + run: ./gradlew koverXmlReportDebug --stacktrace - python3 .github/scripts/coverage_history.py render + - name: Record unit coverage + env: + COMMIT_SHA: ${{ github.sha }} + PR_NUMBER: ${{ steps.meta.outputs.pr }} + SUBJECT: ${{ steps.meta.outputs.subject }} + run: | + python3 .github/scripts/coverage_history.py append --suite unit \ + --csv coverage-data/history.csv \ + --commit "$COMMIT_SHA" --pr "$PR_NUMBER" --subject "$SUBJECT" + python3 .github/scripts/coverage_history.py render \ + --csv coverage-data/history.csv --out coverage-data/COVERAGE.md - - name: Commit coverage history + - name: Push unit coverage run: | - git config user.name 'googlemaps-bot' - git config user.email 'googlemaps-bot@google.com' - git add coverage/history.csv coverage/COVERAGE.md - if git diff --staged --quiet; then - echo "Coverage history unchanged; nothing to commit." - exit 0 - fi - git commit -m 'chore: update coverage history [skip ci]' - git pull --rebase origin main - git push origin HEAD:main + .github/scripts/push_coverage_history.sh 'chore: record unit coverage' diff --git a/AGENTS.md b/AGENTS.md index 27ab3abbe..1ca9b54e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,8 +36,14 @@ Shared Gradle conventions are in `build-logic/` (included build). Coverage is tracked over time. After every merge to `main`, the `Record coverage history` workflow appends a row per module to -`coverage/history.csv` and regenerates `coverage/COVERAGE.md`. Both files are -generated by `.github/scripts/coverage_history.py`. Never edit them by hand. +`history.csv` and regenerates `COVERAGE.md`, both of which live on the +[`coverage-history`](../../tree/coverage-history) branch. They are generated by +`.github/scripts/coverage_history.py`. Never edit them by hand. + +The data is kept off `main` deliberately. Branch protection on `main` uses +strict required status checks, so any commit pushed there marks every open +pull request out-of-date and forces contributors to update their branch and +re-run CI. The history records unit test coverage. The CSV carries a `suite` column so emulator coverage could be added later without changing the format. diff --git a/coverage/COVERAGE.md b/coverage/COVERAGE.md deleted file mode 100644 index 56fda5b8d..000000000 --- a/coverage/COVERAGE.md +++ /dev/null @@ -1,51 +0,0 @@ - -# Test coverage - -Raw data lives in [`history.csv`](history.csv), one row per module per -suite per merged commit. This page is regenerated from it and should not -be edited by hand. - -## Current - -### Unit tests - -JVM tests run by `./gradlew koverXmlReportDebug`, covering `library`, `clustering`, `data`, `heatmaps`, `ui`. - -Measured at `6d222ff` (#1812), recorded 2026-10-08T16:16:44Z. - -| Module | Lines | Line % | | Branches | Branch % | -| --- | ---: | ---: | --- | ---: | ---: | -| `clustering` | 607/1,849 | 32.83% | `███████░░░░░░░░░░░░░` | 181/852 | 21.24% | -| `data` | 1,678/2,938 | 57.11% | `███████████░░░░░░░░░` | 946/2,445 | 38.69% | -| `heatmaps` | 257/314 | 81.85% | `████████████████░░░░` | 134/162 | 82.72% | -| `library` | 1,023/1,172 | 87.29% | `█████████████████░░░` | 311/374 | 83.16% | -| `ui` | 122/151 | 80.79% | `████████████████░░░░` | 48/68 | 70.59% | -| **TOTAL** | 3,687/6,424 | 57.39% | `███████████░░░░░░░░░` | 1,620/3,901 | 41.53% | - -## Trend (last 30 commits) - -Total line coverage, newest first. - -| Date | Commit | PR | Line % | Change | Subject | -| --- | --- | --- | ---: | ---: | --- | -| 2026-10-08 | `6d222ff` | #1812 | 57.39% | 0.00 | build(deps): bump the gradle-minor-and-patch group with 2 updates | -| 2026-10-05 | `bbdbe83` | #1796 | 57.39% | 0.00 | build(deps): bump the gradle-minor-and-patch group with 5 updates | -| 2026-10-01 | `145d2fa` | #1807 | 57.39% | 0.00 | ci: add a workflow to publish snapshot builds | -| 2026-10-01 | `ad2ba13` | #1810 | 57.39% | 0.00 | docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes | -| 2026-10-01 | `48d3371` | #1805 | 57.39% | 0.00 | test: add end-to-end demo smoke test | -| 2026-10-01 | `2bd91d5` | #1806 | 57.39% | 0.00 | ci: merge generated docs PRs from the docs workflow | -| 2026-09-30 | `de264f8` | #1716 | 57.39% | +4.18 | feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0) | -| 2026-09-30 | `657fc55` | #1802 | 53.21% | 0.00 | build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions | -| 2026-09-30 | `f805f5e` | #1803 | 53.21% | +0.02 | fix(library): match reference rounding and unsigned varints in flexible polyline | -| 2026-09-30 | `50e88c9` | #1801 | 53.19% | +1.14 | feat(library): add flexible polyline encoding and decoding | -| 2026-09-24 | `56b3c24` | #1792 | 52.05% | +0.14 | fix(data): enforce decompression limit and reject payload on KMZ directory entries | -| 2026-09-23 | `51c59bf` | #1775 | 51.91% | 0.00 | ci: support GEMINI_MODEL repository variable in triage-issue workflow | -| 2026-09-23 | `651c6e0` | #1791 | 51.91% | 0.00 | build(deps): bump org.robolectric:robolectric | -| 2026-09-22 | `2e33859` | #1789 | 51.91% | 0.00 | ci: group dependabot minor and patch updates | -| 2026-09-22 | `c3fa410` | #1787 | 51.91% | +1.36 | feat: add z-index support across geometry styles and layer mappers | -| 2026-09-21 | `5557301` | #1762 | 50.55% | 0.00 | build(deps): bump com.google.android.libraries.navigation:navigation | -| 2026-09-18 | `a70f257` | #1781 | 50.55% | 0.00 | build(deps): bump actions/setup-java from 5.7.0 to 6.0.1 | -| 2026-09-18 | `9056fd9` | #1785 | 50.55% | +0.02 | feat: add polygon z-index support | -| 2026-09-16 | `3884f22` | #1783 | 50.53% | 0.00 | build(deps): bump androidx.compose:compose-bom | -| 2026-09-15 | `6015319` | #1778 | 50.53% | 0.00 | ci: record unit test coverage history | -| 2026-09-15 | `51e9e43` | #1772 | 50.53% | n/a | chore(ci): harden Gemini issue triage against prompt injection | diff --git a/coverage/history.csv b/coverage/history.csv deleted file mode 100644 index 34f46a3d9..000000000 --- a/coverage/history.csv +++ /dev/null @@ -1,127 +0,0 @@ -timestamp,commit,pr,subject,suite,module,lines_covered,lines_total,line_pct,branches_covered,branches_total,branch_pct,instructions_covered,instructions_total,instruction_pct -2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,clustering,553,1779,31.08,169,834,20.26,3115,9947,31.32 -2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,data,1309,2642,49.55,797,2223,35.85,9451,19410,48.69 -2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-15T15:14:46Z,51e9e43,1772,chore(ci): harden Gemini issue triage against prompt injection,unit,TOTAL,2731,5405,50.53,1340,3529,37.97,17702,35182,50.32 -2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,data,1309,2642,49.55,797,2223,35.85,9451,19410,48.69 -2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-15T23:03:49Z,6015319,1778,ci: record unit test coverage history,unit,TOTAL,2731,5405,50.53,1341,3529,38.0,17702,35182,50.32 -2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,clustering,553,1779,31.08,171,834,20.5,3115,9947,31.32 -2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,data,1309,2642,49.55,797,2223,35.85,9451,19410,48.69 -2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-16T10:07:38Z,3884f22,1783,build(deps): bump androidx.compose:compose-bom,unit,TOTAL,2731,5405,50.53,1342,3529,38.03,17702,35182,50.32 -2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,clustering,553,1779,31.08,169,834,20.26,3115,9947,31.32 -2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,data,1311,2644,49.58,797,2223,35.85,9473,19433,48.75 -2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-18T16:30:00Z,9056fd9,1785,feat: add polygon z-index support,unit,TOTAL,2733,5407,50.55,1340,3529,37.97,17724,35205,50.35 -2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,data,1311,2644,49.58,797,2223,35.85,9473,19433,48.75 -2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-18T21:13:43Z,a70f257,1781,build(deps): bump actions/setup-java from 5.7.0 to 6.0.1,unit,TOTAL,2733,5407,50.55,1341,3529,38.0,17724,35205,50.35 -2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,clustering,553,1779,31.08,169,834,20.26,3115,9947,31.32 -2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,data,1311,2644,49.58,797,2223,35.85,9473,19433,48.75 -2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-21T08:20:37Z,5557301,1762,build(deps): bump com.google.android.libraries.navigation:navigation,unit,TOTAL,2733,5407,50.55,1340,3529,37.97,17724,35205,50.35 -2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85 -2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-22T14:48:21Z,c3fa410,1787,feat: add z-index support across geometry styles and layer mappers,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51 -2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85 -2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-22T18:25:27Z,2e33859,1789,ci: group dependabot minor and patch updates,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51 -2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85 -2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-23T04:26:59Z,651c6e0,1791,build(deps): bump org.robolectric:robolectric,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51 -2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,data,1392,2658,52.37,834,2241,37.22,9921,19509,50.85 -2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-23T15:47:55Z,51c59bf,1775,ci: support GEMINI_MODEL repository variable in triage-issue workflow,unit,TOTAL,2814,5421,51.91,1378,3547,38.85,18172,35281,51.51 -2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0 -2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,library,512,561,91.27,211,260,81.15,2890,3168,91.22 -2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-24T14:16:04Z,56b3c24,1792,fix(data): enforce decompression limit and reject payload on KMZ directory entries,unit,TOTAL,2824,5426,52.05,1384,3551,38.97,18212,35303,51.59 -2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,clustering,553,1779,31.08,171,834,20.5,3115,9947,31.32 -2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0 -2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,library,649,702,92.45,274,334,82.04,3693,3989,92.58 -2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-30T14:44:45Z,50e88c9,1801,feat(library): add flexible polyline encoding and decoding,unit,TOTAL,2961,5567,53.19,1448,3625,39.94,19015,36124,52.64 -2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,clustering,553,1779,31.08,171,834,20.5,3115,9947,31.32 -2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0 -2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,library,651,704,92.47,276,336,82.14,3706,4002,92.6 -2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-30T20:26:06Z,f805f5e,1803,fix(library): match reference rounding and unsigned varints in flexible polyline,unit,TOTAL,2963,5569,53.21,1450,3627,39.98,19028,36137,52.66 -2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,clustering,553,1779,31.08,170,834,20.38,3115,9947,31.32 -2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,data,1402,2663,52.65,840,2245,37.42,9961,19531,51.0 -2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,heatmaps,242,275,88.0,120,146,82.19,1685,1912,88.13 -2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,library,651,704,92.47,276,336,82.14,3706,4002,92.6 -2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,ui,115,148,77.7,43,66,65.15,561,745,75.3 -2026-09-30T22:24:57Z,657fc55,1802,"build(deps): update AGP, Android Lint, Kover, Ktor, and core-ktx to latest stable versions",unit,TOTAL,2963,5569,53.21,1449,3627,39.95,19028,36137,52.66 -2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,clustering,607,1849,32.83,180,852,21.13,3410,10253,33.26 -2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-09-30T23:40:41Z,de264f8,1716,feat!: migrate android-maps-ktx into android-maps-utils (v6.0.0),unit,TOTAL,3687,6424,57.39,1619,3901,41.5,23084,40531,56.95 -2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26 -2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-10-01T13:00:26Z,2bd91d5,1806,ci: merge generated docs PRs from the docs workflow,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95 -2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26 -2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-10-01T17:32:40Z,48d3371,1805,test: add end-to-end demo smoke test,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95 -2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26 -2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-10-01T18:10:13Z,ad2ba13,1810,docs: explain BEGIN_COMMIT_OVERRIDE for PRs with several changes,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95 -2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26 -2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-10-01T18:14:55Z,145d2fa,1807,ci: add a workflow to publish snapshot builds,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95 -2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,clustering,607,1849,32.83,182,852,21.36,3410,10253,33.26 -2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-10-05T08:48:05Z,bbdbe83,1796,build(deps): bump the gradle-minor-and-patch group with 5 updates,unit,TOTAL,3687,6424,57.39,1621,3901,41.55,23084,40531,56.95 -2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,clustering,607,1849,32.83,181,852,21.24,3410,10253,33.26 -2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,data,1678,2938,57.11,946,2445,38.69,11325,20860,54.29 -2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,heatmaps,257,314,81.85,134,162,82.72,1830,2065,88.62 -2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,library,1023,1172,87.29,311,374,83.16,5880,6562,89.61 -2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,ui,122,151,80.79,48,68,70.59,639,791,80.78 -2026-10-08T16:16:44Z,6d222ff,1812,build(deps): bump the gradle-minor-and-patch group with 2 updates,unit,TOTAL,3687,6424,57.39,1620,3901,41.53,23084,40531,56.95