Repository navigation
feat(stats): report testrun stats to the tcache - #3717
Open
OlufemiAdeOlusile wants to merge 4 commits into
Open
OlufemiAdeOlusile wants to merge 4 commits into
OlufemiAdeOlusile wants to merge 4 commits into
Conversation
OlufemiAdeOlusile
requested review from
mkoura and
saratomaz
as code owners
September 29, 2026 04:53
OlufemiAdeOlusile
force-pushed
the
add-stats-upload
branch
from
October 12, 2026 03:13
a0e72cd to
f89bf8b
Compare
Add scripts/stats_json.py, which builds a small JSON document for one testrun from its Allure results. The counts reuse the grouping in count_test_results.py, so the --skipall registration pass is not counted twice. The document adds the run identity, the wall clock duration, the software versions and the CLI coverage summary. Add runner/report_stats.sh, which uploads that document to the tcache /stats endpoint. regression.sh calls it at the end, and node_upgrade_pytest.sh calls it after each upgrade step. It is best effort: callers use `|| :`, so the pytest exit code never changes, and curl has a 60 second limit, so a tcache that does not answer cannot hold up the testrun. The upload is off by default. It runs only when ENABLE_TCACHE_STATS is truthy and the TCACHE_BASIC_AUTH and TCACHE_URL secrets are set. CI reads the flag from the ENABLE_TCACHE_STATS repository variable.
Only a manual run with a typed release name set CI_TESTRUN_NAME, so every other CI run was reported to the tcache as `local`, and the kinds of run could not be told apart. Set TCACHE_TESTRUN_NAME in each workflow's runner/env_* file. A name typed for a manual run still wins, and the document now carries "release": true for such a run. Both names are scrubbed before one is chosen, so a typed name that scrubs to nothing falls back to the workflow's name. The nightly, nightly-cli and nightly-dbsync workflows do not pass the tcache secrets yet, so their names take effect once they do (#3624).
Add the number of `transaction submit` invocations from the CLI coverage report to the stats document, summed over every era group. The invocation count and this number show how much the tests exercise the CLI more reliably than the coverage percentage, which counts the same commands once per era.
Use GITHUB_RUN_ID instead of GITHUB_RUN_NUMBER: the number counts per workflow, so the same typed name in two workflows could share a run id. Scrub testrun names to ASCII letters, digits, `_` and `-` with a regex. `str.isalnum()` kept non-ASCII characters, which the tcache refuses. Mark a run as filtered when DESELECT_FROM_FILE is set as well as MARKEXPR: a skip-passed rerun runs only part of the suite. Show curl's error message when the upload cannot connect, type the CLI counters so mypy passes, and check scripts/stats_json.py with mypy.
OlufemiAdeOlusile
force-pushed
the
add-stats-upload
branch
from
October 19, 2026 02:08
66a9efb to
724f25c
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it does
A test run now reports its test counts to the tcache.
The tcache gained a
/statsendpoint. Nothing called it. This adds the clienthalf.
scripts/stats_json.pyrunner/report_stats.shIt is wired into both test scripts that produce Allure results:
runner/regression.shstep=mainrunner/node_upgrade_pytest.shstep1,step2,step3How it works
scripts/stats_json.pydoes not count the tests itself.count_test_results.pyalready groups Allure result files byhistoryId, sothe
--skipallregistration pass ofrun_tests.shis not counted twice. Thisscript reuses that logic. It adds only the fields a JUnit report cannot carry:
the run identity, the timing, the software versions, and the CLI invocation and
submitted-transaction counts from
cli_coverage.json.runner/report_stats.shruns aftercreate_results.sh, which is what createsthe Allure directory. Every call is
|| :, so a failed upload cannot mask thepytest exit code. curl has a 60 second limit, so a tcache that does not answer
cannot hold up the testrun.
How each run is named
Each workflow's
runner/env_*file now setsTCACHE_TESTRUN_NAME:nightly,nightly-cli,nightly-dbsync,nightly-upgrade,upgrade,regressionandregression-dbsync. A name typed for a manual run (CI_TESTRUN_NAME) stillwins, and that run is marked
"release": true. Without this, every CI runexcept a typed one arrived as
local.The run id is
GITHUB_RUN_ID.GITHUB_RUN_NUMBERcounts per workflow, so thesame typed name in
regression.yamlandregression-dbsync.yamlcould shareone.
The nightly, nightly-cli and nightly-dbsync workflows do not pass the tcache
secrets yet, so their names take effect once they do (#3624).
Turning it on
The upload is off by default. It runs only when
ENABLE_TCACHE_STATSistruthy, checked with
is_truthylike the otherENABLE_*flags. In CI, setthe
ENABLE_TCACHE_STATSrepository variable totrue. Locally, export it.The tcache secrets alone do not turn it on, because the
pypassedand/importsteps use the same secrets.The document is about 660 bytes:
{ "schema": 1, "project": "cardano-node-tests", "testrun_name": "node-1050", "run_id": "18562938417", "step": "main", "origin": "ci", "timestamp": "2026-05-30T23:39:29.232000+00:00", "duration": 4527.316, "exit_code": 0, "filtered": false, "release": true, "counts": {"total": 2145, "passed": 1892, "failed": 0, "broken": 0, "skipped": 253}, "quality": {"never_run": 0, "no_history_id": 0, "read_errors": 0}, "versions": {"cardano_node": "10.5.0", "cardano_cli": "10.11.1.0", "db_sync": null}, "commands": {"count": 213130, "submits": 9990, "coverage_pct": 31.0} }Auth
It reuses
TCACHE_BASIC_AUTH. No new secret is needed.TCACHE_URLpoints at the/resultsprefix, so the script replaces thatsuffix with
/stats. The nightly/historyupload does the same.The secrets were not in scope for the steps that run the test scripts.
They were set only on the steps that call the tcache with
curldirectly.Without this the upload would have skipped silently and recorded nothing.
upgrade_reusable.yamldid not even declare them, so no caller could passthem. Fixed in
regression_reusable.yaml,upgrade_reusable.yaml,nightly_upgrade.yamlandupgrade.yaml.Why the upgrade path is included
It was going to be left out, because you scoped this to
regression.shandthe non-upgrade scripts. That reason was that
node_upgrade_pytest.shemitsno JUnit XML. Still true, but the counts come from Allure now, and that script
calls
create_results.shthree times. Thestepcolumn already exists forthis. Say the word and I will drop it.
No CLI coverage is sent for the upgrade steps.
cli_coverage.shruns in thefinishstep and covers the whole run, so attributing it to one step wouldmisreport it.
Details that are easy to get wrong
parallel under xdist, so the sum of the per-test durations is many times the
elapsed time.
never_runmeans the run was interrupted. It counts tests theregistration pass registered that never got a real result, so every count is
a floor. It is a subset of
skipped._and-, thesame set the workflows keep, but without depending on the locale. It drops
dots, so
node-10.5.0becomesnode-1050. That is lossy, and the tcacheitself accepts dots. Matching the existing calls matters more, because a
different name here could not be joined to the
/importrows. Happy tochange this if you prefer the dots kept everywhere.
filteredwhenMARKEXPRorDESELECT_FROM_FILEis set. A skip-passed rerun sets the second one.Testing
No unit tests are added, the same as for
count_test_results.py. Allframework tests pass. No new ruff, shellcheck, actionlint or zizmor
findings.
scripts/stats_json.pyis added to the mypy hook, which did notcheck it, and it passes.
Verified against a real Allure directory of 4269 result files. The counts match
count_test_results.pyexactly: 2145 total, 1892 passed, 253 skipped. Thedocument was uploaded through
report_stats.shto a real tcache undergunicorn and read back unchanged. The three upgrade steps were checked too:
one run id holding
step1,step2andstep3with exit codes 0, 1 and 2.The failure cases were run through the same calling pattern as
regression.sh: flag off, secrets missing, tcache down, a wrong password,tcache returning 404 or 500, and a tcache that accepts the connection but
never answers. In every case the caller kept its exit code, and curl now
prints why the upload failed.
Each env file was loaded the way GitHub loads it and uploaded to a local
tcache. Every workflow stored its own name, the typed release name won and was
marked as a release, and
DESELECT_FROM_FILEmarked the run as filtered.Depends on
The server side, mkoura/testing-results-cache#25. Because the upload is off by
default, this can merge first. Turn the flag on after
/statsis deployed.🤖 Generated with Claude Code