From 488d58055ee38c6c74cf34b1ea01830f425b5c43 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 07:10:36 +0000 Subject: [PATCH 1/2] Echo the crash log into the job log before uploading it Mirrors workspace/policies/ci-test-diagnostics.md section 3.1: a failure-gated step that prints hs_err_pid*.log (first 200 lines) and the surefire dumpstream/dump into the job log, ahead of the existing crash-dumps upload. An artifact-only crash log is unreadable wherever egress to Azure Blob Storage is denied, and GitHub serves artifact bytes from nowhere else. Paths are repo-root-relative here, matching this repo's single-module layout and the existing upload's globs. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AnNYn8W1xuVxVJtyL34GyH --- .github/workflows/publish.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1968ed0..2862930 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -270,6 +270,33 @@ jobs: - name: Memory after tests if: always() run: free -h + # A forked test JVM that aborts leaves an hs_err_pid log and a surefire + # dumpstream -- both otherwise ONLY inside the artifact uploaded below, + # which is unreachable from anywhere that cannot fetch from Azure Blob + # (a phone, a restricted network, an agent sandbox). Echo them here so the + # aborting frame is readable from the run page itself. See + # ../workspace/policies/ci-test-diagnostics.md section 3.1. + - name: Print crash logs (on failure) + if: failure() + shell: bash + run: | + shopt -s nullglob + found=0 + for f in hs_err_pid*.log; do + found=1 + echo "===== $f (first 200 lines; full file in the uploaded artifact) =====" + sed -n '1,200p' "$f" + done + for f in target/surefire-reports/*.dumpstream target/surefire-reports/*.dump; do + found=1 + echo "===== $f =====" + cat "$f" + done + if [ "$found" = 0 ]; then + echo "No hs_err_pid*.log and no surefire dump/dumpstream was written." + echo "The fork died without the JVM writing a crash log -- the abort bypassed" + echo "the JVM error handler (native exit()/terminate) rather than raising a signal." + fi - name: Upload crash & surefire dumps if: failure() uses: actions/upload-artifact@v7 From 20b8d8ff1a98bc0f4e1c45eaa59525a174804582 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 08:36:42 +0000 Subject: [PATCH 2/2] Stop the crash-log step asserting an abort it cannot observe; supersede stale CI runs Two CI-hygiene changes, applied identically across the four Java repos. 1. "Print crash logs (on failure)" ran on `if: failure()` -- so on EVERY red job, the overwhelming majority of which are ordinary assertion failures that never write a crash log. It nonetheless printed "The fork died without the JVM writing a crash log", stated as fact, under a heading that also echoed perfectly healthy server logs. Reading a normal test failure, that is an invented crash to chase. It now reports the observation, says plainly that no file is the EXPECTED case for a normal failure, and names the one signature -- "The forked VM terminated without properly saying goodbye", or an exit with no test results -- that would actually justify the JVM-abort conclusion. 2. `publish.yml` had no `concurrency:` group, so every push started a full parallel pipeline while superseded ones kept draining; four were live at once in one session, which makes "what is CI saying right now" ambiguous and wastes runner time on results nobody reads. `cancel-in-progress` is scoped to `pull_request` ONLY: a push to main or a v* tag is a release path and cancelling one midway could leave a partially published artifact set, so those always run to completion. Verified: every touched workflow parses with the expected concurrency mapping. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AnNYn8W1xuVxVJtyL34GyH --- .github/workflows/publish.yml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2862930..ee5bf0f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,6 +15,21 @@ on: type: boolean default: false +# Supersede an in-flight run when a PR branch is pushed again. +# +# Without this every push starts a full parallel pipeline and the older ones keep +# draining -- four were live at once during one session, which makes "what is CI +# saying right now" genuinely ambiguous and wastes a lot of runner time on results +# nobody will read. +# +# cancel-in-progress is deliberately scoped to pull_request ONLY. A push to main or +# to a v* tag is a release path: cancelling one midway could leave a partially +# published set of artifacts, so those always run to completion even if another push +# lands behind them. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + permissions: contents: read @@ -294,8 +309,15 @@ jobs: done if [ "$found" = 0 ]; then echo "No hs_err_pid*.log and no surefire dump/dumpstream was written." - echo "The fork died without the JVM writing a crash log -- the abort bypassed" - echo "the JVM error handler (native exit()/terminate) rather than raising a signal." + echo + echo "For an ordinary test failure that is EXPECTED, not a finding: this step runs on" + echo "any job failure, and an assertion failure, a timeout or a compile error writes no" + echo "crash log. Read the surefire output above for the real cause." + echo + echo "It points at a JVM-level abort only if the log ALSO shows a fork ending abnormally" + echo "-- 'The forked VM terminated without properly saying goodbye', or an exit with no" + echo "test results. In that case the abort bypassed the JVM error handler (a native" + echo "exit()/terminate() rather than a raised signal), which is why no file was written." fi - name: Upload crash & surefire dumps if: failure()