Skip to content

chore: migrate from nyc to c8 and enhance coverage reporting#3553

Merged
eablack merged 29 commits intov11.0.0from
eb/migrate-to-c8
Mar 4, 2026
Merged

chore: migrate from nyc to c8 and enhance coverage reporting#3553
eablack merged 29 commits intov11.0.0from
eb/migrate-to-c8

Conversation

@eablack
Copy link
Contributor

@eablack eablack commented Mar 3, 2026

Summary

Migrates test coverage tooling from nyc to c8 and enhances coverage reporting in CI/CD workflows with automated coverage comparison and enforcement.

Type of Change

Patch Updates (patch semver update)

  • chore: Change that does not affect production code

Changes

Coverage Tool Migration

  • Replaced nyc with c8 for test coverage (modern V8-based coverage)
  • Created .c8rc.json configuration file with 60% coverage thresholds for statements, branches, functions, and lines
  • Removed legacy nyc-config.js
  • Updated test scripts to use npx c8 with appropriate flags
  • CI tests use --all --check-coverage to enforce coverage thresholds across the entire codebase
  • Local test:file script provides focused coverage on individual test files with --no-clean

CI/CD Workflow Improvements

Separate Linting Job

  • Moved linting to its own parallel CI job
  • Runs once on ubuntu-latest + Node 22.x instead of 6 times across the matrix
  • Provides faster, clearer feedback on lint failures

Optimized Workflow Triggers

  • Push events now only trigger on main, v11.0.0, and long-lived feature branches
  • PR branches trigger via pull_request event (opened/synchronize)

Enhanced Coverage Reporting

  • Coverage Artifacts: Upload HTML coverage reports on PR runs (30-day retention)
  • Job Summary: Formatted coverage table displayed in GitHub Actions UI for both PR and base branch
  • Coverage Check Job: Runs on PRs to compare coverage against base branch
    • Downloads coverage artifacts from both PR tests and base branch tests
    • Calculates coverage differences (lines, functions, branches)
    • Displays comparison with visual indicators (🟢 for improvements, 🔴 for decreases, ⚪ for no change)
    • Fails the workflow if coverage decreases, unless all metrics are ≥ 90%
    • Passes if base branch has no coverage configured (graceful handling during migration)

Benefits

  1. Faster CI execution - Linting, PR tests, and base tests run in parallel; no duplicate runs
  2. Better visibility - Coverage metrics prominently displayed in Actions UI with comparison tables
  3. Coverage enforcement - PRs automatically fail if coverage decreases (unless ≥ 90%)
  4. Flexible standards - 90% threshold allows minor decreases when coverage is excellent

Testing

Steps:

  1. Passing CI suffices
  2. Verify coverage artifacts are uploaded on PR runs
  3. Verify coverage summary appears in job summary for both test and base-coverage jobs
  4. Verify coverage-check job displays comparison and passes/fails appropriately

Screenshots (if applicable)

Screenshot 2026-03-04 at 9 04 08 AM

Related Issues

https://gus.lightning.force.com/lightning/r/ADM_Work__c/a07EE00002Vjr4ZYAR/view

@eablack eablack requested a review from a team as a code owner March 3, 2026 19:25
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:28 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:32 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:32 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:32 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:32 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:34 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:34 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:34 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 19:34 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 20:11 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 20:11 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 20:11 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 20:11 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:19 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:19 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:19 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:19 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:27 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:27 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:27 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:27 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:38 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:38 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:38 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:38 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 3, 2026 21:54 — with GitHub Actions Inactive
@eablack eablack temporarily deployed to AcceptanceTests March 4, 2026 17:19 — with GitHub Actions Inactive
eablack added 29 commits March 4, 2026 12:58
- Replace nyc with c8 for better ESM and TypeScript support
- c8 uses native V8 coverage instead of instrumentation
- Remove @istanbuljs/nyc-config-typescript dependency
- Update test:unit:justTest:ci script to use c8

c8 provides more accurate coverage and better compatibility with
modern JavaScript (ESM) and TypeScript.
- Replace nyc-config.js with .c8rc.json for proper c8 configuration
- Update test scripts to use npx c8 with appropriate flags
- Add --all and --check-coverage flags to CI test script
- Configure test:file script for focused coverage on individual tests
- Move linting to separate CI job for better parallelization
- Add coverage artifact uploads and job summaries to CI
- Add automatic PR comments with coverage changes via lcov-reporter-action
Replace romeovs/lcov-reporter-action with custom script using gh CLI
to comply with corporate GitHub Actions policy. The script provides
the same functionality: posts coverage report as PR comment and
automatically deletes old comments.
Show coverage changes compared to base branch in PR comments.
Includes visual indicators (🟢/🔴) to highlight improvements
or decreases in coverage metrics.
Move PR coverage comments to a separate workflow that runs after
the main test workflow completes. This keeps the test workflow fast
while still providing detailed coverage comparison in PR comments.

Benefits:
- Main test workflow completes faster
- Coverage comparison runs independently
- Base branch tests don't block PR feedback
Add github.event_name == 'pull_request' condition to artifact upload
to avoid creating unnecessary artifacts on non-PR pushes.
Remove extra indentation that was causing YAML parsing errors.
Replace direct string assignment with heredoc syntax to prevent
YAML parser from treating markdown table pipes as YAML syntax.
Replace multiline string assignments with printf to completely
avoid YAML parser issues with pipe characters in markdown tables.
Temporarily reduce coverage thresholds from 80% to 60% to allow
tests to pass while coverage is being improved.
Switch from workflow_run to pull_request trigger to avoid GITHUB_TOKEN
limitations. The workflow now polls the test workflow completion using
the GitHub API before downloading the coverage artifact and generating
the comparison comment.
Run base branch tests immediately while waiting for PR tests to
complete, then use the pre-computed results for comparison. This
optimizes the workflow by doing both test runs in parallel.
Switch from GitHub API artifact download to the official
actions/download-artifact@v7 action which should work better with
organizational IP allowlist restrictions.
Add separate base-coverage job that runs in parallel with main tests.
New coverage-comment job waits for both to succeed, downloads artifacts,
and posts comparison comment. This keeps the test workflow fast by
running both in parallel while avoiding IP allowlist issues.
Change conditions from github.event_name == 'pull_request' to
github.event.pull_request != null. This works for both pull_request
and push events on PR branches, ensuring coverage runs on every push.
The coverage workflow was failing on push events because it relied on
github.event.pull_request which is null for push events. This commit:

- Updates all job conditionals to check event_name and exclude main/v11.0.0
- Adds GitHub API lookups to determine PR number and base branch dynamically
- Updates PR comment commands to use dynamically determined PR number
- Updates base branch reference to use dynamically determined value

This ensures coverage reports are posted on every push to PR branches,
not just on pull_request events.
The previous approach tried to support both pull_request and push events
but hit IP allowlist restrictions when making GitHub API calls to lookup
PR information.

This commit simplifies the workflow to only run on pull_request events,
which provides all necessary context (PR number, base branch) without
needing any API calls. Coverage reports will update on pull_request
opened/synchronize events.

This avoids the IP allowlist issue entirely while still providing
comprehensive coverage reporting for all PR activity.
The workflow was only triggering on push events, which meant
github.event_name was never 'pull_request', causing coverage jobs
to be skipped.

This adds pull_request to the workflow triggers so it runs on both:
- push events: runs lint, test, integration, acceptance (no coverage)
- pull_request events: runs all jobs including coverage reporting

This ensures coverage reports are generated and posted to PRs.
Restricts push events to only trigger on main and v11.0.0 branches.
PRs will trigger via pull_request events, avoiding duplicate test runs.

This means:
- PR branches: run once via pull_request event (includes coverage)
- Main/v11.0.0: run via push event after merge
- No duplicate runs for PR branches

Added comments to guide adding long-lived feature branches if needed.
The base branch (v11.0.0) doesn't have c8 configured yet, so the
lcov.info file is empty, causing awk to fail with parse errors.

This adds error handling to:
- Default to "0" if coverage data is missing
- Check if base coverage exists before calculating diffs
- Show a simplified report without comparison if base coverage unavailable
- Include a note explaining that base branch doesn't have coverage yet

This allows the coverage workflow to succeed even when comparing against
branches without c8 configured.
Renames coverage-comment to coverage-check and replaces PR comment
functionality (blocked by IP allowlist) with a job that:
- Compares PR coverage against base branch coverage
- Outputs detailed comparison to job summary
- Fails the workflow if any coverage metric (lines, functions, branches) decreases
- Passes if base branch has no coverage configured

This ensures coverage never decreases while working around IP
allowlist restrictions that prevent posting PR comments.

Also fixes integer comparison error by using bash parameter
expansion to default empty values to "0".
Adds exception to coverage check: if all coverage metrics (lines,
functions, branches) are >= 90%, the check passes even if coverage
decreased from the base branch.

This allows for reasonable flexibility once high coverage is achieved
while still ensuring coverage never drops below 90%.
Moves ~100 lines of inline bash from ci.yml to scripts/ci/compare-coverage.sh for:
- Better testability and maintainability
- Ability to run locally for debugging
- Cleaner workflow file
- Reusable coverage comparison logic

The script:
- Accepts PR and base coverage directories as arguments
- Optionally fails on coverage decrease with --fail-on-decrease flag
- Outputs to both stdout and GitHub step summary when available
- Handles missing base coverage gracefully
- Implements 90% threshold exception

Can now be tested locally:
  ./scripts/ci/compare-coverage.sh coverage-pr coverage-base
Removes unnecessary/legacy excludes:
- **/*.spec.ts (no spec files exist in codebase)
- lib/**/* (legacy outDir, now using dist)
- .nyc_output/** (legacy from nyc migration)

Adds missing excludes for non-source directories:
- bin/**/* (runtime entry points)
- scripts/**/* (build/release tooling)
- tmp/**/* (temporary files)
- **/*.mjs (test helpers)

These directories should not be included in coverage metrics as they
are not application source code that ships with the CLI.
Removes c8 coverage instrumentation from 5 of 6 test matrix combinations
to significantly speed up CI execution. Coverage metrics don't vary by OS,
so we only need comprehensive coverage on one platform.

Changes:
- test:unit:justTest:ci: Now runs plain mocha without c8
- test:unit:justTest:ci:coverage: New script with c8 --all for full coverage
- CI workflow: Only ubuntu-22.x runs with coverage instrumentation
- Other 5 matrix combinations run faster without c8 overhead

Performance impact:
- Before: All 6 matrix combinations use c8 --all (~40 min each)
- After: Only 1 combination uses c8 --all, others run plain mocha (~20 min each)
- Expected: ~50% speedup for 5 of 6 test jobs
Windows runners use PowerShell by default, which doesn't support bash
syntax like [ ] tests and &&. Explicitly set shell: bash to ensure
the conditional test script works across all platforms.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants