Skip to content

Rollout: migrate S-CORE modules from @score_tooling//coverage to score_coverage 0.4.2 #3258

Open

Description

@dcalavrezo-qorix

Goal

Move every S-CORE module repository from the coverage pipeline bundled in score_tooling
(@score_tooling//coverage) to the standalone, qualified Bazel module score_coverage 0.3.1
(https://github.com/eclipse-score/coverage_tool, registry release 0.3.1; 0.3.0 added the QNX gcov backend, 0.3.1 the required --instrumentation_filter).

Why: the coverage tool is TCL LOW under the S-CORE Tool Management Plan and is qualified on its
own version, with its own requirements, tests and verification report
(https://eclipse-score.github.io/coverage_tool/main/). Consumers must pin that version, not a
score_tooling release. Interface is unchanged; only labels move.

Reference migration (merged, copy this): eclipse-score/baselibs#584, then the bump #589
(6 files, MODULE.bazel + lockfile, tools/coverage/BUILD, coverage.bazelrc, workflow, YAML comment).

Repositories

Repo Coverage dir score_tooling rules_rust Files to touch Status
baselibs tools/coverage/ 2.2.1 0.68.2-score done #584 merged (0.1.0), #589 merged (0.2.0); eclipse-score/baselibs#626 adds QNX on-target coverage (0.3.1)
logging quality/coverage/ 2.2.0 0.68.2-score done eclipse-score/logging#338 (0.3.1)
lifecycle quality/coverage/ 2.2.1 0.68.2-score done eclipse-score/lifecycle#715 (0.3.1)
persistency quality/coverage/ 2.2.0 0.68.2-score done eclipse-score/persistency#450 merged (0.3.1)
kyron tools/coverage/ 2.2.1 0.68.2-score done eclipse-score/kyron#175 (0.3.1)
orchestrator tools/coverage/ 2.2.1 0.68.1-score → bump .bazelrc, MODULE.bazel, tools/coverage/{BUILD,coverage.bazelrc,coverage_justifications.yaml}, .github/workflows/coverage.yml todo
time tools/coverage/ 2.2.1 none (C++ only) done eclipse-score/time#235 (0.3.1)
inc_security_crypto tools/coverage/ 2.2.1 0.68.2-score done eclipse-score/inc_security_crypto#267 (0.3.1)
inc_someip_gateway none (on the tool since 0.2.0) 2.1.1 none done eclipse-score/inc_someip_gateway#315 (bump to 0.3.1, README label)

Sources: gh search code 'score_tooling//coverage' --owner eclipse-score on 2026-09-14.

Out of scope of this rollout (separate issues):

  • communication: carried its own copy of the pipeline; real migration in quality/coverage: replace the in-repo pipeline by score_coverage (Linux LLVM + QNX gcov)  communication#1171 (Linux LLVM + QNX gcov, draft pending review).
  • reference_integration: uses the legacy Ferrocene flow (rust_coverage_report from @score_tooling//:defs.bzl, --run_under=@score_tooling//coverage:llvm_profile_wrapper). Not provided by score_coverage; migrating it means switching to the LLVM pipeline.
  • tooling: deprecation aliases / removal of coverage/ after all consumers are moved.

Also new in 0.2.0: unmapped_files.txt in the archive lists in-scope files without any coverage data (no-data findings, declaration-only headers, compiled-without-code placeholders). Attach its no-data count to the PR; do not act on it in the migration PR.

Recipe per repository (manual)

  1. Branch off main. git grep -n 'score_tooling//coverage' to get the exact file list.
  2. Baseline first. Run the repo's coverage workflow command locally on a representative package and keep the LCOV:
    bazel coverage --config=llvm_cov --build_tests_only -- //<package>/...
    bazel run @score_tooling//coverage:generate_coverage_html -- --yaml <justifications.yaml> --testlogs-subdir <dir> --archive-dir cov_before
    cp bazel-out/_coverage/_coverage_report.dat before.dat
  3. Replace labels one-to-one:
    • @score_tooling//coverage:defs.bzl → @score_coverage//:defs.bzl
    • @score_tooling//coverage:merger → @score_coverage//:merger
    • @score_tooling//coverage:generate_coverage_html → @score_coverage//:generate_coverage_html
    • @score_tooling//coverage:enable_llvm_coverage_for_death_tests → @score_coverage//:enable_llvm_coverage_for_death_tests
    • doc links to COVERAGE_GUIDE.md → https://eclipse-score.github.io/coverage_tool/main/manual/user_manual.html
  4. MODULE.bazel: add bazel_dep(name = "score_coverage", version = "0.2.0", dev_dependency = True) next to score_tooling (keep score_tooling). Bump rules_rust to 0.68.2-score if lower.
  5. bazel mod deps --lockfile_mode=update (last step, never hand-edit the lockfile).
  6. After run: same as step 2 with @score_coverage//:generate_coverage_html, archive cov_after, copy to after.dat.
  7. python3 compare_lcov.py before.dat after.dat (script attached below). Accept only if:
    • differing: 0 and only in before: empty;
    • only in after: is empty or contains only headers that 0.1.x/score_tooling dropped (headers behind strip_include_prefix, reported under their source path, e.g. external/<repo>/include/...), each appearing once in cov_after/index.html;
    • raw/effective percentages unchanged except for those added headers.
      Anything else: do not commit, post the diff here.
  8. One commit, git commit -s, title ≤ 72 chars, e.g. Migrate coverage pipeline to score_coverage 0.2.0; body with the comparison table. PR to main, link this issue, paste the table. Wait for the coverage workflow + lockfile check.

Recipe with Claude Code (optional)

The same steps are packaged as a skill. Copy skill/migrate-to-score-coverage/SKILL.md (attached) to
~/.claude/skills/migrate-to-score-coverage/SKILL.md, then in the consumer repo run
claude and type:

/migrate-to-score-coverage  Migrate this repo to score_coverage 0.3.1 following the skill. Baseline package: //<package>/... . Tracking issue: eclipse-score/score#3258. Stop and show me the comparison before committing.

Guardrails baked into the skill: commits authored by you with sign-off and no AI trailer, no threshold or justification changes, stop on any non-zero diff, legacy Ferrocene targets are reported instead of migrated. Review the diff and the comparison yourself before pushing.

Definition of done

  • logging
  • lifecycle
  • persistency
  • kyron
  • orchestrator
  • time
  • inc_security_crypto
  • inc_someip_gateway (README / workflow check)
  • each PR carries the before/after comparison table
  • follow-up issues filed for communication, reference_integration, tooling deprecation

Attachments

compare_lcov.py
#!/usr/bin/env python3
"""Compare two LCOV files record by record.

Usage: compare_lcov.py before.dat after.dat

Prints identical / differing SF records, files only present on one side,
and the totals. Exit code 1 if any common record differs.
"""
import sys
from collections import defaultdict


def parse(path):
    records, cur, name = {}, None, None
    for line in open(path, encoding="utf-8"):
        line = line.rstrip("\n")
        if line.startswith("SF:"):
            name, cur = line[3:], defaultdict(list)
        elif line == "end_of_record":
            records[name] = {k: sorted(v) for k, v in cur.items()}
            cur = None
        elif cur is not None and ":" in line:
            key, val = line.split(":", 1)
            if key in ("DA", "BRDA", "LF", "LH", "BRF", "BRH", "FNF", "FNH"):
                cur[key].append(val)
    return records


def totals(records):
    lf = sum(int(r.get("LF", ["0"])[0]) for r in records.values())
    lh = sum(int(r.get("LH", ["0"])[0]) for r in records.values())
    bf = sum(int(r.get("BRF", ["0"])[0]) for r in records.values())
    bh = sum(int(r.get("BRH", ["0"])[0]) for r in records.values())
    pct = lambda a, b: f"{100.0 * a / b:.2f}%" if b else "n/a"
    return f"lines {lh}/{lf} = {pct(lh, lf)}  branches {bh}/{bf} = {pct(bh, bf)}"


def main(before_path, after_path):
    before, after = parse(before_path), parse(after_path)
    common = sorted(set(before) & set(after))
    differing = [f for f in common if before[f] != after[f]]
    print(f"before: {len(before)} files, after: {len(after)} files, common: {len(common)}")
    print(f"identical records: {len(common) - len(differing)}   differing: {len(differing)}")
    for f in differing:
        print(f"  ~ {f}")
        for key in sorted(set(before[f]) | set(after[f])):
            if before[f].get(key) != after[f].get(key):
                print(f"      {key}: {before[f].get(key)} -> {after[f].get(key)}")
    print("only in after:")
    for f in sorted(set(after) - set(before)):
        print(f"  + {f}")
    print("only in before:")
    for f in sorted(set(before) - set(after)):
        print(f"  - {f}")
    print(f"before: {totals(before)}")
    print(f"after:  {totals(after)}")
    return 1 if differing else 0


if __name__ == "__main__":
    if len(sys.argv) != 3:
        sys.exit(__doc__)
    sys.exit(main(sys.argv[1], sys.argv[2]))
skill/migrate-to-score-coverage/SKILL.md
---
name: migrate-to-score-coverage
description: Migrate an S-CORE module repository from @score_tooling//coverage to the qualified score_coverage Bazel module, validate that the reported coverage is unchanged, and open the PR.
---

# Migrate a consumer to score_coverage

You are migrating one S-CORE repository (the current working directory) from the
coverage pipeline bundled in `score_tooling` to the standalone, qualified module
`score_coverage` (https://github.com/eclipse-score/coverage_tool). The public
interface is identical; only labels change. Reference PR: eclipse-score/baselibs#584.

## Rules

- All commits authored by the human user, `git commit -s`, no AI co-author trailer.
- Commit title <= 72 characters. Run `bazel mod deps --lockfile_mode=update` as the
  LAST step before committing; never hand-edit `MODULE.bazel.lock`.
- Never lower a coverage threshold or drop a justification to make CI pass. If
  numbers move, stop and report with the comparison output.
- Do not touch the repo's coverage scope, justification YAML entries, or thresholds.

## Steps

1. **Inventory.** `git grep -n 'score_tooling//coverage'` and
   `git grep -n 'score_tooling.*coverage'`. Expected hits: `MODULE.bazel`
   (llvm toolchain `compile_flags`/features), `tools/coverage/BUILD` or
   `quality/coverage/BUILD` (load of `defs.bzl`), `*/coverage.bazelrc`
   (`--coverage_output_generator`), `.github/workflows/*coverage*.yml`
   (`generate_coverage_html`), READMEs, justification YAML header comment.
   If you find `llvm_profile_wrapper`, `combined_report` or
   `rust_coverage_report`: that is the legacy Ferrocene flow, NOT covered by
   score_coverage. Stop and report.

2. **Baseline run (before touching anything).** Pick the smallest package that
   exercises the scope, e.g. the one named in the repo's coverage workflow, and run
   (adapt config name to the repo, usually `llvm_cov`):
   ```
   bazel coverage --config=llvm_cov --build_tests_only -- <targets>
   bazel run @score_tooling//coverage:generate_coverage_html -- --yaml <justifications.yaml> --testlogs-subdir <dir> --archive-dir cov_before
   cp bazel-out/_coverage/_coverage_report.dat before.dat
   ```
   Save the raw and effective percentages printed by the tool.

3. **Edit labels** (one-to-one):
   | old | new |
   |---|---|
   | `@score_tooling//coverage:defs.bzl` | `@score_coverage//:defs.bzl` |
   | `@score_tooling//coverage:merger` | `@score_coverage//:merger` |
   | `@score_tooling//coverage:generate_coverage_html` | `@score_coverage//:generate_coverage_html` |
   | `@score_tooling//coverage:enable_llvm_coverage_for_death_tests` | `@score_coverage//:enable_llvm_coverage_for_death_tests` |
   | `@score_tooling//coverage:justify` / `:effective_coverage` / `:coverage_summary` | `@score_coverage//:justify` / `:effective_coverage` / `:coverage_summary` |
   | `@score_tooling//coverage:COVERAGE_GUIDE.md` (doc links) | https://eclipse-score.github.io/coverage_tool/main/manual/user_manual.html |

   In `MODULE.bazel`, next to the `score_tooling` dep, add:
   ```
   bazel_dep(name = "score_coverage", version = "0.2.0", dev_dependency = True)
   ```
   Keep `score_tooling` (still needed for format/copyright/etc.). If the repo pins
   `rules_rust` below `0.68.2-score`, bump it to `0.68.2-score` (score_coverage
   requires it; bzlmod would select it anyway, the explicit bump keeps the file honest).

4. **Lockfile.** `bazel mod deps --lockfile_mode=update`.

5. **After run.** Same commands as step 2 with `@score_coverage//:generate_coverage_html`
   and `--archive-dir cov_after`; copy the LCOV to `after.dat`.

6. **Compare.** `python3 compare_lcov.py before.dat after.dat`. Acceptance:
   - `differing: 0` for common records;
   - `only in before:` empty;
   - `only in after:` empty, or only `_virtual_includes/` headers (these were wrongly
     dropped by the old tool, see eclipse-score/baselibs#558). Each such header must
     appear exactly once in the HTML archive (`grep -c '<header>.html' cov_after/index.html`).
   - raw / effective percentages identical unless explained by the added headers.
   Anything else: stop and report the diff. Do not commit.

7. **Commit and PR.** One commit, e.g. title `Migrate coverage pipeline to score_coverage 0.2.0`.
   Body: what moved, label changes, rules_rust bump if any, and the comparison
   table (files common/identical/new, totals before/after). Open the PR against
   `main`, link the tracking issue, and paste the comparison table.

8. **CI.** Wait for the repo's coverage workflow and lockfile check. Report the
   result, including any unrelated failure with its cause.

Activity

  1. changed the title [-]Rollout: migrate S-CORE modules from @score_tooling//coverage to score_coverage 0.2.0[/-] [+]Rollout: migrate S-CORE modules from @score_tooling//coverage to score_coverage 0.3.1[/+] on Sep 30, 2026
  2. dcalavrezo-qorix commented on Sep 30, 2026

    @dcalavrezo-qorix
    ContributorAuthor

    Progress, 2026-09-30

    Released: score_coverage 0.3.0 (2026-09-28, gcov backend for QNX on-target coverage) and 0.3.1 (2026-09-29, --instrumentation_filter=^//<root>[/:] required in every coverage config, reporters warn when it is missing). Release notes. Target version for the rollout is 0.3.1.

    Repo PR Status Comparison old → new (record by record)
    baselibs eclipse-score/baselibs#584, #589 merged (0.1.0 → 0.2.0) identical
    baselibs, QNX on-target eclipse-score/baselibs#626 open, ready for review QNX report: 649 files, 92.2 % lines
    communication eclipse-score/communication#1171 open, draft (Linux + QNX, replaces the in-repo pipeline) 347 records identical in status
    logging eclipse-score/logging#338 open 99 → 117 files (headers recovered), 98/99 identical
    lifecycle eclipse-score/lifecycle#715 open 77 → 147 files (headers recovered), 75/77 identical, 2 explained
    persistency eclipse-score/persistency#450 merged 17/17 identical
    kyron eclipse-score/kyron#175 open (rules_rust bumped to 0.68.2-score) 70/74 identical, rest thread-timing variance
    time eclipse-score/time#235 open 84/86 identical, 2 environment effects, same justifications applied
    inc_security_crypto eclipse-score/inc_security_crypto#267 open 239/239 identical
    inc_someip_gateway eclipse-score/inc_someip_gateway#315 open (bump 0.2.0 → 0.3.1) 48/55 identical, rest IPC timing variance
    orchestrator todo
    reference_integration todo, one bazelrc line
    tooling todo: deprecate coverage/ once the above are merged

    Every PR carries its before/after table in the description. "Headers recovered" means headers reached through include directories that the old pipeline dropped (coverage_tool#5, fixed in 0.2.0); nothing lost coverage anywhere.

  3. dcalavrezo-qorix commented on Oct 20, 2026

    @dcalavrezo-qorix
    ContributorAuthor

    Progress update, 2026-10-20

    Releases of score_coverage since the last update (release notes)

    Repository status

    Repo Status
    baselibs QNX on-target coverage merged (eclipse-score/baselibs#626, on 0.4.0): dedicated coverage_report_qnx.yml, first main run green 2026-10-12 — 278 tests, 89.27 %, 56 score/os/qnx files measured (0 before). Gcov baseline follow-up merged (eclipse-score/baselibs#704); now fully on 0.4.2 (eclipse-score/baselibs#652 merged)
    time done — eclipse-score/time#235 merged (0.3.1); renovate 0.4.1 bump open (eclipse-score/time#249)
    inc_someip_gateway done — eclipse-score/inc_someip_gateway#315 merged
    persistency done; renovate 0.4.1 bump green (eclipse-score/persistency#502)
    logging done on 0.3.1; renovate bump (eclipse-score/logging#356) blocked, see below
    lifecycle fully on 0.4.2 — eclipse-score/lifecycle#804 merged (incl. cc_binary baseline notes)
    communication eclipse-score/communication#1171 superseded by eclipse-score/communication#1240 (combined Linux LLVM + QNX, draft) — parked until the author returns (~2026-10-27); a rebased branch with score_coverage 0.4.x + coverage_scope_qnx is prepared and validated (QNX scope: 646 files vs 613 on Linux)
    config_management migration PR eclipse-score/config_management#62 was closed unmerged after 7 weeks without review — needs a module-team decision (reopen on 0.4.2 or take over the branch)
    reference_integration pending (one-line consumer change once the module model is settled)

    Known blocker for the renovate bumps: score_devcontainer 1.11.1 was yanked (eclipse-score/bazel_registry#528/#529); logging pulls it via score_docs_as_code 8.3.0, so its score_coverage renovate PR stays red until the repo overrides to devcontainer 1.11.2 or a docs-as-code 8.3.1 ships (baselibs resolved this and is on 0.4.2).

    Next: reference_integration migration, resume communication#1240 end of October, then deprecate @score_tooling//coverage. A pattern-based scope (command-line aspect over //..., no per-platform deps lists) is under discussion as the 0.5.0 direction.

  4. changed the title [-]Rollout: migrate S-CORE modules from @score_tooling//coverage to score_coverage 0.3.1[/-] [+]Rollout: migrate S-CORE modules from @score_tooling//coverage to score_coverage 0.4.2[/+] on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bazelPull requests that update bazel codecommunity:infrastructureGeneral Score infrastructure topics

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions