diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index 616b199813..69cb98adf0 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -1628,4 +1628,161 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) expect(output == "") } } + + // Credentials tests + test("Repository credentials passed to coursier") { + val testOrg = "test-org" + val testName = "the-messages" + val testVersion = "0.1.2" + val user = "username" + val password = "1234" + val realm = "Realm" + val inputs = TestInputs( + os.rel / "messages" / "Messages.scala" -> + """package messages + | + |object Messages { + | def hello(name: String): String = + | s"Hello $name" + |} + |""".stripMargin, + os.rel / "hello" / "Hello.scala" -> + s"""//> using dep "$testOrg::$testName:$testVersion" + |import messages.Messages + |object Hello { + | def main(args: Array[String]): Unit = + | println(Messages.hello(args.headOption.getOrElse("Unknown"))) + |} + |""".stripMargin + ) + + inputs.fromRoot { root => + val configFile = { + val dir = root / "conf" + os.makeDir.all(dir, if (Properties.isWin) null else "rwx------") + dir / "config.json" + } + val extraEnv = Map( + "SCALA_CLI_CONFIG" -> configFile.toString + ) + val repoPath = root / "the-repo" + os.proc( + TestUtil.cli, + "--power", + "publish", + "--publish-repo", + repoPath.toNIO.toUri.toASCIIString, + "messages", + "--organization", + testOrg, + "--name", + testName, + "--project-version", + testVersion + ) + .call(cwd = root, stdin = os.Inherit, stdout = os.Inherit, env = extraEnv) + + TestUtil.serveFilesInHttpServer(repoPath, user, password, realm) { (host, port) => + // This codeblock represents test("No repository credentials passed to coursier") + { + val resWithNoCreds = os.proc( + TestUtil.cli, + "run", + "--repository", + s"http://$host:$port", + "hello", + "--", + "TestUser" + ).call( + cwd = root, + env = Map( + "USER" -> user, + "PASSWORD" -> password + ), + check = false, + mergeErrIntoOut = true + ) + + expect(resWithNoCreds.exitCode == 1) + } + + // This codeblock represents test("Repository credentials passed to coursier - environment variables") + { + val resWithEnvVar = os.proc( + TestUtil.cli, + "run", + "--repository", + s"http://$host:$port", + "hello", + "--", + "TestUser" + ).call( + cwd = root, + env = Map( + "USER" -> user, + "PASSWORD" -> password, + "COURSIER_CREDENTIALS" -> s"$host $user:$password" + ), + mergeErrIntoOut = true + ) + + expect(resWithEnvVar.exitCode == 0) + } + + // This codeblock represents test("Repository credentials passed to coursier - config entry") + { + os.write( + configFile, + s"""{ + |"repositories.credentials": [ + |{"host":"$host","user":"value:$user","password":"value:$password","matchHost":true} + |] + |}""".stripMargin + ) + val resWithConfig = os.proc( + TestUtil.cli, + "run", + "--repository", + s"http://$host:$port", + "hello", + "--", + "TestUser" + ).call( + cwd = root, + env = Map( + "USER" -> user, + "PASSWORD" -> password + ) ++ extraEnv, + mergeErrIntoOut = true + ) + + expect(resWithConfig.exitCode == 0) + } + + // This codeblock represents test("Repository credentials passed to coursier - java properties") + { + os.write(root / ".scala-jvmopts", s"-Dcoursier.credentials=$host $user:$password\n") + + val resWithProps = os.proc( + TestUtil.cli, + "run", + "--repository", + s"http://$host:$port", + "hello", + "--", + "TestUser" + ).call( + cwd = root, + env = Map( + "USER" -> user, + "PASSWORD" -> password + ), + mergeErrIntoOut = true + ) + + expect(resWithProps.exitCode == 0) + } + } + } + } } diff --git a/website/docs/guides/intro.md b/website/docs/guides/intro.md index a9b6485218..c645f2734b 100644 --- a/website/docs/guides/intro.md +++ b/website/docs/guides/intro.md @@ -47,5 +47,5 @@ Less introductory guides on specific topics. - [SBT and Mill export](./sbt-mill.md) - learn how to convert your Scala CLI project into an SBT or Mill project (when you need a more powerful build tool). -- [proxies](./proxies.md) - learn how to configure Scala CLI to work with proxies. +- [proxies](./repositories.md) - learn how to configure Scala CLI to work with proxies. - [Markdown](./markdown.md) - learn how to work with `.md` sources. \ No newline at end of file diff --git a/website/docs/guides/java-properties.md b/website/docs/guides/java-properties.md new file mode 100644 index 0000000000..a2bdf01c2c --- /dev/null +++ b/website/docs/guides/java-properties.md @@ -0,0 +1,29 @@ +--- +title: Java properties +sidebar_position: 8 +--- + +Although the Scala CLI runner can be used as a native image and thus will not always be run on the JVM it still supports java properties. +There are a couple ways to specify them: +- as command line arguments, before the sub-command name and sources, when invoking `scala-cli`, e.g. +```bash ignore + scala-cli '-Dcoursier.credentials=maven.pkg.github.com Private_Token:gh_token1234' run . +``` +- save them in `.scala-jvmopts` file in the project's root, e.g. +```text +-Dcoursier.credentials=maven.pkg.github.com Private_Token:gh_token1234 +-Dhttp.proxy=4.4.4.4 +-Dhttp.user=User2 +``` +- set them globally using `scala-cli config`, e.g. +```bash ignore + scala-cli config -i java.properties "http.proxy=4.4.4.4" "http.user=User2" "coursier.credentials=..." +``` +- set them globally in `JAVA_OPTS` or `JDK_JAVA_OPTIONS` environment variable, e.g. +```bash ignore + export JAVA_OPTS="-Dhttp.proxy=4.4.4.4 -Dhttp.user=User2" +``` + +:::note +The `-D` prefix can only be dropped when writing the values to config. +::: \ No newline at end of file diff --git a/website/docs/guides/proxies.md b/website/docs/guides/proxies.md deleted file mode 100644 index f091a3872e..0000000000 --- a/website/docs/guides/proxies.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Proxies ⚡️ -sidebar_position: 51 ---- - -## HTTP proxies - -:::caution -Even though the `config` command is not restricted, some available configuration keys may be, and thus may -require setting the `--power` option to be used. -That includes configuration keys tied to setting up proxies, like `httpProxy.address` and others. -You can pass the `--power` option explicitly or set it globally by running: -```bash ignore -scala-cli config power true -``` -::: - -### Configuration - -If you can only download artifacts through a proxy, you need to configure it beforehand, like -```bash ignore -scala-cli --power config httpProxy.address http://proxy.company.com -``` - -Replace `proxy.company.com` by the address of your proxy. - -Change `http://` to `https://` if your proxy is accessible via HTTPS. - -### Authentication - -If your proxy requires authentication, set your user and password with -```bash ignore -scala-cli --power config httpProxy.user value:_encoded_user_ -scala-cli --power config httpProxy.password value:_encoded_password_ -``` - -Replace `_encoded_user_` and `_encoded_password_` by your actual user and password, following -the [password option format](/docs/reference/password-options.md). They should typically look like -`env:ENV_VAR_NAME`, `file:/path/to/file`, or `command:command to run`. - -## Default repositories - -If you don't rely on proxies, but rather download artifacts through different Maven repositories, -set those repositories like: -```bash ignore -scala-cli --power config repositories.default https://first-repo.company.com https://second-repo.company.com -``` - -## Mirrors - -If you're fine directly downloading artifacts from the internet, but would rather have some -repositories requests go through a repository of yours, configure mirror repositories, like -```bash ignore -scala-cli --power config repositories.mirrors https://repo1.maven.org/maven2=https://repository.company.com/maven -``` - -To have all requests to a Maven repository go through a repository of yours, do -```bash ignore -scala-cli --power config repositories.mirrors maven:*=https://repository.company.com/maven -``` diff --git a/website/docs/guides/repositories.md b/website/docs/guides/repositories.md new file mode 100644 index 0000000000..aeb6214662 --- /dev/null +++ b/website/docs/guides/repositories.md @@ -0,0 +1,105 @@ +--- +title: Repositories and HTTP Proxies ⚡️ +sidebar_position: 51 +--- + +Scala CLI downloads the dependencies declared in your projects using [Coursier](https://get-coursier.io/). +The default repositories being searched are the Maven Central and local Ivy repository on your machine. +If additional repositories are required it is possible to declare them: +- on the command line with `--repository` or `--repo` or just `-r` +- with the `//> using repositories` directive + +The values can be names of predefined repositories accepted by Coursier, some of which are: +- `sonatype:_value_` and `sonatype-s01:_value_` for Sonatype servers e.g. `sonatype:snapshots` + snapshots from both servers are searched when using `snapshots` +- `jitpack` +- `m2Local` + +## Custom repositories + +Supplying the address of custom repositories is also accepted when using `--repository` or `//> using repositories`. +To do so, provide the URL to the repository's root, e.g. `https://maven.pkg.github.com/USER/REPO` for GitHub Package Registry. +By default, custom repositories are treated as Maven repositories, to specify an Ivy repository, prefix the address with `ivy:` and supply the ivy pattern at the end e.g. `ivy:http://localhost:8081/repository/ivy-releases/[defaultPattern]`. + +:::tip +`[defaultPattern]` gets expanded by Coursier to: +```text + [organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] +``` +::: + +## Repository Authentication + +:::caution +Even though the `config` command is not restricted, some available configuration keys may be, and thus may +require setting the `--power` option to be used. +That includes the configuration key tied to repositories settings, like `repositories.credentials` and others. +You can pass the `--power` option explicitly or set it globally by running: +```bash ignore +scala-cli config power true +``` +::: + +Repository authentication is also supported and there are a couple ways of using it: +- specifying credentials for each host in `COURSIER_CREDENTIALS` environment variable or in the `coursier.credentials` java property ([read more here](/docs/guides/java-properties.md)), + the supported format in this case is `host-address username:password`, e.g. `my_domain.com MyUserName:myPasswOrd` +- adding config entries for each host, this can be done using `scala-cli --power config repositories.credentials host _username_ _password_`, + username and password values should follow the [password option format](/docs/reference/password-options.md), e.g. +```bash ignore + scala-cli --power config repositories.credentials maven.pkg.github.com value:PrivateToken env:GH_TOKEN +``` + +## Default repositories + +You can override the default Coursier repositories globally by invoking: +```bash ignore +scala-cli --power config repositories.default https://first-repo.company.com https://second-repo.company.com +``` + +## HTTP proxies + +:::caution +Even though the `config` command is not restricted, some available configuration keys may be, and thus may +require setting the `--power` option to be used. +That includes configuration keys tied to setting up proxies, like `httpProxy.address` and others. +You can pass the `--power` option explicitly or set it globally by running: +```bash ignore +scala-cli config power true +``` +::: + +### Configuration + +If you can only download artifacts through a proxy, you need to configure it beforehand, like +```bash ignore +scala-cli --power config httpProxy.address http://proxy.company.com +``` + +Replace `proxy.company.com` by the address of your proxy. + +Change `http://` to `https://` if your proxy is accessible via HTTPS. + +### Authentication + +If your proxy requires authentication, set your user and password with +```bash ignore +scala-cli --power config httpProxy.user value:_encoded_user_ +scala-cli --power config httpProxy.password value:_encoded_password_ +``` + +Replace `_encoded_user_` and `_encoded_password_` by your actual user and password, following +the [password option format](/docs/reference/password-options.md). They should typically look like +`env:ENV_VAR_NAME`, `file:/path/to/file`, or `command:command to run`. + +## Mirrors + +If you're fine directly downloading artifacts from the internet, but would rather have some +repositories requests go through a repository of yours, configure mirror repositories, like +```bash ignore +scala-cli --power config repositories.mirrors https://repo1.maven.org/maven2=https://repository.company.com/maven +``` + +To have all requests to a Maven repository go through a repository of yours, do +```bash ignore +scala-cli --power config repositories.mirrors maven:*=https://repository.company.com/maven +``` \ No newline at end of file