Add [coursier].jvm_options to support custom CAs and proxies - #23629
Open
steverice wants to merge 1 commit into
Open
Add [coursier].jvm_options to support custom CAs and proxies#23629steverice wants to merge 1 commit into
[coursier].jvm_options to support custom CAs and proxies#23629steverice wants to merge 1 commit into
Conversation
Coursier ships as a GraalVM native image. It bundles its own trust store from build time and does not read the `HTTP_PROXY`/`HTTPS_PROXY` environment variables, so a custom CA or proxy has to be supplied as JVM system properties on Coursier's own command line. Pants offered no way to pass those, so any environment behind a TLS-inspecting proxy, or without direct internet access, could not use Pants' JVM support at all: Coursier fails to fetch Maven artifacts, nailgun, or JDKs. `[jvm].global_options` does not help, as it only reaches the JVM processes Pants runs, not Coursier itself. Add a `[coursier].jvm_options` option, injected at both places Coursier is invoked: * `COURSIER_FETCH_WRAPPER_SCRIPT`, between `"$coursier_exe"` and `fetch`. * `Coursier.args()`, between the exe path and the subcommand, which covers `java-home` and therefore JDK provisioning. Coursier only honors `-J` options that precede its subcommand, so they cannot simply be appended to the existing args. `Coursier.args()` deliberately skips the injection when a `wrapper` is passed: there the exe path is consumed by the wrapper script as a positional argument, so inserting options at that point would shift the wrapper's own arguments. The fetch wrapper receives them via the template instead. Values are prefixed with `-J` automatically when the caller omits it, so both `-Dfoo=bar` and `-J-Dfoo=bar` work. Validated end-to-end by dogfooding it on this repo. In a container with no DNS resolver and egress only via an HTTP proxy, `./pants lint check` fails outright: Coursier dies with `UnknownHostException` fetching `ch.epfl.scala:scalafix-cli_2.13.12:0.11.1`. With `[coursier].jvm_options` set to the proxy properties -- written without the `-J` prefix, to exercise the normalization -- the same command completes, with ruff, flake8, autoflake, preamble, visibility and mypy all passing. Closes pantsbuild#23391
steverice
marked this pull request as ready for review
August 18, 2026 04:06
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.
Coursier ships as a GraalVM native image. It bundles its own trust store from build time and does not read the
HTTP_PROXY/HTTPS_PROXYenvironment variables, so a custom CA or proxy has to be supplied as JVM system properties on Coursier's own command line. Pants offered no way to pass those, so any environment behind a TLS-inspecting proxy, or without direct internet access, could not use Pants' JVM support at all: Coursier fails to fetch Maven artifacts, nailgun, or JDKs.[jvm].global_optionsdoes not help, as it only reaches the JVM processes Pants runs, not Coursier itself.Add a
[coursier].jvm_optionsoption, injected at both places Coursier is invoked:COURSIER_FETCH_WRAPPER_SCRIPT, between"$coursier_exe"andfetch.Coursier.args(), between the exe path and the subcommand, which coversjava-homeand therefore JDK provisioning.Coursier only honors
-Joptions that precede its subcommand, so they cannot simply be appended to the existing args.Coursier.args()deliberately skips the injection when awrapperis passed: there the exe path is consumed by the wrapper script as a positional argument, so inserting options at that point would shift the wrapper's own arguments. The fetch wrapper receives them via the template instead.Values are prefixed with
-Jautomatically when the caller omits it, so both-Dfoo=barand-J-Dfoo=barwork.Validated end-to-end by dogfooding it on this repo. In a container with no DNS resolver and egress only via an HTTP proxy,
./pants lint checkfails outright: Coursier dies withUnknownHostExceptionfetchingch.epfl.scala:scalafix-cli_2.13.12:0.11.1. With[coursier].jvm_optionsset to the proxy properties -- written without the-Jprefix, to exercise the normalization -- the same command completes, with ruff, flake8, autoflake, preamble, visibility and mypy all passing.This PR was written primarily by Claude Code (Opus 5).
Closes #23391