diff --git a/build.gradle.kts b/build.gradle.kts index d741f34e..4514c100 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { exclude("cglib", "cglib") exclude("org.kuali.maven.wagons", "maven-s3-wagon") } + // compile("com.jcabi:jcabi-aether:0.10.1:sources") //can be used for debugging, but somehow adds logging to dependency resolvement? compile("org.apache.maven:maven-core:3.0.3") compile("org.slf4j:slf4j-nop:1.7.25") @@ -50,4 +51,4 @@ val jar by tasks.getting { // Build shadowJar when val assemble by tasks.getting { dependsOn(shadowJar) -} \ No newline at end of file +} diff --git a/src/main/kotlin/kscript/app/DependencyUtil.kt b/src/main/kotlin/kscript/app/DependencyUtil.kt index 255fdff1..e4baf42c 100644 --- a/src/main/kotlin/kscript/app/DependencyUtil.kt +++ b/src/main/kotlin/kscript/app/DependencyUtil.kt @@ -3,6 +3,7 @@ package kscript.app import com.jcabi.aether.Aether import org.sonatype.aether.RepositoryException import org.sonatype.aether.artifact.Artifact +import org.sonatype.aether.repository.Authentication import org.sonatype.aether.repository.RemoteRepository import org.sonatype.aether.util.artifact.DefaultArtifact import org.sonatype.aether.util.artifact.JavaScopes.COMPILE @@ -59,7 +60,8 @@ fun resolveDependencies(depIds: List, customRepos: List = emp // Print the classpath return classPath } catch (e: RepositoryException) { - errorMsg("Failed to lookup dependencies. Check dependency locators or file a bug on https://github.com/holgerbrandl/kscript") + // Probably a wrapped Nullpointer from 'DefaultRepositorySystem.resolveDependencies()', this however is probably a connection problem. + errorMsg("Failed while connecting to the server. Check the connection (http/https, port, proxy, credentials, etc.) of your maven dependency locators. If you suspect this is a bug, you can create an issue on https://github.com/holgerbrandl/kscript") errorMsg("Exception: $e") quit(1) } @@ -67,7 +69,13 @@ fun resolveDependencies(depIds: List, customRepos: List = emp fun resolveDependenciesViaAether(depIds: List, customRepos: List, loggingEnabled: Boolean): List { val jcenter = RemoteRepository("jcenter", "default", "http://jcenter.bintray.com/") - val customRemoteRepos = customRepos.map { it -> RemoteRepository(it.id, "default", it.url) } + val customRemoteRepos = customRepos.map { mavenRepo -> + RemoteRepository(mavenRepo.id, "default", mavenRepo.url).apply { + if (!mavenRepo.user.isNullOrEmpty() && !mavenRepo.password.isNullOrEmpty()) { + authentication = Authentication(mavenRepo.user, mavenRepo.password) + } + } + } val remoteRepos = customRemoteRepos + jcenter val aether = Aether(remoteRepos, File(System.getProperty("user.home") + "/.m2/repository")) diff --git a/src/main/kotlin/kscript/app/Script.kt b/src/main/kotlin/kscript/app/Script.kt index b37405d1..36acc2e3 100644 --- a/src/main/kotlin/kscript/app/Script.kt +++ b/src/main/kotlin/kscript/app/Script.kt @@ -129,7 +129,7 @@ fun Script.collectDependencies(): List { // if annotations are used add dependency on kscript-annotations if (lines.any { isKscriptAnnotation(it) }) { - dependencies += "com.github.holgerbrandl:kscript-annotations:1.2" + dependencies += "com.github.holgerbrandl:kscript-annotations:1.4" } return dependencies.distinct() @@ -182,7 +182,7 @@ private fun isDependDeclare(line: String) = // -data class MavenRepo(val id: String, val url: String) +data class MavenRepo(val id: String, val url: String, val user: String = "", val password: String = "") /** * Collect custom artifact repos declared with @file:MavenRepository @@ -192,12 +192,16 @@ fun Script.collectRepos(): List { // only supported annotation format for now // @file:MavenRepository("imagej", "http://maven.imagej.net/content/repositories/releases/") + // @file:MavenRepository("imagej", "http://maven.imagej.net/content/repositories/releases/", user="user", password="pass") return lines .filter { it.contains(dependsOnMavenPrefix) } .map { it.replaceFirst(dependsOnMavenPrefix, "").substringBeforeLast(")") } - .map { it.split(",").map { it.trim(' ', '"', '(') }.let { MavenRepo(it[0], it[1]) } } - - // todo add credential support https://stackoverflow.com/questions/36282168/how-to-add-custom-maven-repository-to-gradle + .map { + it.split(",").map { it.trim(' ', '"', '(') }.let { annotationParams -> + val namedArgs = annotationParams.filter { it.contains("=") }.map { item: String -> item.let { item.substringBefore("=") to item.substringAfter("=\"") } }.toMap() + MavenRepo(annotationParams[0], annotationParams[1], namedArgs.getOrDefault("user", ""), namedArgs.getOrDefault("password", "")) + } + } } diff --git a/src/test/kotlin/Tests.kt b/src/test/kotlin/Tests.kt index 6c12ae1c..88bf2962 100644 --- a/src/test/kotlin/Tests.kt +++ b/src/test/kotlin/Tests.kt @@ -35,7 +35,7 @@ class Tests { val expected = listOf( "something:dev-1.1.0-alpha3(T2):1.2.14", "de.mpicbg.scicomp:kutils:0.7", - "com.github.holgerbrandl:kscript-annotations:1.2" + "com.github.holgerbrandl:kscript-annotations:1.4" ) Script(lines).collectDependencies() shouldBe expected @@ -58,7 +58,7 @@ class Tests { "de.mpicbg.scicomp.joblist:joblist-kotlin:1.1", "de.mpicbg.scicomp:kutils:0.7", "log4j:log4j:1.2.14", - "com.github.holgerbrandl:kscript-annotations:1.2" + "com.github.holgerbrandl:kscript-annotations:1.4" ) Script(lines).collectDependencies() shouldBe expected @@ -83,12 +83,39 @@ class Tests { collectDependencies() shouldBe listOf( "net.clearvolume:cleargl:2.0.1", "log4j:log4j:1.2.14", - "com.github.holgerbrandl:kscript-annotations:1.2" + "com.github.holgerbrandl:kscript-annotations:1.4" ) } } + @Test + fun customRepoWithCreds() { + val lines = listOf( + """@file:MavenRepository("imagej-releases", "http://maven.imagej.net/content/repositories/releases", user="user", password="pass") """, + """@file:MavenRepository("imagej-snapshots", "http://maven.imagej.net/content/repositories/snapshots", password="pass", user="user") """, + """@file:DependsOnMaven("net.clearvolume:cleargl:2.0.1")""", + """@file:DependsOn("log4j:log4j:1.2.14")""", + """println("foo")""" + ) + + with(Script(lines)) { + + collectRepos() shouldBe listOf( + MavenRepo("imagej-releases", "http://maven.imagej.net/content/repositories/releases", "user", "pass"), + MavenRepo("imagej-snapshots", "http://maven.imagej.net/content/repositories/snapshots", "user", "pass") //Provided with name in non-typical order + ) + + collectDependencies() shouldBe listOf( + "net.clearvolume:cleargl:2.0.1", + "log4j:log4j:1.2.14", + "com.github.holgerbrandl:kscript-annotations:1.4" + ) + } + + } + + // combine kotlin opts spread over multiple lines @Test diff --git a/test/TestsReadme.md b/test/TestsReadme.md index 8dfbe715..47b866b7 100644 --- a/test/TestsReadme.md +++ b/test/TestsReadme.md @@ -81,6 +81,61 @@ which kscript kscript --version which resdeps.kts kscript --clear-cache -kscript https://git.io/v1cG6 my argu ments +kscript https://git.io/v1cG6 my argu ments ``` + + +## Manual testing + +As of this writing, testing the credentials is only done manually with a dockerized Artifactory. + +#### 1. Set up preconfigured artifactory with docker. + +``` +# download and start artifactory container +docker run --name artifactory -d -p 8081:8081 docker.bintray.io/jfrog/artifactory-oss:latest + +# Copy preconfigured gloabl config (with custom repo) and security config (with credentials user) into container. +docker cp ./test/resources/artifactory_config/artifactory.config.xml artifactory:/var/opt/jfrog/artifactory/etc/artifactory.config.import.xml +docker cp ./test/resources/artifactory_config/security_descriptor.xml artifactory:/var/opt/jfrog/artifactory/etc/security.import.xml + +# Make the configs accessable +docker exec -u 0 -it artifactory sh -c 'chmod 777 $ARTIFACTORY_HOME/etc/*.import.xml' + +# Restart docker after is done with initial booting (otherwise restart breaks the container). +echo "sleeping for 15..." && sleep 15 +docker restart artifactory +``` + +#### 2. Create and upload a downloadable archive. +``` +tmpClass=$(mktemp --suffix ".class") +tmpZipDir=$(mktemp -d) +echo "public class something() {}" > $tmpClass +zip $tmpZipDir/tmp.zip $tmpClass +curl --request PUT -u admin:password -T $tmpZipDir/tmp.zip http://localhost:8081/artifactory/authenticated_repo/group/somejar/1.0/somejar-1.0.jar +``` + +#### 3. Then run the following kotlin script with the encrypted password +``` +echo ' +@file:MavenRepository("my-art", "http://localhost:8081/artifactory/authenticated_repo", user="auth_user", password="password") +@file:DependsOn("com.jcabi:jcabi-aether:0.10.1") // If unencrypted works via jcenter +@file:DependsOnMaven("group:somejar:1.0") // If encrypted works. +println("Hello, World!") +' | kscript - +``` + +### Additional info for manual testing + +- Docker & container docu: https://www.jfrog.com/confluence/display/RTF/Installing+with+Docker +- Loading configs docu: https://www.jfrog.com/confluence/display/RTF/Configuration+Files + +``` +# get active security descriptor +curl -u admin:password -X GET -H "Accept: application/xml" http://localhost:8081/artifactory/api/system/security > ./test/resources/artifactory_config/security_descriptor.xml + +# Also works with encrypted password instead of plaintext. +curl -u admin:password -X GET http://localhost:8081/artifactory/api/security/encryptedPassword +``` diff --git a/test/resources/artifactory_config/artifactory.config.xml b/test/resources/artifactory_config/artifactory.config.xml new file mode 100644 index 00000000..c8641f1a --- /dev/null +++ b/test/resources/artifactory_config/artifactory.config.xml @@ -0,0 +1,410 @@ + + + false + true + 100 + 2 + dd-MM-yy HH:mm:ss z + + true + 1552559021264 + + + true + false + + supported + + false + 60 + true + + + true + 3 + 60 + + + + + + false + 5 + + + false + false + + + + backup-daily + true + 0 0 2 ? * MON-FRI + 0 + false + + true + false + false + + + backup-weekly + false + 0 0 2 ? * SAT + 336 + false + + true + false + false + + + + false + 0 23 5 * * ? + + + + artifactory-build-info + buildinfo + Build Info repository + **/* + simple-default + V2 + false + false + true + true + 0 + 0 + true + + false + unique + client-checksums + false + 0 + false + false + + + authenticated_repo + maven + **/* + maven-2-default + V2 + false + false + true + true + 0 + 0 + false + + false + unique + client-checksums + false + 0 + false + false + + + libs-release-local + maven + **/* + maven-2-default + V2 + false + false + true + false + 0 + 0 + false + + false + unique + client-checksums + false + 0 + false + false + + + libs-snapshot-local + maven + **/* + maven-2-default + V2 + false + false + false + true + 0 + 0 + false + + false + unique + client-checksums + false + 0 + false + false + + + + + jcenter + maven + **/* + maven-2-default + V2 + false + false + true + true + 0 + 0 + false + + false + https://jcenter.bintray.com + false + false + true + false + false + 600 + 300 + 1800 + generate-if-absent + 0 + false + false + true + false + https://jcenter.bintray.com + + false + + false + + + false + + + false + + + true + + false + false + 15000 + false + false + false + + + + + libs-release + maven + **/* + maven-2-default + V2 + false + false + + libs-release-local + jcenter + + discard_active_reference + libs-release-local + + 600 + + false + i386,amd64 + + + libs-snapshot + maven + **/* + maven-2-default + V2 + false + false + + libs-snapshot-local + jcenter + + discard_active_reference + libs-snapshot-local + + 600 + + false + i386,amd64 + + + + + + + + + + maven-2-default + [orgPath]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + true + [orgPath]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).pom + SNAPSHOT + SNAPSHOT|(?:(?:[0-9]{8}.[0-9]{6})-(?:[0-9]+)) + + + ivy-default + [org]/[module]/[baseRev](-[folderItegRev])/[type]s/[module](-[classifier])-[baseRev](-[fileItegRev]).[ext] + true + [org]/[module]/[baseRev](-[folderItegRev])/[type]s/ivy-[baseRev](-[fileItegRev]).xml + \d{14} + \d{14} + + + gradle-default + [org]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + true + [org]/[module]/ivy-[baseRev](-[fileItegRev]).xml + \d{14} + \d{14} + + + maven-1-default + [org]/[type]s/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + true + [org]/[type]s/[module]-[baseRev](-[fileItegRev]).pom + .+ + .+ + + + nuget-default + [orgPath]/[module]/[module].[baseRev](-[fileItegRev]).nupkg + false + .* + .* + + + npm-default + [orgPath]/[module]/[module]-[baseRev](-[fileItegRev]).tgz + false + .* + .* + + + bower-default + [orgPath]/[module]/[module]-[baseRev](-[fileItegRev]).[ext] + false + .* + .* + + + vcs-default + [orgPath]/[module]/[refs<tags|branches>]/[baseRev]/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + false + .* + [a-zA-Z0-9]{40} + + + sbt-default + [org]/[module]/(scala_[scalaVersion<.+>])/(sbt_[sbtVersion<.+>])/[baseRev]/[type]s/[module](-[classifier]).[ext] + true + [org]/[module]/(scala_[scalaVersion<.+>])/(sbt_[sbtVersion<.+>])/[baseRev]/[type]s/ivy.xml + \d{14} + \d{14} + + + simple-default + [orgPath]/[module]/[module]-[baseRev].[ext] + false + .* + .* + + + composer-default + [orgPath]/[module]/[module]-[baseRev](-[fileItegRev]).[ext] + false + .* + .* + + + conan-default + [org]/[module]/[baseRev]/[channel<[^/]+>][remainder<(?:.*)>].[ext] + false + .* + .* + + + puppet-default + [orgPath]/[module]/[orgPath]-[module]-[baseRev].tar.gz + false + .* + .* + + + go-default + [orgPath]/[module]/@v/v[refs].zip + false + .* + .* + + + build-default + [orgPath]/[module](-[fileItegRev]).[ext] + false + .* + .* + + + + + + 0 0 /4 * * ? + + + 0 12 5 * * ? + + + 0 12 0 * * ? + + + false + false + 1024 + 5000 + 10 + + + true + false + 14 + + + false + false + + + + false + + + 720 + + + 31536000 + + + 5 + + diff --git a/test/resources/artifactory_config/security_descriptor.xml b/test/resources/artifactory_config/security_descriptor.xml new file mode 100644 index 00000000..12968310 --- /dev/null +++ b/test/resources/artifactory_config/security_descriptor.xml @@ -0,0 +1,193 @@ + + + + + auth_user + bcrypt$$2a$08$C49mKNUv4cBpMKQQx1IWceyi815FxZHoWUKCS5VDFJnxKWnQT/bI2 + mail@example.com + false + true + true + true + false + true + true + internal + false + + readers + + + 1552924560026 + 172.17.0.1 + 0 + false + + + anonymous + false + true + false + true + false + true + true + internal + false + + + 0 + 0 + false + + + admin + bcrypt$$2a$08$nSSjEcBo5PSdfGFPnIKYJunGN4Pe1fQsaOL7WfO8CFn5e7C.JPVri + true + true + true + true + false + true + true + internal + false + + + 1552924483554 + 172.17.0.1 + 0 + false + + + access-admin + bcrypt$$2a$08$R9BzMARP9Lk6Vozos/1Vxekwdx9N6Qd77g8Kpbr8KGb8xrxuLfTya + false + true + false + true + false + true + true + internal + false + + + 0 + 0 + false + + + + + readers + A group for read-only users + true + internal + false + + + + + + + readers + true + 1 + + + anonymous + false + 1 + + + admin + 1552924543459 + jfrt@01d68n3kh4ff1v17hx5cd31ac6:6f18e7e7-ef97-4e6e-b512-5369d735a293 + + Anything + + ** + + + + ANY DISTRIBUTION + ANY REMOTE + + + + + + + auth_user + false + 7 + + + admin + 1552924392860 + jfrt@01d68ps9zp6psd1kte4x180tkv:98074077-5199-4c86-a2b0-4b92a37d18ab + + auth_repos + + ** + + + + authenticated_repo + + + + + + + anonymous + false + 3 + + + _system_ + 1552923015360 + jfrt@01d68n3kh4ff1v17hx5cd31ac6:7b2b808a-31f6-4141-888c-1d542676c213 + + Any Remote + + ** + + + + ANY REMOTE + + + + + + + + + readers + true + 1 + + + anonymous + false + 1 + + + admin + 1552924543525 + jfrt@01d68n3kh4ff1v17hx5cd31ac6:114a4a5d-695b-41e9-8637-8f4e7494ed2a + + Anything + + ** + + + + artifactory-build-info + + + + + \ No newline at end of file