Skip to content

Add [coursier].jvm_options to support custom CAs and proxies - #23629

Open
steverice wants to merge 1 commit into
pantsbuild:mainfrom
steverice:coursier-jvm-options
Open

Add [coursier].jvm_options to support custom CAs and proxies#23629
steverice wants to merge 1 commit into
pantsbuild:mainfrom
steverice:coursier-jvm-options

Conversation

@steverice

Copy link
Copy Markdown

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.

This PR was written primarily by Claude Code (Opus 5).

Closes #23391

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
steverice marked this pull request as ready for review August 18, 2026 04:06
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.

Coursier TLS errors with corp proxy for Restricted Internet access

1 participant