Skip to content

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

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

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