Repository navigation
CAMEL-24290: ci - report tests that only passed after a retry - #25598
Conversation
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
apupier
left a comment
There was a problem hiding this comment.
I think it would be easier to maintain and easier to leverage reported flaky tests by reusing the same tool than for Jenkins. Which is develocity.
davsclaus
left a comment
There was a problem hiding this comment.
This is a rules-and-conventions review from OSS Helper — it does not replace CodeRabbit, Sourcery, SonarCloud, or a dedicated static analyzer.
Overall this is a well-scoped, well-tested CI visibility change. To verify rather than just read it, I checked out the branch, ran all 18 unit tests locally (all pass), confirmed the forbid_dtd=True XXE defense actually rejects the UTF-16-hidden-DOCTYPE fixture, confirmed reportRecoveredFlakes genuinely runs on the always-path of incremental-build.sh (after the build's exit code is captured, before the final exit $ret), confirmed the new astral-sh/setup-uv SHA pin matches the v10.0.1 tag, and confirmed both commits carry the Co-Authored-By trailer per project convention. JIRA CAMEL-24290 is Open/Unassigned and the PR's "implements one bullet, doesn't claim the umbrella ticket" framing matches that.
One correctness issue below (inline), plus two non-blocking notes:
Recommend confirming before merge: this is a fork PR, and the only checks that ran are pr-ci-scripts-validation.yml (isolated unit tests) plus the PR-id uploader/dependency-review. pr-build-main.yml — the workflow that actually exercises the new flake-label input, the new install-uv composite-action step, and the new "Upload recovered-flake report" step — has no run at all for this branch yet (checked via gh run list --workflow=pr-build-main.yml), most likely GitHub's approval gate for fork PRs touching .github/workflows/** / .github/actions/**. The unit tests validate collect-flakes.py's logic in isolation but can't prove the composite-action wiring (uv landing on PATH, FLAKE_LABEL threading through, the hashFiles('flakes.json') upload gate) works on a live runner. Worth a maintainer approving/triggering that run before merging.
Minor/optional: in pr-build-main.yml, the new "Upload recovered-flake report" step isn't gated on !matrix.experimental the way the "Save PR number and test comment" step is. Harmless today (the matrix has no experimental: true entry), but worth a thought if an experimental JDK entry is ever added.
Claude Code on behalf of Claus Ibsen (@davsclaus)
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Thanks for the suggestion, @apupier. I checked whether Develocity could stand in for this instead of the custom flake report, and it can't cover the case this feature is actually for.
So for a fork PR (the majority of external Camel contributions), and the exact audience that can't see Jenkins internals and needs inline visibility most - no Develocity scan ever gets published. There's nothing to leverage there. Where I think you're right: for merged builds on trusted ASF Jenkins infra, Develocity's cross-build flaky-test view is a better long-term signal than one job's retry report. That's an argument for wiring Develocity on the trusted side too, not a substitute for PR-time visibility on the fork side. I can file a follow-up for that if it's useful. Claude Code on behalf of Adriano Machado (@ammachado) This reply was generated by an AI agent and may contain inaccuracies. Please verify before relying on it. |
There was a problem hiding this comment.
I'm a bit sceptical to the fact that contributors will benefit to the "flaky but still passing" report, especially given that there are a lot of them today. Either they will look and can be confused because it is an existing flaky one, or they won't care as it is passing anyway.
The "flaky but failing" are already looked by almost nobody.
Anyway, if i'm alone to think that that's fine and we can go ahead after other pints clarified/tackled.
Can you try to have these jobs triggered before we merge the PR to ensure that it is not breaking the main branch please?
|
Addressing the two points from this review: CI not triggered: Root cause is Value of the "flaky but still passing" report: Fair concern, and I don't have a strong counter beyond what's in the original commit message — for a first-time or occasional contributor whose PR fails, then passes on retry, seeing why inline (rather than having to know Jenkins exists, find the right job, and dig through console logs) lowers the bar to understanding "was this me or was this the test." I agree the "flaky but failing" case is the one nobody reads today; I'm not trying to fix that habit with this PR, just make the passing-after-retry case visible where a PR author will actually see it. Open to gating this behind something narrower (e.g. only show it if the flake count is small, or only on first-time-contributor PRs) if that addresses the noise concern — let me know if you'd like that adjustment before merge. Claude Code on behalf of Adriano Machado (@ammachado) This reply was generated by an AI agent and may contain inaccuracies. Please verify before relying on it. |
9802c55 to
e2869c2
Compare
|
🧪 CI tested the following changed modules:
🔬 Scalpel shadow comparison
|
oscerd
left a comment
There was a problem hiding this comment.
The mechanism is sound and nicely tested — recovered-flake selection (only flakyFailure/flakyError), document-order first-failure, markdown escaping, and DTD rejection all check out, and the step never fails the build. Nice work.
Two things before merge:
- The PR is currently conflicting with main (
mergeStateStatus: DIRTY) — it'll need a rebase/conflict resolution. Likely sites are the recently-changed.github/actions/incremental-build/incremental-build.shand the PR-build workflows. - It bundles an unrelated docs commit (
0e60db5, thenav.adoc"Resources & Guides" reorg). Consider splitting that into its own PR/JIRA — the nav change itself looks correct, it's just a separate concern (and uses achore(docs):subject rather thanCAMEL-XXXX:).
Reviewed with Claude Code (Opus 4.8) on behalf of @oscerd. This is an AI-generated review and may contain inaccuracies; please verify before applying.
Surefire retries failing tests: surefire.rerunFailingTestsCount defaults to
2 in the `full` profile of parent/pom.xml, and both CI systems pass it again
explicitly. A test that fails and then passes within those attempts leaves
the build green and leaves nothing in the console output, so today the retry
is invisible.
parse_errors.sh cannot cover this for two independent reasons: it parses the
.txt reports, which carry no flake data at all, and it only runs inside the
build-failure branch, while a recovered flake exits 0.
Add collect-flakes.py, invoked on the always-path of incremental-build.sh.
It walks **/target/{surefire,failsafe}-reports/TEST-*.xml and reports every
<testcase> carrying <flakyFailure>/<flakyError>. Tests with <rerunFailure>
failed every attempt and already fail the build, so they are excluded.
Two outputs: a section appended to the PR comment and job summary, and
flakes.json uploaded as a workflow artifact. Develocity publishes build
scans only when authenticated, so fork PRs produce none; that artifact is
the only per-PR record of flakiness available.
No time figure is reported. Surefire records no per-attempt timing and
<testcase time> reflects only the final successful attempt, so a
minutes-lost number would understate timeout-driven flakes specifically.
XML is parsed with defusedxml, declared inline via PEP 723 and resolved by
uv. A pre-parse byte scan for <!DOCTYPE is not sufficient: it misses a
UTF-16 document, where the marker is interleaved with NUL bytes.
testdata/TEST-utf16-doctype-rejected.xml pins that case.
Failures are logged and skipped; this step must never fail a job whose
verdict is already decided.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correctness: - Pass forbid_dtd=True explicitly. defusedxml only forbids entity *declarations* by default, so a bare <!DOCTYPE .. SYSTEM ..> parsed cleanly even though the docstring, the test class name and the docs all claimed such a report is refused. - Read flaky attempts in document order. Collecting per tag returned the first flakyFailure even when a flakyError came first, so the "First failure" column named the wrong attempt, hiding the timeout that is typically the real cause. - HTML-escape failure messages. GitHub treats "expected: <true> but was: <false>" as raw HTML and strips it, emptying the column for exactly the assertion messages that produce most flake reports. JDK matrix attribution: - Name the JDK in the section and record it in flakes.json (flake-label action input, --label on the script). The PR-comment artifact uploads with overwrite: true on the grounds that content is identical across the matrix; flake data is the one part that is not, so an unattributed section left the reader unable to tell which JDK a flake came from. - Upload flakes.json from main-build.yml as flakes-main-java-<version>. The action already produced it there and it was being discarded, even though main-branch flakes are the least noisy signal available. Other: - Replace the **/target glob with a pruned os.walk. After a full build every module's target/ holds thousands of class and generated-source directories that cannot contain a report. - Write the "Tested modules" header before anything else appends to the job summary, so the reader gets what was built before what happened while building it. - Fix "1 retried attempts" and a test docstring naming a command that fails with ModuleNotFoundError. Adds three tests (18 total) plus fixtures for attempt ordering and for a DOCTYPE that declares no entities of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Escape module, class, and test name through _cell() in the markdown table row, not just the failure message. A test name containing '|' (a JUnit 5 @ParameterizedTest display name, or a Camel URI/DSL parameterized test) otherwise splits the row into the wrong columns. - Gate the "Upload recovered-flake report" step on !matrix.experimental for consistency with the other post-build steps. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pins `version`/`python-version` on the setup-uv step so the recovered-flake collector runs against a fixed uv (0.12.5) and Python (3.11) rather than whatever "latest" resolves to at run time, even though the setup-uv action itself is already SHA-pinned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
e2869c2 to
cbda9e9
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review after 3 new commits (d0b5664, 60596ee, cbda9e9). All findings from previous reviews are addressed.
Previous findings — status:
_cell()not applied to module/test/classname: ✅ fixed — all four columns now go through_cell()forbid_dtd=Truenot explicit: ✅ fixed — present inparse_report- document-order attempt iteration: ✅ fixed —
[el for el in testcase if el.tag in FLAKY_TAGS] !matrix.experimentalguard on artifact upload: ✅ fixed — inpr-build-main.yml- uv/Python version pinning: ✅ fixed in
action.yaml—version: "0.12.5",python-version: "3.11" - nav.adoc unrelated commit: ✅ removed (no longer in PR files)
- Conflicts with main: ✅ resolved —
mergeStateStatus: CLEAN pr-build-main.ymllive run: ✅ confirmed (CI comment with action run link present)
One inconsistency remains inline.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
Co-authored-by: Guillaume Nodet - AI Bot <gnodet+bot@gmail.com>
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review after commit f03024aa ("Update .github/workflows/pr-ci-scripts-validation.yml").
The uv/Python version pinning is now applied to pr-ci-scripts-validation.yml — the intent is correct. However, the commit introduced a broken YAML structure that will prevent the workflow from running at all (see inline). All other previous findings remain addressed.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
The review suggestion applied in f03024a replaced a narrower line range than it covered, leaving a duplicate empty "Install uv" step and folding the test step's `run:` into the `uses:` step. GitHub Actions rejects both, so the validation workflow failed to run. Restore the separate "Test the incremental build scripts" step while keeping the uv 0.12.5 / Python 3.11 pins the suggestion intended to add. Net change versus cbda9e9 is only those two pin lines. _Claude Code on behalf of Adriano Machado (@ammachado)_ _This was generated by an AI agent and may contain inaccuracies. Please verify before relying on it._ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review after commit 6a89cfc ("Update .github/workflows/pr-ci-scripts-validation.yml").
All previous findings are now addressed.
Previous findings — status:
_cell()not applied to module/test/classname: ✅ fixed — all four columns now go through_cell()(diff lines 329–332 ofcollect-flakes.py)forbid_dtd=Truenot explicit: ✅ confirmed present- Broken YAML in
pr-ci-scripts-validation.yml(duplicate step +run:onuses:step): ✅ fixed —6a89cfcsplits the combined block into two clean, well-formed steps;pr-ci-scripts-validation.ymlCI ran and passed (SUCCESS) - uv/Python version pinning in validation workflow: ✅ confirmed — both
version: "0.12.5"andpython-version: "3.11"are present in theInstall uvstep - All other previous findings: ✅ remain addressed
Current state:
mergeStateStatus: CLEAN- All CI checks passing
_cell()applied consistently to all four table columns- DTD rejection tested with three fixtures (entity expansion, bare SYSTEM DOCTYPE, UTF-16-encoded DOCTYPE)
reportRecoveredFlakescorrectly on the always-path ofincremental-build.sh(after exit code is captured)- No unrelated commits in the PR
Code is solid. Ready to merge.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
|
this is causing build failure https://github.com/apache/camel/actions/runs/36748827776/job/110001865059#step:26:6 |
|
@apupier thanks for spotting this. uv is only used by the recovered-flake report, which the incremental build does not reach on every path (e.g. a main build that tests no module), so the setup-uv post step found no cache directory and failed the job. Fixed on main in 6d8f350 by setting Claude Code on behalf of davsclaus |
Description
Part of CAMEL-24290 (improve CI to support larger workload). This is the visibility step: it makes an existing, currently invisible cost measurable before anything is changed about how tests run.
The problem
Surefire retries failing tests.
surefire.rerunFailingTestsCountdefaults to2in thefullprofile ofparent/pom.xml(activated by!quickly), and both CI systems pass it again explicitly (Jenkinsfile:17,install-mvnd/action.yml:71).A test that fails and then passes within those attempts is a recovered flake. The build goes green, nothing appears in the console output, and the retry leaves no trace. We are already paying up to 3x the runtime of every flaky test, and the retry is precisely the thing that stops anyone noticing.
parse_errors.shcannot cover this, for two independent reasons:.txtreports, which contain no flake data at all (verified:grep -c "Flake"on a surefire 3.5.6.txtreport with a known flake returns0).if [[ ${ret} -ne 0 ]]branch, and a recovered flake exits0.The change
collect-flakes.pyruns on the always-path ofincremental-build.sh. It walks the reactor fortarget/{surefire,failsafe}-reports/TEST-*.xmland reports every<testcase>carrying<flakyFailure>/<flakyError>. Tests with<rerunFailure>/<rerunError>failed every attempt and already fail the build, so they are deliberately excluded.The walk prunes rather than globbing: after a full build every module's
target/holds thousands of class and generated-source directories, none of which can hold a report, so**/target/...would descend into all of them.Two outputs:
flakes.json, uploaded asflakes-java-<version>on PRs andflakes-main-java-<version>onmain..mvn/develocity.xmlpublishes build scans only when authenticated, so fork PRs produce no Develocity data. This artifact is the only per-PR record of flakiness we can accumulate.The section names its JDK
The PR-comment artifact uploads with
overwrite: trueacross the JDK matrix, justified by the existing comment inpr-build-main.yml: the content is identical across entries because the same modules are tested. Flake data is the one part where that does not hold. If JDK 17 flakes and JDK 25 does not, whichever entry finishes last decides what the comment shows.Rather than restructure the comment/artifact strategy in this PR, the section names the entry it came from (
flake-labelon the action,--labelon the script, also recorded inflakes.json):Last writer still wins, so the comment shows one JDK's flakes. What the label buys is that a shown section is attributable, and that an empty section can no longer be silently confused between "no flakes" and "the other JDK overwrote it". The per-JDK
flakes-*artifacts remain the complete record, and the label inflakes.jsonlets aggregation distinguish a test that only flakes on one JDK from one that flakes everywhere.Deliberate non-goals
No time figure is reported. Surefire records no per-attempt timing, and
<testcase time>reflects only the final successful attempt. Estimating cost from it would systematically understate timeout-driven flakes, which are the common kind. Counts and test names are honest; a fabricated minutes-lost number would poison the evidence this is meant to gather.Nothing about test execution changes. No test is skipped, quarantined, or retried differently. This PR only reports.
Notes for reviewers
uv run(PEP 723 describes inline dependency declaration similar tojbang).uvis installed by the action (astral-sh/setup-uv, pinned by SHA) so the action stays self-contained for downstream repos that reuse it. Both theuvversion and the Python version it resolves against are pinned explicitly (version: "0.12.5",python-version: "3.11") — the action's own SHA pin does not fix whatuvitself installs at run time, sinceversiondefaults to "latest" absent apyproject.toml/uv.tomlpin. The same pins are applied to theInstall uvstep inpr-ci-scripts-validation.yml, so the tests run against the versions production uses.defusedxmlrather than the stdlib, withforbid_dtd=Truepassed explicitly.defusedxmlforbids entity declarations by default but not a bare<!DOCTYPE .. SYSTEM ..>, so the default alone does not enforce "Surefire never emits a DOCTYPE, therefore any report carrying one did not come from the build".testdata/TEST-doctype-no-entities-rejected.xmlpins this.<!DOCTYPEis not sufficient either: it misses a UTF-16 document, where the marker is interleaved with NUL bytes, and the entity then expands normally.testdata/TEST-utf16-doctype-rejected.xmlpins that case. (The stdlib-only alternative of installing pyexpat entity handlers is unavailable:ET.XMLParser.parserno longer exists on current CPython.)findall("flakyFailure") + findall("flakyError")) returns them grouped by tag, so the "First failure" column named the wrong attempt whenever the two kinds mixed, hiding the timeout that is typically the real cause.expected: <true> but was: <false>as raw HTML and strips it, which emptied the column for exactly the assertion messages that produce most flake reports.main()always returns0, because by that point the build verdict is already decided.TEST-utf16-doctype-rejected.xmlshows as binary in the diff. That is expected for UTF-16 with a BOM.Verification
pr-ci-scripts-validation.yml(path filter extended to.github/actions/incremental-build/**).GITHUB_STEP_SUMMARYset and unset states.Path,strand relative.roots, at root, one-level and three-level module depths.Target
mainbranch)Tracking
CAMEL-24290. Note the JIRA issue is currently Unassigned and this PR does not claim it; it implements one bullet from the ticket's list.
Apache Camel coding standards and style
I checked that each commit in the pull request has a meaningful subject line and body.
I have run
mvn clean install -DskipTestslocally from root folder and I have committed all auto-generated changes.Not run, and not applicable: this PR touches only
.github/**(Python, YAML, shell, Markdown). No Java sources, POMs, or generated files are involved, so no auto-generated content can change.pr-build-main.ymlitself carriespaths-ignore: .github/**for exactly this reason.AI-assisted contributions
Co-authored-bytrailers) and the PR description identifies the AI tool used.Written with Claude Code (Claude Opus 5 and Claude Sonnet 5, across commits). Every AI-assisted commit carries a
Co-Authored-Bytrailer (f03024aais a reviewer's suggestion applied through the GitHub UI and carries that reviewer's trailer).Claude Code on behalf of @ammachado