From 72244ad5ceb294364df6e2662eb4a402c508a8a8 Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Sun, 20 Sep 2026 08:35:05 -0600 Subject: [PATCH 1/2] ci: retry the scalafix lint when Maven cannot resolve the plugin --- .github/workflows/pr_build_linux.yml | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_build_linux.yml b/.github/workflows/pr_build_linux.yml index cbb73fab985..2da3fb44941 100644 --- a/.github/workflows/pr_build_linux.yml +++ b/.github/workflows/pr_build_linux.yml @@ -202,9 +202,36 @@ jobs: restore-keys: | ${{ runner.os }}-java-maven- + # Split a Maven Central hiccup from a real lint violation. Resolving the + # scalafix plugin descriptor has failed mid-job before, leaving + # "No plugin found for prefix 'scalafix'" and a red lint that looks + # exactly like a violation (pull request #6035). The scalafix-syntactic + # job above solves the same problem by retrying the download and then + # linting offline; Maven has no equally safe offline mode here, because + # the goal resolves rule artifacts as it runs, so retry instead. Stop the + # moment scalafix reports actual errors, so a genuinely broken pull + # request is not rebuilt four times. Backoff matches + # .github/actions/maven-bootstrap: 10s, 20s, 40s plus 0-4s of jitter. - name: Run scalafix check run: | - ./mvnw -B package -DskipTests scalafix:scalafix -Dscalafix.mode=CHECK -Psemanticdb ${{ matrix.profile.maven_opts }} + set -o pipefail + for attempt in 1 2 3 4; do + if ./mvnw -B package -DskipTests scalafix:scalafix -Dscalafix.mode=CHECK \ + -Psemanticdb ${{ matrix.profile.maven_opts }} 2>&1 | tee scalafix-check.log; then + exit 0 + fi + if grep -q 'Scalafix invoked with errors' scalafix-check.log; then + echo "::error::scalafix reported lint violations; see the log above." + exit 1 + fi + if [ "$attempt" -eq 4 ]; then + echo "::error::scalafix could not run after $attempt attempts; no violation was reported." + exit 1 + fi + delay=$((10 * (1 << (attempt - 1)) + RANDOM % 5)) + echo "::warning::scalafix attempt $attempt failed without reporting a violation; retrying in ${delay}s." + sleep "$delay" + done - name: Setup Node.js uses: actions/setup-node@v7 From 49ff6ce03b75a63fc2a785b78ac4ccc2044267d5 Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Sun, 11 Oct 2026 20:06:48 -0600 Subject: [PATCH 2/2] ci: retry the scalafix lint only on download failures, and pass -U on retries Address review feedback on the retry loop: - Write the captured log to a mktemp file instead of the checkout, so a successful run no longer fails the clean-tree check on scalafix-check.log. - Retry only when Maven's error names a download (No plugin found for prefix, could not be resolved, Could not transfer, or the scala.tools.nsc.Main ClassNotFoundException a missing compiler jar produces). A lint violation or a compile error fails on the first attempt. Replayed against 111 Lint Java step logs from CI since 2026-09-20, this retries exactly the six download failures and none of the 30 compile errors or 25 lint violations. - Pass -U on retries. Two of the five plugin failures since 09-20 were a 404 from Central, and Maven caches a 404 in the local repository, so a plain retry fails the same way until the update interval elapses. - Run the step under bash. The container default is sh (dash), where $RANDOM is empty and the jitter was silently zero. --- .github/workflows/pr_build_linux.yml | 39 +++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/pr_build_linux.yml b/.github/workflows/pr_build_linux.yml index b3820ec92da..d782258e056 100644 --- a/.github/workflows/pr_build_linux.yml +++ b/.github/workflows/pr_build_linux.yml @@ -202,35 +202,44 @@ jobs: restore-keys: | ${{ runner.os }}-java-maven- - # Split a Maven Central hiccup from a real lint violation. Resolving the - # scalafix plugin descriptor has failed mid-job before, leaving - # "No plugin found for prefix 'scalafix'" and a red lint that looks - # exactly like a violation (pull request #6035). The scalafix-syntactic - # job above solves the same problem by retrying the download and then - # linting offline; Maven has no equally safe offline mode here, because - # the goal resolves rule artifacts as it runs, so retry instead. Stop the - # moment scalafix reports actual errors, so a genuinely broken pull - # request is not rebuilt four times. Backoff matches + # Split a Maven Central hiccup from a real lint violation. When the + # scalafix plugin fails to download (Central has answered 403, 429 and + # 404), Maven stops with "No plugin found for prefix 'scalafix'" and the + # red lint looks exactly like a violation. A scala-compiler jar that did + # not download shows up as "ClassNotFoundException: scala.tools.nsc.Main". + # The scalafix-syntactic job above lints offline after a retried + # download. Maven has no equally safe offline mode here, because the + # build resolves plugin and compiler artifacts as it goes, so this step + # retries the whole build instead, and only when Maven's error names a + # download. A lint violation or a compile error still fails on the first + # attempt. Maven caches a 404 in the local repository and will not ask + # again on its own, so the retries pass -U. Backoff matches # .github/actions/maven-bootstrap: 10s, 20s, 40s plus 0-4s of jitter. - name: Run scalafix check + # Bash rather than the container default `sh`: the retry below needs + # pipefail, $RANDOM, and shell arithmetic. + shell: bash run: | set -o pipefail + log=$(mktemp) + update= for attempt in 1 2 3 4; do - if ./mvnw -B package -DskipTests scalafix:scalafix -Dscalafix.mode=CHECK \ - -Psemanticdb ${{ matrix.profile.maven_opts }} 2>&1 | tee scalafix-check.log; then + if ./mvnw -B $update package -DskipTests scalafix:scalafix -Dscalafix.mode=CHECK \ + -Psemanticdb ${{ matrix.profile.maven_opts }} 2>&1 | tee "$log"; then exit 0 fi - if grep -q 'Scalafix invoked with errors' scalafix-check.log; then - echo "::error::scalafix reported lint violations; see the log above." + if ! grep -qE '^\[ERROR\] .*(No plugin found for prefix|could not be resolved|Could not transfer|ClassNotFoundException: scala\.tools\.nsc\.Main)' "$log"; then + echo "::error::Maven failed for a reason other than a download, such as a lint violation or a compile error; not retrying." exit 1 fi if [ "$attempt" -eq 4 ]; then - echo "::error::scalafix could not run after $attempt attempts; no violation was reported." + echo "::error::Maven could not download a plugin or dependency after $attempt attempts; this is not a lint violation." exit 1 fi delay=$((10 * (1 << (attempt - 1)) + RANDOM % 5)) - echo "::warning::scalafix attempt $attempt failed without reporting a violation; retrying in ${delay}s." + echo "::warning::Maven failed to download a plugin or dependency (attempt $attempt of 4); retrying in ${delay}s with -U." sleep "$delay" + update=-U done - name: Setup Node.js