File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -319,8 +319,26 @@ jobs:
319319 ls -la .coverage* 2>/dev/null || echo "no .coverage data files found"
320320
321321 - name : Combine coverage and enforce combined threshold
322+ id : coverage-gate
322323 run : make ci-tests-coverage-check
323324
325+ # Single deterministic Codecov upload: the combined report from ALL legs is
326+ # sent once here, instead of each leg uploading separately. Codecov then
327+ # shows one stable number (no mid-run partial-merge flapping) and this needs
328+ # no hardcoded leg count — it auto-scales to any number of provider legs.
329+ # if: always() so the report uploads even when the threshold gate fails
330+ # above (the job still fails on the gate; Codecov still gets the data).
331+ - name : Upload combined coverage to Codecov
332+ if : always()
333+ continue-on-error : true
334+ uses : codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
335+ with :
336+ token : ${{ secrets.CODECOV_TOKEN }}
337+ files : coverage-combined.xml
338+ name : combined
339+ # Overwrite any prior partial report for this commit with the full merge.
340+ override_commit : ${{ github.event.pull_request.head.sha || github.sha }}
341+
324342 # Single stable required status check for branch protection. Replaces listing
325343 # every individual job name (which drifts silently when jobs are renamed).
326344 # Register "CI Passed" as the sole required check in branch protection.
Original file line number Diff line number Diff line change @@ -185,15 +185,12 @@ jobs:
185185 coverage-*.xml
186186 .coverage*
187187
188- - name : Upload coverage to Codecov
189- if : inputs.upload-coverage && always()
190- continue-on-error : true
191- uses : codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
192- with :
193- token : ${{ secrets.CODECOV_TOKEN }}
194- files : coverage-${{ inputs.test-type }}${{ inputs.provider != '' && format('-{0}', inputs.provider) || '' }}.xml
195- flags : ${{ inputs.test-type }}${{ inputs.provider != '' && format('-{0}', inputs.provider) || '' }}
196- name : ${{ inputs.test-type }}${{ inputs.provider != '' && format('-{0}', inputs.provider) || '' }}-tests
188+ # NOTE: per-leg coverage is NOT uploaded to Codecov here. The .coverage
189+ # data files are uploaded as artifacts (above) and the coverage-combine
190+ # fan-in job in ci-tests.yml merges them and uploads ONE combined report
191+ # to Codecov. A single upload gives Codecov one deterministic number
192+ # (no mid-run partial-merge flapping) and auto-scales to any number of
193+ # provider legs without a hardcoded after_n_builds count.
197194
198195 - name : Upload test results to Codecov
199196 # Gated on upload-coverage: legs without pytest (e.g. ui-smoke) produce
Original file line number Diff line number Diff line change 1+ # Coverage is uploaded to Codecov exactly ONCE, from the ci-tests
2+ # coverage-combine job, which merges every leg's .coverage data first. A single
3+ # upload gives Codecov one deterministic number (no mid-run partial-merge
4+ # flapping) and needs no hardcoded leg count — it auto-scales to any number of
5+ # provider legs.
16coverage :
27 status :
38 project :
@@ -22,7 +27,14 @@ flags:
2227 - src/orb/
2328 carryforward : true
2429
30+ # Kept in sync with [tool.coverage.run] omit in pyproject.toml so Codecov's
31+ # view matches the combined report we upload.
2532ignore :
2633 - " tests/"
2734 - " docs/"
2835 - " scripts/"
36+ - " src/orb/k8s_legacy/**" # own separate test suite; runs outside the main legs
37+ - " **/tests/**" # test files under src/ are not production source
38+ - " src/orb/__main__.py" # thin process entrypoints — not unit-tested
39+ - " src/orb/run.py"
40+ - " src/orb/interface/server_daemon.py"
Original file line number Diff line number Diff line change @@ -168,6 +168,9 @@ ci-tests-infrastructure: ## Run infrastructure tests only (matches ci.yml infra
168168ci-tests-coverage-check : # # Combined-coverage gate: merge per-leg data + enforce threshold
169169 @echo " Combining per-leg coverage data and checking combined threshold ($( COVERAGE_THRESHOLD) %)..."
170170 $(call run-tool,coverage,combine)
171+ # Emit the merged XML BEFORE the threshold check so it is always produced
172+ # for the single Codecov upload, even when the gate below fails.
173+ $(call run-tool,coverage,xml -o coverage-combined.xml)
171174 # `coverage report` uses --fail-under (the pytest-cov spelling
172175 # --cov-fail-under is not valid for the coverage CLI).
173176 $(call run-tool,coverage,report --fail-under=$(COVERAGE_THRESHOLD ) )
Original file line number Diff line number Diff line change @@ -545,6 +545,20 @@ env = []
545545[tool .coverage .run ]
546546source = [" src/orb" ]
547547branch = true
548+ omit = [
549+ # k8s_legacy is the legacy HostFactory plugin: it has its own test suite
550+ # under src/orb/k8s_legacy/tests (in pytest testpaths) that runs separately
551+ # from the main legs, so it always shows 0% here and unfairly drags the
552+ # combined number. Exclude it from the main coverage report.
553+ " src/orb/k8s_legacy/*" ,
554+ # Test files that live under src/ are not production source.
555+ " */tests/*" ,
556+ # Thin process entrypoints / daemon lifecycle: not meaningfully unit-tested
557+ # (exercised via subprocess/integration, not the coverage-uploading legs).
558+ " src/orb/__main__.py" ,
559+ " src/orb/run.py" ,
560+ " src/orb/interface/server_daemon.py" ,
561+ ]
548562
549563[tool .coverage .report ]
550564precision = 2
You can’t perform that action at this time.
0 commit comments