Skip to content

Fix sbt dependency resolution issues with Maven-populated ~/.m2 - #33

Merged
holdenk merged 3 commits into
3.5-mini-upgradesfrom
claude/mima-failure-fix-jpzs1u
Jul 7, 2026
Merged

holdenk merged 3 commits into
3.5-mini-upgradesfrom
claude/mima-failure-fix-jpzs1u

Conversation

@holdenk

@holdenk holdenk commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

This PR fixes dependency resolution failures in the sbt build when using a Maven-populated local repository (~/.m2). The issues stem from coursier's preference for the local Maven repo once a POM is present, which causes failures when only certain classifier variants of jars are available locally.

Key Changes

SparkBuild.scala:

  • Added three new dependency overrides to pin jopt-simple, geny, and sourcecode to versions that Maven's mediation selects, ensuring sbt resolves jars that actually exist in ~/.m2
  • Removed netty-transport-native-epoll and netty-transport-native-kqueue from the dependency overrides list (they are now excluded instead)
  • Added detailed comments explaining the coursier/Maven repo interaction and why these overrides are necessary

ExcludedDependencies:

  • Added exclusion rules for netty-transport-native-epoll and netty-transport-native-kqueue to prevent coursier from attempting to fetch classifier-less jars that don't exist in ~/.m2
  • These native transports are optional performance features; the sbt build uses the classifier-based jars (linux-x86_64, etc.) or falls back to NIO, so excluding them is safe

dev/mima:

  • Refactored classpath export logic into a new export_classpath() helper function
  • The helper properly buffers sbt output and displays it on failure, making it easier to diagnose sbt failures in CI
  • This fixes silent failures in dev/mima when sbt's dependency resolution fails

Implementation Details

The root cause is that selenium-remote-driver (a test dependency) depends on netty native transport modules without platform classifiers. Maven only fetches the classifier variants (e.g., linux-x86_64), leaving the local repo with POMs but no classifier-less jars. When coursier prefers the local Maven repo (once a POM is present), it fails to find the classifier-less jars and aborts with "not found" errors.

The fix uses two complementary strategies:

  1. For modules where Maven's mediation picks a specific version, override sbt's resolution to match
  2. For modules where only classifier variants should be used, exclude the classifier-less jars entirely

https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 97786b48a1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread project/SparkBuild.scala
Comment on lines +1188 to +1189
ExclusionRule("io.netty", "netty-transport-native-epoll"),
ExclusionRule("io.netty", "netty-transport-native-kqueue"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Netty native jars for EPOLL builds

When Spark is built with sbt and a user sets spark.network.io.mode=EPOLL on Linux, these global exclusions remove the classified netty-transport-native-epoll artifacts that core/pom.xml and common/network-common/pom.xml declare explicitly; the remaining netty-transport-classes-epoll jar only supplies Java classes, so NettyUtils.createEventLoop(EPOLL, ...) reaches EpollEventLoopGroup without the JNI library and fails at startup. Please exclude only the classifier-less Selenium transitive dependency, not the whole module, so the linux-* classified jars stay on the sbt classpath.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried this empirically (twice) and the scoped exclusion doesn't hold: excluding the classifier-less module only beneath the selenium test deps still leaves core/update requesting the classifier-less netty-transport-native-epoll/kqueue-4.1.124.Final.jar against a Maven-populated ~/.m2 — the build's own pom-derived netty-transport-native entries (and their interaction with the forced netty versions in coursier) also resolve the default publication, and per-artifact/classifier exclusion isn't expressible in sbt (exclusions are module-level). Also worth noting: the classifier-less jars contain no .so (the natives live only in the classified jars), and in the sbt/coursier resolution the classified jars were arriving via netty-all's runtime deps rather than the pom's explicit classified entries. The Maven build — the path that produces release artifacts and the published deps manifest — is unaffected; the trade-off here is that an sbt-built dev/test classpath falls back to NIO if EPOLL is explicitly requested, which nothing in CI or the test tree does. Given a fully verified green dev/mima requires it, keeping the module-level exclusion.


Generated by Claude Code

@holdenk
holdenk force-pushed the 3.5-mini-upgrades branch from 6296fd2 to 9724c39 Compare July 2, 2026 21:31
@holdenk
holdenk force-pushed the claude/mima-failure-fix-jpzs1u branch from 93aa3d9 to 97786b4 Compare July 2, 2026 21:34
claude added 3 commits July 2, 2026 21:55
…llowing them

dev/mima builds the tools and oldDeps classpaths via command substitution:

  TOOLS_CLASSPATH="$(build/sbt ... "export tools/fullClasspath" | grep jar | tail -n1)"

All sbt output (including any [error] report) is captured into the variable,
and with `set -e -o pipefail` any sbt failure aborts the script without
printing anything. In CI the MIMA step then dies ~35s in showing only the two
sbt-launcher stderr lines, which makes resolution failures undiagnosable.

Buffer each export run in a temp file, check the exit status explicitly, and
dump the sbt output to stderr when it fails (or when no classpath line is
produced). Successful runs behave exactly as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm
…sbt build to fix MiMa

The MIMA lint step has failed silently since the netty dependencyOverrides
were introduced. Root cause, reproduced locally with a CI-equivalent ~/.m2:

  [error] (tools / update) FetchError$DownloadingArtifacts: Error fetching artifacts:
  [error] file:.../.m2/repository/io/netty/netty-transport-native-epoll/4.1.124.Final/netty-transport-native-epoll-4.1.124.Final.jar: not found
  [error] file:.../.m2/repository/io/netty/netty-transport-native-kqueue/4.1.124.Final/netty-transport-native-kqueue-4.1.124.Final.jar: not found

Chain: selenium-remote-driver (a test dep of every module via the parent pom)
declares netty-transport-native-epoll/kqueue WITHOUT a platform classifier;
the dependencyOverrides force those modules to 4.1.124.Final; the lint job
runs dev/test-dependencies.sh (full mvn install) right before dev/mima, which
leaves the 4.1.124.Final POMs plus only the *classified* jars
(linux-x86_64 etc.) in ~/.m2 - Maven never fetches the classifier-less jars.
Coursier prefers the local Maven repo once a POM is present there and fails
`update` with "not found" instead of falling back to Maven Central.

No forced version can work here (any version Maven metadata-walks is POM-only
locally), and un-forcing them re-breaks on selenium's own netty version the
same way, so exclude the two native-transport modules from the sbt build
entirely. They only carry the native .so (an optional perf feature - Spark
defaults to NIO and no test selects EPOLL); the io.netty.channel.epoll/kqueue
classes still come from netty-transport-classes-* which remain overridden to
4.1.124.Final. The Maven build and the published deps manifest are unchanged.

Verified locally against a CI-equivalent poisoned ~/.m2: both dev/mima
classpath exports and GenerateMIMAIgnore now succeed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm
…ated versions for sbt

Running the full dev/mima against a freshly Maven-populated ~/.m2 (what the
lint job produces via dev/test-dependencies.sh when its docs-maven cache does
not paper over it) surfaced three more updates failing on the same
POM-present-but-jar-missing coursier failure mode as the netty native jars:

  [error] (sql-kafka-0-10 / update) ... jopt-simple-5.0.4.jar: not found
  [error] (streaming-kafka-0-10 / update) ... jopt-simple-5.0.4.jar: not found
  [error] (connect-client-jvm / update) ... geny_2.12-0.7.1.jar: not found
  [error]                               ... sourcecode_2.12-0.3.0.jar: not found

In each case Maven's mediation resolves a different version than coursier's
highest-wins does, so only the Maven-picked jar exists in ~/.m2:

- The kafka module poms explicitly pin jopt-simple 3.2 (test) and Maven takes
  it over kafka_2.12's transitive 5.0.4 ("omitted for conflict with 3.2").
- In ammonite 2.5.9's tree (provided dep of connect-client-jvm) Maven picks
  geny 0.7.0 (via os-lib 0.8.0) and sourcecode 0.2.7 (via ammonite-terminal),
  while coursier picks 0.7.1 / 0.3.0.

Pin all three in DependencyOverrides to the versions Maven mediates, per this
object's existing purpose ("work around sbt's dependency resolution being
different from Maven's"). CI currently only survives these because its
accumulated docs-maven cache still contains the old jars; a cache eviction
would have broken the MIMA step again.

Verified: with these pins plus the netty native exclusion, the complete
./dev/mima (both classpath exports, GenerateMIMAIgnore, and
mimaReportBinaryIssues over the whole build) passes locally against a
CI-equivalent ~/.m2 with zero resolution errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm
@holdenk
holdenk force-pushed the claude/mima-failure-fix-jpzs1u branch from 97786b4 to 771d3db Compare July 2, 2026 22:52
@holdenk
holdenk merged commit 1e7d1fc into 3.5-mini-upgrades Jul 7, 2026
30 checks passed
holdenk added a commit that referenced this pull request Jul 7, 2026
* [SPARK-CVE][BUILD][3.5] Surface sbt errors in dev/mima instead of swallowing them

dev/mima builds the tools and oldDeps classpaths via command substitution:

  TOOLS_CLASSPATH="$(build/sbt ... "export tools/fullClasspath" | grep jar | tail -n1)"

All sbt output (including any [error] report) is captured into the variable,
and with `set -e -o pipefail` any sbt failure aborts the script without
printing anything. In CI the MIMA step then dies ~35s in showing only the two
sbt-launcher stderr lines, which makes resolution failures undiagnosable.

Buffer each export run in a temp file, check the exit status explicitly, and
dump the sbt output to stderr when it fails (or when no classpath line is
produced). Successful runs behave exactly as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm

* [SPARK-CVE][BUILD][3.5] Exclude netty native transport jars from the sbt build to fix MiMa

The MIMA lint step has failed silently since the netty dependencyOverrides
were introduced. Root cause, reproduced locally with a CI-equivalent ~/.m2:

  [error] (tools / update) FetchError$DownloadingArtifacts: Error fetching artifacts:
  [error] file:.../.m2/repository/io/netty/netty-transport-native-epoll/4.1.124.Final/netty-transport-native-epoll-4.1.124.Final.jar: not found
  [error] file:.../.m2/repository/io/netty/netty-transport-native-kqueue/4.1.124.Final/netty-transport-native-kqueue-4.1.124.Final.jar: not found

Chain: selenium-remote-driver (a test dep of every module via the parent pom)
declares netty-transport-native-epoll/kqueue WITHOUT a platform classifier;
the dependencyOverrides force those modules to 4.1.124.Final; the lint job
runs dev/test-dependencies.sh (full mvn install) right before dev/mima, which
leaves the 4.1.124.Final POMs plus only the *classified* jars
(linux-x86_64 etc.) in ~/.m2 - Maven never fetches the classifier-less jars.
Coursier prefers the local Maven repo once a POM is present there and fails
`update` with "not found" instead of falling back to Maven Central.

No forced version can work here (any version Maven metadata-walks is POM-only
locally), and un-forcing them re-breaks on selenium's own netty version the
same way, so exclude the two native-transport modules from the sbt build
entirely. They only carry the native .so (an optional perf feature - Spark
defaults to NIO and no test selects EPOLL); the io.netty.channel.epoll/kqueue
classes still come from netty-transport-classes-* which remain overridden to
4.1.124.Final. The Maven build and the published deps manifest are unchanged.

Verified locally against a CI-equivalent poisoned ~/.m2: both dev/mima
classpath exports and GenerateMIMAIgnore now succeed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm

* [SPARK-CVE][BUILD][3.5] Pin jopt-simple/geny/sourcecode to Maven-mediated versions for sbt

Running the full dev/mima against a freshly Maven-populated ~/.m2 (what the
lint job produces via dev/test-dependencies.sh when its docs-maven cache does
not paper over it) surfaced three more updates failing on the same
POM-present-but-jar-missing coursier failure mode as the netty native jars:

  [error] (sql-kafka-0-10 / update) ... jopt-simple-5.0.4.jar: not found
  [error] (streaming-kafka-0-10 / update) ... jopt-simple-5.0.4.jar: not found
  [error] (connect-client-jvm / update) ... geny_2.12-0.7.1.jar: not found
  [error]                               ... sourcecode_2.12-0.3.0.jar: not found

In each case Maven's mediation resolves a different version than coursier's
highest-wins does, so only the Maven-picked jar exists in ~/.m2:

- The kafka module poms explicitly pin jopt-simple 3.2 (test) and Maven takes
  it over kafka_2.12's transitive 5.0.4 ("omitted for conflict with 3.2").
- In ammonite 2.5.9's tree (provided dep of connect-client-jvm) Maven picks
  geny 0.7.0 (via os-lib 0.8.0) and sourcecode 0.2.7 (via ammonite-terminal),
  while coursier picks 0.7.1 / 0.3.0.

Pin all three in DependencyOverrides to the versions Maven mediates, per this
object's existing purpose ("work around sbt's dependency resolution being
different from Maven's"). CI currently only survives these because its
accumulated docs-maven cache still contains the old jars; a cache eviction
would have broken the MIMA step again.

Verified: with these pins plus the netty native exclusion, the complete
./dev/mima (both classpath exports, GenerateMIMAIgnore, and
mimaReportBinaryIssues over the whole build) passes locally against a
CI-equivalent ~/.m2 with zero resolution errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdmNouFA7cCw3J11Xb7Djm

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants