diff --git a/libraries/scripting/dependencies-maven/build.gradle.kts b/libraries/scripting/dependencies-maven/build.gradle.kts index 54c7be785fb..0f5468f8f22 100644 --- a/libraries/scripting/dependencies-maven/build.gradle.kts +++ b/libraries/scripting/dependencies-maven/build.gradle.kts @@ -13,12 +13,12 @@ dependencies { compile("org.eclipse.aether:aether-util:1.1.0") compile("org.eclipse.aether:aether-connector-basic:1.1.0") compile("org.eclipse.aether:aether-transport-wagon:1.1.0") - compile("org.eclipse.aether:aether-transport-http:1.1.0") compile("org.eclipse.aether:aether-transport-file:1.1.0") compile("org.apache.maven:maven-core:3.8.1") compile("org.apache.maven:maven-settings-builder:3.8.1") compile("org.apache.maven:maven-aether-provider:3.3.9") compile("org.apache.maven.wagon:wagon-provider-api:3.4.3") + compile("org.apache.maven.wagon:wagon-http:3.4.3") testCompile(projectTests(":kotlin-scripting-dependencies")) testCompile(commonDep("junit")) testRuntimeOnly("org.slf4j:slf4j-nop:1.7.30") diff --git a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt index 416fcae06ce..dfc374d753e 100644 --- a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt +++ b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt @@ -17,13 +17,14 @@ import kotlin.script.experimental.api.SourceCode import kotlin.script.experimental.api.asSuccess import kotlin.script.experimental.dependencies.ExternalDependenciesResolver import kotlin.script.experimental.dependencies.RepositoryCoordinates -import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult -import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull +import kotlin.script.experimental.dependencies.impl.* import kotlin.script.experimental.dependencies.maven.impl.AetherResolveSession import kotlin.script.experimental.dependencies.maven.impl.mavenCentral -import kotlin.script.experimental.dependencies.impl.dependencyScopes - +@Deprecated( + "This class is not functional and left only for compatibility reasons. Use kotlin.script.experimental.dependencies.ExternalDependenciesResolver.Options for passing authorization options", + replaceWith = ReplaceWith("RepositoryCoordinates(url)", "kotlin.script.experimental.dependencies.RepositoryCoordinates") +) class MavenRepositoryCoordinates( url: String, val username: String?, @@ -89,21 +90,31 @@ class MavenDependenciesResolver : ExternalDependenciesResolver { val url = repositoryCoordinates.toRepositoryUrlOrNull() ?: return false.asSuccess() val repoId = repositoryCoordinates.string.replace(FORBIDDEN_CHARS, "_") - val repo = RemoteRepository.Builder(repoId, "default", url.toString()) - if (repositoryCoordinates is MavenRepositoryCoordinates) { - val username = repositoryCoordinates.username?.let(::tryResolveEnvironmentVariable) - val password = repositoryCoordinates.password?.let(::tryResolveEnvironmentVariable) - if (username != null) { - val auth = AuthenticationBuilder().apply { - addUsername(username) - if (password != null) { - addPassword(password) - } - } - repo.setAuthentication(auth.build()) - } - } - repos.add(repo.build()) + val repo = RemoteRepository.Builder(repoId, "default", url.toString()).apply { + /** + * Here we set all the authentication information we have, unconditionally. + * Actual information that will be used (as well as lower-level checks, + * such as nullability or emptiness) is determined by implementation. + * + * @see org.eclipse.aether.transport.wagon.WagonTransporter.getProxy + * @see org.apache.maven.wagon.shared.http.AbstractHttpClientWagon.openConnectionInternal + */ + setAuthentication( + AuthenticationBuilder().apply { + val mavenRepo = repositoryCoordinates as? MavenRepositoryCoordinates + val username = options.username ?: mavenRepo?.username + val password = options.password ?: mavenRepo?.password + addUsername(username?.let(::tryResolveEnvironmentVariable)) + addPassword(password?.let(::tryResolveEnvironmentVariable)) + addPrivateKey( + options.privateKeyFile?.let(::tryResolveEnvironmentVariable), + options.privateKeyPassphrase?.let(::tryResolveEnvironmentVariable) + ) + }.build() + ) + }.build() + + repos.add(repo) return true.asSuccess() } diff --git a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt index 5c6176a24e5..9a3e6398252 100644 --- a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt +++ b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt @@ -11,6 +11,7 @@ import org.apache.maven.settings.Settings import org.apache.maven.settings.SettingsUtils import org.apache.maven.settings.TrackableBase import org.apache.maven.settings.building.* +import org.codehaus.plexus.DefaultPlexusContainer import org.eclipse.aether.RepositorySystem import org.eclipse.aether.RepositorySystemSession import org.eclipse.aether.artifact.Artifact @@ -19,6 +20,8 @@ import org.eclipse.aether.collection.CollectRequest import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory import org.eclipse.aether.graph.Dependency import org.eclipse.aether.graph.DependencyFilter +import org.eclipse.aether.internal.transport.wagon.PlexusWagonConfigurator +import org.eclipse.aether.internal.transport.wagon.PlexusWagonProvider import org.eclipse.aether.repository.LocalRepository import org.eclipse.aether.repository.Proxy import org.eclipse.aether.repository.RemoteRepository @@ -29,7 +32,9 @@ import org.eclipse.aether.resolution.DependencyResult import org.eclipse.aether.spi.connector.RepositoryConnectorFactory import org.eclipse.aether.spi.connector.transport.TransporterFactory import org.eclipse.aether.transport.file.FileTransporterFactory -import org.eclipse.aether.transport.http.HttpTransporterFactory +import org.eclipse.aether.transport.wagon.WagonConfigurator +import org.eclipse.aether.transport.wagon.WagonProvider +import org.eclipse.aether.transport.wagon.WagonTransporterFactory import org.eclipse.aether.util.filter.DependencyFilterUtils import org.eclipse.aether.util.repository.AuthenticationBuilder import org.eclipse.aether.util.repository.DefaultMirrorSelector @@ -48,7 +53,7 @@ class AetherResolveSession( val remotes by lazy { val proxySelector = settings.getActiveProxy()?.let { proxy -> val selector = DefaultProxySelector() - val auth = with (AuthenticationBuilder()) { + val auth = with(AuthenticationBuilder()) { addUsername(proxy.username) addPassword(proxy.password) build() @@ -95,8 +100,19 @@ class AetherResolveSession( ) locator.addService( TransporterFactory::class.java, - HttpTransporterFactory::class.java + WagonTransporterFactory::class.java ) + + val container = DefaultPlexusContainer() + locator.setServices( + WagonProvider::class.java, + PlexusWagonProvider(container) + ) + locator.setServices( + WagonConfigurator::class.java, + PlexusWagonConfigurator(container) + ) + locator.getService(RepositorySystem::class.java) } diff --git a/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt b/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt index 9b66fac03a0..e3d7b9e8d58 100644 --- a/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt +++ b/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt @@ -22,7 +22,7 @@ import kotlin.script.experimental.dependencies.impl.set @ExperimentalContracts class MavenResolverTest : ResolversTestBase() { - fun resolveAndCheck( + private fun resolveAndCheck( coordinates: String, options: ExternalDependenciesResolver.Options = ExternalDependenciesResolver.Options.Empty, checkBody: (Iterable) -> Boolean = { true } @@ -39,13 +39,18 @@ class MavenResolverTest : ResolversTestBase() { files.forEach { it.delete() } } + private fun buildOptions(vararg options: Pair): ExternalDependenciesResolver.Options { + return makeExternalDependenciesResolverOptions(mutableMapOf().apply { + for (option in options) this[option.first] = option.second + }) + } + fun testResolveSimple() { resolveAndCheck("org.jetbrains.kotlin:kotlin-annotations-jvm:1.3.50") { files -> files.any { it.name.startsWith("kotlin-annotations-jvm") } } } - @ExperimentalStdlibApi fun testResolveWithRuntime() { val compileOnly = "compile" val compileRuntime = "compile,runtime" @@ -54,9 +59,7 @@ class MavenResolverTest : ResolversTestBase() { listOf(compileOnly, compileRuntime).forEach { scopes -> resolveAndCheck( "org.uberfire:uberfire-io:7.39.0.Final", - makeExternalDependenciesResolverOptions(buildMap { - this[DependenciesResolverOptionsName.SCOPE] = scopes - }) + buildOptions(DependenciesResolverOptionsName.SCOPE to scopes) ) { files -> resolvedFilesCount[scopes] = files.count() files.any { it.name.startsWith("uberfire-commons") } @@ -81,6 +84,22 @@ class MavenResolverTest : ResolversTestBase() { } } + // Ignored - tests with custom repos often break the CI due to the caching issues + // TODO: find a way to enable it back + @Ignore + fun ignore_testAuth() { + val resolver = MavenDependenciesResolver() + val options = buildOptions( + DependenciesResolverOptionsName.USERNAME to "", + DependenciesResolverOptionsName.PASSWORD to "", + ) + resolver.addRepository("https://packages.jetbrains.team/maven/p/crl/maven/", options) + val files = runBlocking { + resolver.resolve("com.jetbrains:space-sdk:1.0-dev") + }.valueOrThrow() + assertTrue(files.any { it.name.startsWith("space-sdk") }) + } + // Ignored - tests with custom repos often break the CI due to the caching issues // TODO: find a way to enable it back @Ignore diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt index 03b3abfbd52..ed1a14bb95d 100644 --- a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt @@ -23,7 +23,11 @@ operator fun MutableMap.set(key: DependenciesResolverOptionsName */ enum class DependenciesResolverOptionsName(optionName: String? = null) { TRANSITIVE, - SCOPE; + SCOPE, + USERNAME, + PASSWORD, + KEY_FILE, + KEY_PASSPHRASE; val key = optionName ?: name.lowercase() } @@ -33,3 +37,27 @@ val ExternalDependenciesResolver.Options.transitive val ExternalDependenciesResolver.Options.dependencyScopes get() = value(DependenciesResolverOptionsName.SCOPE)?.split(",") + +/** + * Username to access repository (should be passed with [password]) + */ +val ExternalDependenciesResolver.Options.username + get() = value(DependenciesResolverOptionsName.USERNAME) + +/** + * Password to access repository (should be passed with [username]) + */ +val ExternalDependenciesResolver.Options.password + get() = value(DependenciesResolverOptionsName.PASSWORD) + +/** + * Absolute path to the private key file to access repository + */ +val ExternalDependenciesResolver.Options.privateKeyFile + get() = value(DependenciesResolverOptionsName.KEY_FILE) + +/** + * Passphrase to access file passed in [privateKeyFile] + */ +val ExternalDependenciesResolver.Options.privateKeyPassphrase + get() = value(DependenciesResolverOptionsName.KEY_PASSPHRASE)