From f5a0ef581fafceafc9dbc14e8eb1932f4068a60c Mon Sep 17 00:00:00 2001 From: Schalk Cronje Date: Mon, 6 May 2019 11:43:53 +0200 Subject: [PATCH 1/4] Run Ivy process in separate Java process (#40) - Restructure code to use external Java process. - Use MarkupBuilder to generate XML. - Additional remote test source set to (mostly) test remote code in isolation. - Ivy & Groovy versin data is read from a properties file. --- CHANGELOG.adoc | 18 +- build.gradle | 58 +-- gradle/integration-tests.gradle | 38 +- gradle/ivyAntVersions.gradle | 21 ++ ...fflineRepositorySyncIntegrationSpec.groovy | 334 +++++++++--------- .../CannotUseCurrentProjectException.groovy | 2 +- .../org/ysb33r/gradle/ivypot/IvyXml.java | 37 ++ .../ivypot/OfflineRepositoryExtension.groovy | 69 ++++ .../ivypot/OfflineRepositoryPlugin.groovy | 8 +- .../ivypot/OfflineRepositorySync.groovy | 228 ++++-------- .../gradle/ivypot/internal/AntLogLevel.groovy | 44 +++ .../gradle/ivypot/internal/Credentials.groovy | 2 +- .../gradle/ivypot/internal/IvyUtils.groovy | 62 ++++ .../internal/PatternBasedResolver.groovy | 2 +- .../gradle/ivypot/remote/ExecutionData.groovy | 46 +++ .../ysb33r/gradle/ivypot/remote/IvyAnt.groovy | 106 ++++++ .../IvyDependency.groovy} | 25 +- .../gradle/ivypot/repositories/Google.groovy | 10 +- .../repositories/IvyArtifactRepository.groovy | 25 +- .../gradle/ivypot/repositories/JCenter.groovy | 10 +- .../MavenArtifactRepository.groovy | 28 +- .../ivypot/repositories/MavenCentral.groovy | 9 +- .../ivypot/repositories/MavenLocal.groovy | 18 +- .../ivypot/repositories/Repository.groovy | 2 +- .../repositories/RepositoryHandler.groovy | 2 +- .../repositories/RepositoryTraits.groovy | 2 +- .../gradle/ivypot/remote/IvyAntSpec.groovy | 93 +++++ .../OfflineRepositoryExtensionSpec.groovy | 34 ++ .../ivypot/OfflineRepositoryPluginSpec.groovy | 6 +- .../ivypot/OfflineRepositorySyncSpec.groovy | 101 +++--- .../internal/PatternBasedResolverSpec.groovy | 2 +- 31 files changed, 959 insertions(+), 483 deletions(-) create mode 100644 gradle/ivyAntVersions.gradle create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.java create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/internal/AntLogLevel.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy rename src/main/groovy/org/ysb33r/gradle/ivypot/{IvyXml.groovy => remote/IvyDependency.groovy} (66%) create mode 100644 src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy create mode 100644 src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index f9bb9f6..1465d90 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,16 +1,30 @@ = CHANGELOG :issue: link:https://github.com/ysb33r/ivypot-gradle-plugin/issues/ +== 0.10 - Roadmap + +=== Improvements + +* {issue}40[#40] Run repository synchronisation process in separate JVM + +=== Breaking changes + +* Not binary compatible with 0.9. + == 0.9 === Compatibility -* Gradle compatibility tested 4.0 - 5.2.1 +* Gradle compatibility tested 5.0 - 5.2.1 === Breaking changes * Minimum JDK8 required -* Major rewrite of the internal reposiotry support. Relies less on Gradle less APIs and provides a DSL layer that mimics the Gradle `repository` structure, but is not binary compatible. +* Major rewrite of the internal repository support. Relies less on Gradle less APIs and provides a DSL layer that mimics the Gradle `repository` structure, but is not binary compatible. + +=== Compatibility + +* Gradle compatibility tested 4.0 - 5.2.1 == 0.8 diff --git a/build.gradle b/build.gradle index bf94d06..24ad954 100644 --- a/build.gradle +++ b/build.gradle @@ -19,9 +19,10 @@ plugins { id 'com.gradle.build-scan' version '2.2.1' id 'groovy' id 'maven' + id 'java-gradle-plugin' id 'com.gradle.plugin-publish' version '0.9.9' id 'com.github.hierynomus.license' version '0.12.1' - id 'org.ysb33r.gradletest' version '2.0-beta.3' + id 'org.ysb33r.gradletest' version '2.0-rc.4' id 'org.ysb33r.os' version '0.9' } @@ -38,49 +39,59 @@ sourceCompatibility = 1.8 targetCompatibility = 1.8 ext { + ivyVersion = '2.4.0' groovyLongVer = GroovySystem.version groovyShortVer = GroovySystem.version.replaceAll(/\.\d+$/, '') - - ivyJar = fileTree("${gradle.gradleHomeDir}/lib/plugins") { - include 'ivy*.jar' - } } -task foo { - doLast { - println ivyJar.files - } -} +apply from: 'gradle/ivyAntVersions.gradle' + repositories { jcenter() } +configurations { + spock + ivy + + testCompile.extendsFrom spock + integrationTestCompile.extendsFrom spock + remoteTestCompile.extendsFrom spock +} + dependencies { - compile 'org.ysb33r.gradle:grolifant:0.10' + spock "org.spockframework:spock-core:1.2-groovy-${groovyShortVer}", { + exclude module: 'groovy-all' + exclude group: 'org.codehaus.groovy' + } + + compile 'org.ysb33r.gradle:grolifant:0.12.1' compile gradleApi() compile localGroovy() - testCompile "org.spockframework:spock-core:1.2-groovy-${groovyShortVer}", { - exclude module: 'groovy-all' - } testCompile 'org.slf4j:slf4j-api:1.7.13' testCompile 'org.slf4j:slf4j-simple:1.7.13' - testRuntime ivyJar -} + integrationTestCompile gradleTestKit() -test { - systemProperties DONT_LOOK_FOR_IVY_JAR: 1 + remoteTestCompile localGroovy() + remoteTestRuntime "org.codehaus.groovy:groovy-ant:${groovyLongVer}" + remoteTestRuntime "org.apache.ivy:ivy:${ivyVersion}" } -integrationTest { - systemProperties TESTROOT: buildDir - +tasks.withType(Test) { if (gradle.startParameter.isOffline()) { systemProperties OFFLINE: 1 } } + +processResources { + from generateIvyAntVersions, { + into 'META-INF' + } +} + license { header = rootProject.file('config/HEADER') strictCheck = true @@ -88,7 +99,7 @@ license { mapping { groovy = 'DOUBLESLASH_STYLE' } - ext.year = '2013-2018' + ext.year = '2013-2019' excludes(['**/*.ad', '**/*.asciidoc', '**/*.adoc', '**/*.md', '**/*.properties', '**/*CompatibilitySpec.groovy']) } @@ -110,7 +121,6 @@ pluginBundle { groupId = project.group artifactId = 'ivypot' } - } publishPlugins { @@ -120,7 +130,7 @@ publishPlugins { } gradleTest { - versions '5.2.1' + versions '5.4.1' versions '5.0' inputs.files jar diff --git a/gradle/integration-tests.gradle b/gradle/integration-tests.gradle index c022d55..f2c66c1 100644 --- a/gradle/integration-tests.gradle +++ b/gradle/integration-tests.gradle @@ -1,6 +1,20 @@ // This is based upon what Rob Fletcher has done at // https://raw.githubusercontent.com/robfletcher/gradle-compass/master/gradle/integration-tests.gradle -import org.gradle.util.GradleVersion + + +sourceSets { + integrationTest { +// java.srcDir file('src/integTest/java') +// groovy.srcDir file('src/integTest/groovy') +// resources.srcDir file('src/integTest/resources') +// compileClasspath = sourceSets.main.output + configurations.integrationTestCompile +// runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime + } + + remoteTest { + compileClasspath = sourceSets.main.output +configurations.remoteTestCompile + } +} configurations { integrationTestCompile { @@ -11,26 +25,18 @@ configurations { } } -sourceSets { - integrationTest { - java.srcDir file('src/integTest/java') - groovy.srcDir file('src/integTest/groovy') - resources.srcDir file('src/integTest/resources') - compileClasspath = sourceSets.main.output + configurations.integrationTestCompile - runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime - } +task remoteTest(type: Test, dependsOn: jar) { + testClassesDirs = sourceSets.remoteTest.output.classesDirs + classpath = sourceSets.remoteTest.runtimeClasspath + mustRunAfter test } task integrationTest(type: Test, dependsOn: jar) { - if(GradleVersion.current() < GradleVersion.version('4.0')) { - testClassesDir = sourceSets.integrationTest.output.classesDir - } else { - testClassesDirs = sourceSets.integrationTest.output.classesDirs - } + testClassesDirs = sourceSets.integrationTest.output.classesDirs classpath = sourceSets.integrationTest.runtimeClasspath - mustRunAfter test + mustRunAfter test, remoteTest } -check.dependsOn integrationTest +check.dependsOn integrationTest, remoteTest diff --git a/gradle/ivyAntVersions.gradle b/gradle/ivyAntVersions.gradle new file mode 100644 index 0000000..1eb61bc --- /dev/null +++ b/gradle/ivyAntVersions.gradle @@ -0,0 +1,21 @@ +task generateIvyAntVersions { + ext { + outFile = file("${buildDir}/ivyAnt/org.ysb33r.gradle.ivypot.versions.properties") + ivyVersion = { project.ext.ivyVersion } + groovyVersion = { project.ext.groovyLongVer } + } + + inputs.properties.put('versions', "${ivyVersion()}${groovyVersion()}" ) + outputs.file(outFile) + + doLast { + def props = new Properties() + props.putAll([ + 'ivy.version': ivyVersion.call(), + 'groovy.version': groovyVersion.call() + ]) + outFile.withOutputStream { strm -> + props.store(strm,'') + } + } +} \ No newline at end of file diff --git a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy index 8990626..75c4594 100644 --- a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy +++ b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -14,64 +14,50 @@ package org.ysb33r.gradle.ivypot -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.artifacts.ConfigurationContainer -import org.gradle.api.artifacts.Dependency -import org.gradle.testfixtures.ProjectBuilder -import spock.lang.Ignore +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.rules.TemporaryFolder import spock.lang.IgnoreIf -import spock.lang.IgnoreRest import spock.lang.Issue +import spock.lang.PendingFeature import spock.lang.Specification -import spock.lang.Stepwise - /** These fixtures needs to run in the order specified as these tests are expensive in terms of downloads * The repository is only created once during the run to save on download time. * * @author Schalk W. Cronjé */ -@Stepwise +@IgnoreIf({ System.getProperty('IS_OFFLINE') }) class OfflineRepositorySyncIntegrationSpec extends Specification { - static final boolean OFFLINE = System.getProperty('IS_OFFLINE') - static final File ROOT = new File("${System.getProperty('TESTROOT') ?: new File('./build').absolutePath}/integrationTest/orsis") - static final File TESTROOT = new File(ROOT,'tests') - static final File LOCALREPO = new File(ROOT, 'local-offline-repo') - - Project project - - void setupSpec() { - OfflineRepositorySync.DONT_LOOK_FOR_IVY_JAR = true + public static final String DEFAULT_TASK = 'syncRemoteRepositories' + public static final String REPO_PATH = 'repo' - if (LOCALREPO.exists()) { - LOCALREPO.deleteDir() - } - - LOCALREPO.mkdirs() + @Rule + TemporaryFolder testFolder - } + File projectDir + File buildFile + File settingsFile + File repoDir + String buildScript void setup() { - if (TESTROOT.exists()) { - TESTROOT.deleteDir() - } + projectDir = testFolder.root + buildFile = new File(projectDir, 'build.gradle') + settingsFile = new File(projectDir, 'settings.gradle') + repoDir = new File(projectDir, REPO_PATH) + settingsFile.text = 'rootProject.name="testproject"' - TESTROOT.mkdirs() - project = ProjectBuilder.builder().withProjectDir(TESTROOT).build() - project.apply plugin: 'org.ysb33r.ivypot' } - @IgnoreIf({ OFFLINE }) - def "Can we sync from mavenCentral?"() { - - given: - def pathToLocalRepo = LOCALREPO + void 'Can we sync from mavenCentral?'() { - project.allprojects { - - configurations { + setup: + writeBuildFile """ + configurations { compile } @@ -84,29 +70,28 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { repositories { mavenCentral() } - - repoRoot "${pathToLocalRepo}" } // end::example_jcenter[] - } + """ - project.evaluate() - project.tasks.syncRemoteRepositories.exec() + when: + BuildResult result = build() - expect: - LOCALREPO.exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/ivy-2.4.xml').exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/commons-io-2.4.jar').exists() + then: + verifyAll { + result.task(":${DEFAULT_TASK}").outcome == TaskOutcome.SUCCESS + repoDir.exists() + } + verifyAll { + file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' + file_exists 'commons-io/commons-io/2.4/commons-io-2.4.jar' + } } - @IgnoreIf({ OFFLINE }) - def "Honour non-transitive dependencies"() { - - given: - def pathToLocalRepo = LOCALREPO - - project.allprojects { + void 'Honour non-transitive dependencies'() { + setup: + writeBuildFile """ configurations { compile } @@ -121,29 +106,27 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { repositories { mavenCentral() } + } + """ - repoRoot "${pathToLocalRepo}" - } - } - - project.evaluate() - project.tasks.syncRemoteRepositories.exec() + when: + BuildResult result = build() - expect: - LOCALREPO.exists() - new File(LOCALREPO, 'junit/junit/4.12/ivy-4.12.xml').exists() - new File(LOCALREPO, 'junit/junit/4.12/junit-4.12.jar').exists() - !new File(LOCALREPO, 'org.hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar').exists() + then: + verifyAll { + result.task(":${DEFAULT_TASK}").outcome == TaskOutcome.SUCCESS + } + verifyAll { + file_exists 'junit/junit/4.12/ivy-4.12.xml' + file_exists 'junit/junit/4.12/junit-4.12.jar' + not_file_exists 'org.hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar' + } } - @IgnoreIf({ OFFLINE }) - def "Can we sync from mavenCentral using a different local artifactPattern?"() { - - given: - def pathToLocalRepo = LOCALREPO - - project.allprojects { + void 'Can we sync from mavenCentral using a different local artifactPattern?'() { + setup: + writeBuildFile """ configurations { compile } @@ -157,29 +140,27 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { mavenCentral() } - repoRoot "${pathToLocalRepo}" - repoArtifactPattern = '[organisation]/[module]/[revision]/[type]s/[artifact]-[revision](.[ext])' } - } + """ - project.evaluate() - project.tasks.syncRemoteRepositories.exec() + when: + BuildResult result = build() - expect: - LOCALREPO.exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/ivy-2.4.xml').exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/jars/commons-io-2.4.jar').exists() + then: + verifyAll { + result.task(":${DEFAULT_TASK}").outcome == TaskOutcome.SUCCESS + } + verifyAll { + file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' + file_exists 'commons-io/commons-io/2.4/jars/commons-io-2.4.jar' + } } - @IgnoreIf({ OFFLINE }) - def "Two syncs to same folder should not cause an overwrite exceptions"() { - - given: - def pathToLocalRepo = LOCALREPO - - project.allprojects { + void "Two syncs to same folder should not cause an overwrite exceptions"() { + setup: + writeBuildFile """ configurations { compile } @@ -193,73 +174,69 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { jcenter() mavenCentral() } - - repoRoot "${pathToLocalRepo}" } - } + """ - project.evaluate() - project.tasks.syncRemoteRepositories.exec() + when: + BuildResult result1 = build() + BuildResult result2 = build() - expect: - new File(LOCALREPO, 'commons-io/commons-io/2.4/ivy-2.4.xml').exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/commons-io-2.4.jar').exists() + then: + verifyAll { + result1.task(":${DEFAULT_TASK}").outcome == TaskOutcome.SUCCESS + result2.task(":${DEFAULT_TASK}").outcome == TaskOutcome.UP_TO_DATE + repoDir.exists() + } + verifyAll { + file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' + file_exists 'commons-io/commons-io/2.4/commons-io-2.4.jar' + } } - - @IgnoreIf({ OFFLINE }) - def "Seting includeBuildScriptDependencies means that buildscript configuration will be added"() { + void "Setting includeBuildScriptDependencies means that buildscript configuration will be added"() { given: - def pathToLocalRepo = new File(ROOT,'second-repo') - - project.allprojects { - - buildscript { - repositories { - ivy { - url LOCALREPO.toURI() - layout 'gradle' - } - } - dependencies { - classpath 'commons-io:commons-io:2.4' - } + withBuildScript ''' + repositories { + mavenCentral() } + dependencies { + classpath 'commons-io:commons-io:2.4' + } + ''' - + writeBuildFile """ syncRemoteRepositories { repositories { - ivy { - url LOCALREPO.toURI() - } + mavenCentral() } - repoRoot "${pathToLocalRepo}" - includeBuildScriptDependencies = true } - } + """ - project.evaluate() + when: + BuildResult result = build() - Set deps = project.syncRemoteRepositories.dependencies - - expect: - deps.find { Dependency it -> it.group == 'commons-io' && it.name == 'commons-io' && it.version == '2.4' } + then: + verifyAll { + result.task(":${DEFAULT_TASK}").outcome == TaskOutcome.SUCCESS + repoDir.exists() + } + verifyAll { + file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' + file_exists 'commons-io/commons-io/2.4/commons-io-2.4.jar' + } } @Issue('https://github.com/ysb33r/ivypot-gradle-plugin/issues/12') - @IgnoreIf({ true || OFFLINE }) - def "Can we sync from a rubygems proxy?"() { - - given: - def pathToLocalRepo = LOCALREPO + @PendingFeature + void "Can we sync from a rubygems proxy?"() { - project.allprojects { - - configurations { + setup: + writeBuildFile """ + configurations { compile } @@ -272,34 +249,27 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { repositories { maven { url 'http://rubygems.lasagna.io/proxy/maven/releases' } } - - repoRoot "${pathToLocalRepo}" } // end::example_rubygems[] - } + """ - project.evaluate() - project.tasks.syncRemoteRepositories.exec() + when: + build() - expect: - LOCALREPO.exists() - new File(LOCALREPO, 'rubygems/colorize/0.7.7/ivys/ivy.xml').exists() - new File(LOCALREPO, 'rubygems/colorize/0.7.7/gems/colorize.gem').exists() + then: + file_exists'rubygems/colorize/0.7.7/ivys/ivy.xml' + file_exists'rubygems/colorize/0.7.7/gems/colorize.gem' } - @IgnoreIf({ OFFLINE }) - def "Can we sync from Google?"() { - - given: - def pathToLocalRepo = LOCALREPO - - project.allprojects { + void 'Can we sync from Google?'() { + setup: + writeBuildFile """ configurations { compile } - // tag::example_jcenter[] + // tag::example_google[] dependencies { compile 'com.android.support.constraint:constraint-layout:1.0.2' } @@ -308,19 +278,59 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { repositories { google() } - - repoRoot "${pathToLocalRepo}" } - // end::example_jcenter[] - } + // end::example_google[] + """ + + when: + build() + + then: + file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' + file_exists 'commons-io/commons-io/2.4/commons-io-2.4.jar' + } + + private boolean file_exists(String path) { + new File(repoDir, path).exists() + } + + private boolean not_file_exists(String path) { + !file_exists(path) + } - project.evaluate() - project.tasks.syncRemoteRepositories.exec() + private BuildResult build() { + GradleRunner.create().withDebug(true) + .withPluginClasspath() + .withProjectDir(projectDir) + .withArguments(['-i', '-s', DEFAULT_TASK]) + .forwardOutput() + .build() + } - expect: - LOCALREPO.exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/ivy-2.4.xml').exists() - new File(LOCALREPO, 'commons-io/commons-io/2.4/commons-io-2.4.jar').exists() + void withBuildScript(String content) { + buildScript = """ + buildscript { + ${content} + } + """ } + private void writeBuildFile(String content) { + buildFile.text = """ + ${buildScript ?: ''} + + plugins { + id 'org.ysb33r.ivypot' + } + + repositories { + mavenCentral() + jcenter() + } + + syncRemoteRepositories.repoRoot '${REPO_PATH}' + + ${content} + """ + } } \ No newline at end of file diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/CannotUseCurrentProjectException.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/CannotUseCurrentProjectException.groovy index 7b27a26..ac3e9f3 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/CannotUseCurrentProjectException.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/CannotUseCurrentProjectException.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.java b/src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.java new file mode 100644 index 0000000..60f7460 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.java @@ -0,0 +1,37 @@ +/** + * ============================================================================ + * (C) Copyright Schalk W. Cronje 2013-2019 + * + * This software is licensed under the Apache License 2.0 + * See http://www.apache.org/licenses/LICENSE-2.0 for license details + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + * + * ============================================================================ + */ +package org.ysb33r.gradle.ivypot; + +import groovy.xml.MarkupBuilder; + +import java.io.StringWriter; + +/** + * @author Schalk W. Cronjé + */ +public interface IvyXml { + /** + * Returns a XML snippet suitable for including in the resolvers section + * + * @return Returns the XML that describes a specific section for Ivy settings. + */ + default String resolverXml() { + StringWriter writer = new StringWriter(); + MarkupBuilder builder = new MarkupBuilder(writer); + writeTo(builder); + return writer.toString(); + } + + void writeTo(MarkupBuilder builder); +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy new file mode 100644 index 0000000..90dcada --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy @@ -0,0 +1,69 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot + +import groovy.transform.CompileStatic +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency + +@CompileStatic +class OfflineRepositoryExtension { + + String groovyVersion = GroovySystem.version + String ivyVersion = '2.3.0' + + OfflineRepositoryExtension(Project project) { + this.project = project + + + this.class.getResourceAsStream('/META-INF/org.ysb33r.gradle.ivypot.versions.properties').withCloseable { + if (it != null) { + Properties props = new Properties() + props.load(it) + + if (props.containsKey('ivy.version')) { + ivyVersion = props['ivy.version'] + } + if (props.containsKey('groovy.version')) { + groovyVersion = props['groovy.version'] + } + } + } + } + + Configuration getConfiguration() { + def deps = [ + createDependency("${GROOVY_GROUP}:groovy-ant:${groovyVersion}"), + createDependency("${IVY_GROUP}:ivy:${ivyVersion}") + ] + + project.configurations.detachedConfiguration( + deps.toArray() as Dependency[] + ) + } + + private Dependency createDependency(final String notation, final Closure configurator = null) { + if (configurator) { + project.dependencies.create(notation, configurator) + } else { + project.dependencies.create(notation) + } + } + + private static final String GROOVY_GROUP = 'org.codehaus.groovy' + private static final String IVY_GROUP = 'org.apache.ivy' + private Project project +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy index 8fc97da..ebcc3b1 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -24,13 +24,13 @@ import org.gradle.util.GradleVersion */ class OfflineRepositoryPlugin implements Plugin { - final static String MINIMUM_GRADLE = '4.0' + final static String MINIMUM_GRADLE = '5.0' void apply(Project project) { - if (GradleVersion.current() < GradleVersion.version(MINIMUM_GRADLE)) { throw new GradleException("Ivypot can only be used with Gradle ${MINIMUM_GRADLE} or later") } - project.tasks.create 'syncRemoteRepositories', OfflineRepositorySync + project.extensions.create('offlineRepositories', OfflineRepositoryExtension, project) + project.tasks.create('syncRemoteRepositories', OfflineRepositorySync) } } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy index 5ccf358..0ba2337 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -16,58 +16,46 @@ package org.ysb33r.gradle.ivypot import groovy.transform.CompileDynamic import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.apache.tools.ant.BuildListener -import org.apache.tools.ant.DefaultLogger +import groovy.xml.MarkupBuilder import org.gradle.api.DefaultTask -import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.artifacts.repositories.IvyArtifactRepository -import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import org.gradle.internal.FileUtils -import org.gradle.util.GradleVersion +import org.gradle.process.JavaExecSpec +import org.ysb33r.gradle.ivypot.internal.AntLogLevel +import org.ysb33r.gradle.ivypot.internal.IvyUtils +import org.ysb33r.gradle.ivypot.remote.ExecutionData +import org.ysb33r.gradle.ivypot.remote.IvyAnt +import org.ysb33r.gradle.ivypot.remote.IvyDependency import org.ysb33r.gradle.ivypot.repositories.RepositoryHandler +import org.ysb33r.grolifant.api.FileUtils import org.ysb33r.grolifant.api.StringUtils +import static org.ysb33r.grolifant.api.FileUtils.resolveClassLocation +import static org.ysb33r.grolifant.api.FileUtils.toSafeFileName + @CompileStatic class OfflineRepositorySync extends DefaultTask { @CompileDynamic OfflineRepositorySync() { - String ivyJar = findIvyJarPath(project) - ivyAnt = new AntBuilder() - - ivyAnt.taskdef name: "${name}Configure", - classname: 'org.apache.ivy.ant.IvyConfigure', - classpath: ivyJar - - ivyAnt.taskdef name: "${name}Resolve", - classname: 'org.apache.ivy.ant.IvyResolve', - classpath: ivyJar repositories = new RepositoryHandler(project) - if (GradleVersion.current() < GradleVersion.version('4.0')) { - inputs.properties.put('project configurations', { -> - this.projectConfigurations - }) - } else { - inputs.property('project configurations', { OfflineRepositorySync ors -> - Set configs = ors.getConfigurations() - configs.collect { Configuration c -> - c.dependencies.collect { Dependency d -> - "${d.group}:${d.name}:${d.version}" - }.join(',') - }.join('|') - }.curry(this)) - } + inputs.property('project configurations', { OfflineRepositorySync ors -> + Set configs = ors.getConfigurations() + configs.collect { Configuration c -> + c.dependencies.collect { Dependency d -> + "${d.group}:${d.name}:${d.version}" + }.join(',') + }.join('|') + }.curry(this)) } /** The pattern that will be used to write artifacts into the target repository @@ -101,7 +89,6 @@ class OfflineRepositorySync extends DefaultTask { this.repoRoot = repo } - /** If no configurations were listed, returns all the configurations * * @return A project configuration container with all of the named configurations. Does not @@ -241,7 +228,7 @@ class OfflineRepositorySync extends DefaultTask { if (includeBuildScriptDependencies) { deps.addAll(getExternalModuleDependencies( - project.rootProject.buildscript.configurations.getByName('classpath') + project.rootProject.buildscript.configurations.getByName('classpath') )) } @@ -258,140 +245,78 @@ class OfflineRepositorySync extends DefaultTask { @TaskAction void exec() { + File executionDataFile = new File(project.buildDir, "tmp/${toSafeFileName(name)}/execution.data") + File ivySettingsFile = createIvySettingsFile() - boolean overwrite = project.gradle.startParameter.isRefreshDependencies() - - getRepoRoot().mkdirs() - initIvyInstaller() - setAntLogLevel() + List ivyDeps = dependencies.collect { + ivyDependency(it) + } - dependencies.each { Dependency dep -> - ivyInstall(dep, overwrite) + ExecutionData executionData = new ExecutionData() + executionData.with { + overwrite = project.gradle.startParameter.isRefreshDependencies() + ivySettings = ivySettingsFile.canonicalFile + ivyRepoRoot = getRepoRoot().canonicalFile + logLevel = AntLogLevel.fromGradleLogLevel(logging.level) + dependencies.addAll(ivyDeps) } + ExecutionData.serializeData(executionDataFile, executionData) + Configuration ivyRuntime = offlineRepositorySync.configuration + File ivyAnt = resolveClassLocation(IvyAnt).file + project.javaexec { JavaExecSpec jes -> + jes.with { + main = IvyAnt.canonicalName + classpath ivyRuntime, ivyAnt + args executionDataFile.absolutePath + } + } } - /** - * - * @param dep - * @param overwrite - * - * @sa {@link https://ant.apache.org/ivy/history/trunk/use/resolve.html} - */ - @PackageScope - @CompileDynamic - void ivyInstall(Dependency dep, boolean overwrite) { - - ivyAnt."${name}Resolve"( - inline: true, - organisation: dep.group, module: dep.name, revision: dep.version, - transitive: dep instanceof ModuleDependency ? ((ModuleDependency) dep).transitive : true, - type: '*', - conf: '*' + private IvyDependency ivyDependency(Dependency dep) { + new IvyDependency( + organisation: dep.group, + module: dep.name, + revision: dep.version, + transitive: dep instanceof ModuleDependency ? ((ModuleDependency) dep).transitive : true, + typeFilter: '*', + confFilter: '*' ) } - @PackageScope - @CompileDynamic - void initIvyInstaller() { - ivyAnt."${name}Configure" file: createIvySettingsFile().absolutePath - - } - - @PackageScope - File createIvySettingsFile() { - def target = new File(temporaryDir, 'ivysettings.xml') - target.text = ivyXml() - target + private OfflineRepositoryExtension getOfflineRepositorySync() { + project.extensions.getByType(OfflineRepositoryExtension) } + private File createIvySettingsFile() { + File target = new File(temporaryDir, 'ivysettings.xml') + IvyUtils.writeSettingsFile( + target, + repositories, + getRepoRoot(), + new File(project.buildDir, "tmp/ivypot/${FileUtils.toSafeFileName(name)}"), + repoIvyPattern, + repoArtifactPattern, + repositoryCredentials - /** Returns the XML required for ivysettings.xml. - * @sa {@link https://github.com/apache/groovy/blob/master/src/resources/groovy/grape/defaultGrapeConfig.xml} - */ - @PackageScope - @CompileDynamic - String ivyXml() { - File cacheDir = project.gradle.startParameter.projectCacheDir ?: new File(project.buildDir, 'tmp') - String xml = "" - - xml += "" - - this.repositories.each { - if (it.metaClass.respondsTo(it, 'getCredentials')) { - if (it.credentials.username && it.credentials.password) { - xml += """" - - this.repositories.each { xml += it.resolverXml() } - - xml += """""" - } - - @PackageScope - void setAntLogLevel() { - if (ivyAnt) { - org.apache.tools.ant.Project localRef = ivyAnt.project - LogLevel gradleLogLevel = project.logging.level - ivyAnt.project.buildListeners.each { BuildListener it -> - if (it instanceof DefaultLogger) { - DefaultLogger antLogger = ((DefaultLogger) it) - switch (gradleLogLevel) { - case null: - case gradleLogLevel.LIFECYCLE: - antLogger.messageOutputLevel = localRef.MSG_WARN - break - case gradleLogLevel.DEBUG: - antLogger.messageOutputLevel = localRef.MSG_DEBUG - break - case gradleLogLevel.QUIET: - antLogger.messageOutputLevel = localRef.MSG_ERR - break - case gradleLogLevel.INFO: - antLogger.messageOutputLevel = localRef.MSG_VERBOSE - break - } - } - } - } + ) + target } - /** Returns the JAR path to be used for loading IVY. - * - * @param project - * @return Returns the classpath (or null if the class is already available). - */ @CompileDynamic - private static String findIvyJarPath(Project project) { - if (DONT_LOOK_FOR_IVY_JAR) { - return null - } else { - def files = new File(project.gradle.gradleHomeDir, 'lib/plugins').listFiles(new FilenameFilter() { - @Override - boolean accept(File dir, String name) { - name ==~ /ivy-\d.+.jar/ - } - }) - - if (!files?.size()) { - throw new GradleException("Cannot locate an Ivy Ant jar in ${project.gradle.gradleHomeDir}/lib/plugins") - } - - return files[0] + private List> getRepositoryCredentials() { + this.repositories.findAll { repo -> + repo.metaClass.respondsTo(repo, 'getRepositoryCredentials') && repo.credentials?.username && repo.credentials?.password + }.collect { repo -> + [ + host : repo.url.host, + username: repo.credentials.username, + password: repo.credentials.password + ] as Map } } private void addConfigurationsRecursivelyFrom(final Project p) { - if (p != project) { projectConfigurations[p] = [] } @@ -425,11 +350,6 @@ class OfflineRepositorySync extends DefaultTask { private Object repoRoot private final RepositoryHandler repositories - private final AntBuilder ivyAnt private final List configurations = [] private final Map> projectConfigurations = [:] - - private static final String LOCALREPONAME = '~~~local~~~repo~~~' - private static final String REMOTECHAINNAME = '~~~remote~~~resolvers~~~' - private static boolean DONT_LOOK_FOR_IVY_JAR = System.getProperty('DONT_LOOK_FOR_IVY_JAR') } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/AntLogLevel.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/AntLogLevel.groovy new file mode 100644 index 0000000..976eebe --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/AntLogLevel.groovy @@ -0,0 +1,44 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot.internal + +import groovy.transform.CompileStatic +import org.gradle.api.logging.LogLevel + +import static org.apache.tools.ant.Project.MSG_DEBUG +import static org.apache.tools.ant.Project.MSG_ERR +import static org.apache.tools.ant.Project.MSG_VERBOSE +import static org.apache.tools.ant.Project.MSG_WARN + +@CompileStatic +class AntLogLevel { + static int fromGradleLogLevel(LogLevel gradleLogLevel) { + switch (gradleLogLevel) { + case null: + case gradleLogLevel.LIFECYCLE: + MSG_WARN + break + case gradleLogLevel.DEBUG: + MSG_DEBUG + break + case gradleLogLevel.QUIET: + MSG_ERR + break + case gradleLogLevel.INFO: + MSG_VERBOSE + break + } + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/Credentials.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/Credentials.groovy index 3ab54bd..48cb37d 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/Credentials.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/Credentials.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy new file mode 100644 index 0000000..a4862af --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy @@ -0,0 +1,62 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot.internal + +import groovy.transform.CompileDynamic +import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder +import org.ysb33r.gradle.ivypot.repositories.RepositoryHandler +import org.ysb33r.grolifant.api.FileUtils + +@CompileStatic +class IvyUtils { + public static final String REMOTECHAINNAME = '~~~remote~~~resolvers~~~' + + @CompileDynamic + static void writeSettingsFile( + File settingsFile, + RepositoryHandler repositories, + File repoRoot, + File cacheDir, + String ivyPattern, + String artifactPattern, + Iterable> repositoryCredentials + ) { + def xmlWriter = new StringWriter() + def xml = new MarkupBuilder(xmlWriter) + xml.ivysettings { + settings(defaultResolver: REMOTECHAINNAME) + caches( + defaultCacheDir: repoRoot.absolutePath, + artifactPattern: artifactPattern, + ivyPattern: ivyPattern, + resolutionCacheDir: cacheDir.absolutePath + ) + + repositoryCredentials.each { repo -> + credentials(repo) + } + + resolvers { + chain(name: REMOTECHAINNAME, returnFirst: true) { + repositories.each { repo -> + repo.writeTo(xml) + } + } + } + } + settingsFile.text = xmlWriter.toString() + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolver.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolver.groovy index 987120a..8ae19d8 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolver.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolver.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy new file mode 100644 index 0000000..cdd3753 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy @@ -0,0 +1,46 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot.remote + +import groovy.transform.CompileStatic + +@CompileStatic +class ExecutionData implements Serializable { + + boolean overwrite + int logLevel + File ivySettings + File ivyRepoRoot + List dependencies = [] + + static void serializeData(File destination, ExecutionData data) { + destination.parentFile.mkdirs() + destination.withOutputStream { out -> + new ObjectOutputStream(out).withCloseable { oos -> + oos.writeObject(data) + } + } + } + + static ExecutionData deserializeData(File source) { + ExecutionData ret + source.withInputStream { input -> + new ObjectInputStream(input).withCloseable { ois -> + ret = (ExecutionData) ois.readObject() + } + } + ret + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy new file mode 100644 index 0000000..3ca65cd --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy @@ -0,0 +1,106 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot.remote + +import groovy.transform.CompileDynamic +import groovy.transform.CompileStatic +import org.apache.tools.ant.BuildListener +import org.apache.tools.ant.DefaultLogger +import org.apache.tools.ant.Project + +@CompileStatic +class IvyAnt { + public static final String CONFIGURE_TASK = 'ivyConfigure' + public static final String CONFIGURE_CLASS = 'org.apache.ivy.ant.IvyConfigure' + public static final String RESOLVE_TASK = 'ivyResolve' + public static final String RESOLVE_CLASS = 'org.apache.ivy.ant.IvyResolve' + + static void main(String[] args) { + if (args.size() != 1) { + throw new RuntimeException('No serialised location specified') + } + + ExecutionData executionData = ExecutionData.deserializeData(new File(args[0])) + def ivyAnt = new IvyAnt(executionData.ivySettings) + ivyAnt.logLevel = executionData.logLevel + ivyAnt.resolve( + executionData.ivyRepoRoot, + executionData.dependencies, + executionData.overwrite + ) + } + + IvyAnt(File ivySettings) { + this.ivySettings = ivySettings + ivyAnt = new AntBuilder() + configureAnt() + initialiseIvy() + } + + void setLogLevel(int antLogLevel) { + Project localRef = ivyAnt.project + + localRef.buildListeners.findAll { BuildListener it -> + it instanceof DefaultLogger + }.each { + ((DefaultLogger) it).messageOutputLevel = antLogLevel + } + } + + void resolve(File repoRoot, List deps, boolean overwrite) { + repoRoot.mkdirs() + deps.each { + ivyInstall(it, overwrite) + } + } + + @CompileDynamic + private void initialiseIvy() { + ivyAnt."${CONFIGURE_TASK}" file: ivySettings.absolutePath + } + + /** + * + * @param dep + * @param overwrite + * + * @sa {@link https://ant.apache.org/ivy/history/trunk/use/resolve.html} + */ + @CompileDynamic + private void ivyInstall(IvyDependency dep, boolean overwrite) { + ivyAnt."${RESOLVE_TASK}"( + inline: true, + organisation: dep.organisation, module: dep.module, revision: dep.revision, + transitive: dep.transitive, + type: dep.typeFilter, + conf: dep.confFilter + ) + } + + @CompileDynamic + private void configureAnt() { + + ivyAnt.taskdef name: CONFIGURE_TASK, + classname: CONFIGURE_CLASS +// classpath: ivyJar + + ivyAnt.taskdef name: RESOLVE_TASK, + classname: RESOLVE_CLASS +// classpath: ivyJar + } + + private final AntBuilder ivyAnt + private final File ivySettings +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy similarity index 66% rename from src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.groovy rename to src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy index 332a147..8393764 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/IvyXml.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -12,15 +12,16 @@ // ============================================================================ // -package org.ysb33r.gradle.ivypot +package org.ysb33r.gradle.ivypot.remote -/** - * @author Schalk W. Cronjé - */ -interface IvyXml { - /** Returns a XML snippet suitable for including in the resolvers section - * - * @return - */ - String resolverXml() -} \ No newline at end of file +import groovy.transform.CompileStatic + +@CompileStatic +class IvyDependency implements Serializable { + String organisation + String module + String revision + boolean transitive + String typeFilter + String confFilter +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Google.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Google.groovy index f10639a..29958bf 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Google.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Google.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -14,7 +14,9 @@ package org.ysb33r.gradle.ivypot.repositories +import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder /** * @since 1.0 @@ -23,8 +25,8 @@ import groovy.transform.CompileStatic class Google extends MavenArtifactRepository { @Override - String resolverXml() { - """""" + @CompileDynamic + void writeTo(MarkupBuilder builder) { + builder.ibiblio(name: name, root: 'https://dl.google.com/dl/android/maven2/', m2compatible: true) } - } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/IvyArtifactRepository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/IvyArtifactRepository.groovy index 4c05546..191c91d 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/IvyArtifactRepository.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/IvyArtifactRepository.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -16,6 +16,7 @@ package org.ysb33r.gradle.ivypot.repositories import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder import org.gradle.api.Action import org.gradle.api.artifacts.repositories.RepositoryLayout import org.ysb33r.gradle.ivypot.internal.PatternBasedResolver @@ -158,13 +159,9 @@ class IvyArtifactRepository implements Repository, RepositoryTraits { action.execute(repositoryLayout) } - /** Returns a XML snippet suitable for including in the resolvers section - * - * @return - */ @Override - String resolverXml() { - + @CompileDynamic + void writeTo(MarkupBuilder builder) { if (repositoryLayout == null) { throw new UnsupportedOperationException('layout has not seen set for Ivy repository') } @@ -172,14 +169,14 @@ class IvyArtifactRepository implements Repository, RepositoryTraits { PatternBasedResolver patterns = new PatternBasedResolver() applyPatterns(patterns) - String ret = "" - patterns.ivyPatterns.each { - ret += "" - } - patterns.artifactPatterns.each { - ret += "" + builder.url(name: name, m2compatible: patterns.m2compatible) { + patterns.ivyPatterns.each { pat -> + ivy(pattern: pat.pattern) + } + patterns.artifactPatterns.each { pat -> + artifact(pattern: pat.pattern) + } } - ret += '' } @CompileDynamic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/JCenter.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/JCenter.groovy index 2bde383..b9e5282 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/JCenter.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/JCenter.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -14,7 +14,9 @@ package org.ysb33r.gradle.ivypot.repositories +import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder /** * @author Schalk W. Cronjé @@ -23,8 +25,8 @@ import groovy.transform.CompileStatic class JCenter extends MavenArtifactRepository { @Override - String resolverXml() { - """""" + @CompileDynamic + void writeTo(MarkupBuilder builder) { + builder.ibiblio(name: name, root: 'https://jcenter.bintray.com/', m2compatible: true) } - } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenArtifactRepository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenArtifactRepository.groovy index 495e410..9420143 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenArtifactRepository.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenArtifactRepository.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -16,6 +16,7 @@ package org.ysb33r.gradle.ivypot.repositories import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder import org.ysb33r.grolifant.api.StringUtils /** @@ -30,7 +31,7 @@ class MavenArtifactRepository implements Repository, RepositoryTraits { * @return The additional URLs. Returns an empty list if there are no such URLs. */ Set getArtifactUrls() { - StringUtils.stringize(this.artifactUrls).collect{ String it -> it.toURI() } as Set + StringUtils.stringize(this.artifactUrls).collect { String it -> it.toURI() } as Set } /** @@ -72,24 +73,19 @@ class MavenArtifactRepository implements Repository, RepositoryTraits { artifactUrls.addAll(urls) } - /** Returns a XML snippet suitable for including in the resolvers section - * - * @return - */ @Override - String resolverXml() { - if(artifactUrls.size()) { - String ret = "" - getArtifactUrls().eachWithIndex { URI u,int index -> - ret+= "" + @CompileDynamic + void writeTo(MarkupBuilder builder) { + if (artifactUrls.size()) { + builder.chain(name: name) { + ibiblio(name: "${name}_root}", m2compatible: true, root: url, usepoms: true) + getArtifactUrls().eachWithIndex { URI u, int index -> + ibiblio(name: "${name}_${index}", m2compatible: true, root: u, usepoms: false) + } } - - ret + '' - } else { - "" + builder.ibiblio(name: name, m2compatible: true, root: url) } - } private List artifactUrls = [] diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenCentral.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenCentral.groovy index 95e48a7..432c69c 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenCentral.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenCentral.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -14,7 +14,9 @@ package org.ysb33r.gradle.ivypot.repositories +import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder /** * @since 1.0 @@ -22,7 +24,8 @@ import groovy.transform.CompileStatic @CompileStatic class MavenCentral extends MavenArtifactRepository { @Override - String resolverXml() { - """""" + @CompileDynamic + void writeTo(MarkupBuilder builder) { + builder.ibiblio(name: name, m2compatible: true) } } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenLocal.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenLocal.groovy index fdaa8fc..9e8b90d 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenLocal.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/MavenLocal.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -13,6 +13,9 @@ // package org.ysb33r.gradle.ivypot.repositories + +import groovy.xml.MarkupBuilder + /** * @since 1.0 */ @@ -24,9 +27,14 @@ class MavenLocal extends MavenArtifactRepository { } @Override - String resolverXml() { - """""" + void writeTo(MarkupBuilder builder) { + builder.ibiblio( + name:name, + root: url, + m2compatible: true, + checkmodified: true, + changingPattern:'.*', + changingMatcher: 'regexp' + ) } } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Repository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Repository.groovy index aab88ce..c8013d9 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Repository.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/Repository.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryHandler.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryHandler.groovy index 7df1269..6ef71a9 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryHandler.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryHandler.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryTraits.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryTraits.groovy index 47d92b2..a266a86 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryTraits.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/RepositoryTraits.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details diff --git a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy new file mode 100644 index 0000000..613bb9c --- /dev/null +++ b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy @@ -0,0 +1,93 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot.remote + +import groovy.xml.MarkupBuilder +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.IgnoreIf +import spock.lang.Specification + +class IvyAntSpec extends Specification { + + @Rule + TemporaryFolder testProject + + File ivySettings + File repoDir + File cacheDir + + void setup() { + ivySettings = new File(testProject.root, 'ivysettings.xml') + repoDir = new File(testProject.root, 'repo') + cacheDir = new File(testProject.root, 'cache') + writeIvySettings() + } + + void 'Can initialise IvyAnt'() { + when: + IvyAnt ivyAnt = new IvyAnt(ivySettings) + + then: + noExceptionThrown() + + when: + ivyAnt.logLevel = 2 + + then: + noExceptionThrown() + } + + @IgnoreIf({ System.getProperty('OFFLINE') }) + void 'Can resolve an artifact'() { + + when: + def dep = new IvyDependency( + organisation: 'commons-io', + module: 'commons-io', + revision: '2.4', + transitive: true, + typeFilter: '*', + confFilter: '*' + ) + + new IvyAnt(ivySettings).resolve(repoDir, [dep], true) + + then: + verifyAll { + new File(repoDir, 'commons-io/commons-io/2.4/ivy-2.4.xml').exists() + new File(repoDir, 'commons-io/commons-io/2.4/commons-io-2.4.jar').exists() + } + } + + private void writeIvySettings() { + ivySettings.withWriter { writer -> + new MarkupBuilder(writer).ivysettings { + settings(defaultResolver: 'foobar') + caches( + defaultCacheDir: repoDir.absolutePath, + resolutionCacheDir: cacheDir.absolutePath, + artifactPattern: '[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])', + ivyPattern: '[organisation]/[module]/[revision]/ivy-[revision].xml', + ) + resolvers { + chain(name: 'foobar', returnFirst: true) { + ibiblio(name: 'MavenRepo', m2compatible: true) + } + } + } + } + } +} diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy new file mode 100644 index 0000000..8dab3cd --- /dev/null +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy @@ -0,0 +1,34 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + +package org.ysb33r.gradle.ivypot + +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import spock.lang.Specification + + +class OfflineRepositoryExtensionSpec extends Specification { + + Project project = ProjectBuilder.builder().build() + + void 'Version values are read from property file'() { + given: + OfflineRepositoryExtension ext = project.extensions.create('foo', OfflineRepositoryExtension, project) + + expect: + ext.ivyVersion != '2.3.0' + ext.groovyVersion != ext.ivyVersion + } +} \ No newline at end of file diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPluginSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPluginSpec.groovy index c3154bf..c5aa20a 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPluginSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPluginSpec.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -23,10 +23,6 @@ import spock.lang.Specification */ class OfflineRepositoryPluginSpec extends Specification { - void setupSpec() { - OfflineRepositorySync.DONT_LOOK_FOR_IVY_JAR = true - } - def "Can the plugin be applied"() { given: def project = ProjectBuilder.builder().build() diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy index 53f42b7..6ee4cde 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details @@ -14,14 +14,13 @@ package org.ysb33r.gradle.ivypot + import org.gradle.api.Project import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.repositories.IvyArtifactRepository import org.gradle.testfixtures.ProjectBuilder import spock.lang.Specification - /** * @author Schalk W. Cronjé */ @@ -30,15 +29,12 @@ class OfflineRepositorySyncSpec extends Specification { Project project OfflineRepositorySync syncTask - void setupSpec() { - OfflineRepositorySync.DONT_LOOK_FOR_IVY_JAR = true - } void setup() { project = ProjectBuilder.builder().build() - syncTask = project.tasks.create('syncTask',OfflineRepositorySync) + syncTask = project.tasks.create('syncTask', OfflineRepositorySync) } - void "Setting up repositories" () { + void "Setting up repositories"() { given: project.allprojects { @@ -47,7 +43,7 @@ class OfflineRepositorySyncSpec extends Specification { syncTask { repoRoot '/path/to/folder' - configurations 'compile','testCompile' + configurations 'compile', 'testCompile' repositories { mavenCentral() @@ -103,26 +99,26 @@ class OfflineRepositorySyncSpec extends Specification { } // Need to extract repositories in order collected using these gradle-assigned names - def mavenC = syncTask.repositories.getByName('MavenRepo') - def mavenL = syncTask.repositories.getByName('MavenLocal') - def maven2 = syncTask.repositories.getByName('maven_1') - def maven3 = syncTask.repositories.getByName('maven_2') - def bintray= syncTask.repositories.getByName('BintrayJCenter') - def ivyMaven= syncTask.repositories.getByName('ivy_3') - def ivyGradle= syncTask.repositories.getByName('ivy_4') - def ivyIvy= syncTask.repositories.getByName('ivy_5') - def ivyPattern= syncTask.repositories.getByName('ivy_6') - def google= syncTask.repositories.getByName('Google') + def mavenC = syncTask.repositories.getByName('MavenRepo') + def mavenL = syncTask.repositories.getByName('MavenLocal') + def maven2 = syncTask.repositories.getByName('maven_1') + def maven3 = syncTask.repositories.getByName('maven_2') + def bintray = syncTask.repositories.getByName('BintrayJCenter') + def ivyMaven = syncTask.repositories.getByName('ivy_3') + def ivyGradle = syncTask.repositories.getByName('ivy_4') + def ivyIvy = syncTask.repositories.getByName('ivy_5') + def ivyPattern = syncTask.repositories.getByName('ivy_6') + def google = syncTask.repositories.getByName('Google') expect: 'Local repo has been set' syncTask.repoRoot == project.file('/path/to/folder') and: 'mavenCentral is loaded' - mavenC.resolverXml() == """""" + mavenC.resolverXml() == $//$ and: 'a maven repo can be added' maven2.url == 'http://foo.com/bar'.toURI() - maven2.resolverXml() == "" + maven2.resolverXml() == $//$ and: 'a maven repo with artifact urls and credentials can be added' maven3.url == 'http://hog.com/whole'.toURI() @@ -130,45 +126,48 @@ class OfflineRepositorySyncSpec extends Specification { maven3.artifactUrls.contains('http://hog.com/two'.toURI()) maven3.credentials.username == 'the' maven3.credentials.password == 'pig' - maven3.resolverXml() == '''''' + - "" + - "" + - '' + maven3.resolverXml() == ''' + + + +''' and: 'JCenter is loaded' - bintray.resolverXml() == '' + bintray.resolverXml() == $//$ and: 'mavenLocal is loaded' - mavenL.resolverXml() == """""" + mavenL.resolverXml() == $//$ and: 'ivy with maven layout loaded' - ivyMaven.resolverXml() == '''''' + - "" + - "" + - '' + ivyMaven.resolverXml() == """ + + +""" and: 'ivy with gradle layout loaded' - ivyGradle.resolverXml() == '''''' + - "" + - "" + - '' + ivyGradle.resolverXml() == """ + + +""" and: 'ivy with ivy layout loaded + credentials' - ivyIvy.resolverXml() == '''''' + - "" + - "" + - '' + ivyIvy.resolverXml() == """ + + +""" ivyIvy.credentials.username == 'the' ivyIvy.credentials.password == 'pig' and: 'ivy with pattern layout loaded' - ivyPattern.resolverXml() == '''''' + - "" + - "" + - '' + ivyPattern.resolverXml() == ''' + + +''' and: 'google was loaded' - google.resolverXml() == '''''' + google.resolverXml() == $//$ } void "Not specifying a configuration, means all configurations are loaded"() { @@ -186,8 +185,8 @@ class OfflineRepositorySyncSpec extends Specification { Iterable configs = syncTask.configurations expect: - getConfiguration(configs,'config1') != null - getConfiguration(configs,'config2') != null + getConfiguration(configs, 'config1') != null + getConfiguration(configs, 'config2') != null configs.size() == 2 } @@ -208,10 +207,10 @@ class OfflineRepositorySyncSpec extends Specification { Iterable configs = syncTask.configurations then: - getConfiguration(configs,'config2') == null + getConfiguration(configs, 'config2') == null and: - getConfiguration(configs,'config1') != null + getConfiguration(configs, 'config1') != null configs.size() == 1 } @@ -231,10 +230,10 @@ class OfflineRepositorySyncSpec extends Specification { Iterable configs = syncTask.configurations then: - getConfiguration(configs,'config2') == null + getConfiguration(configs, 'config2') == null and: - getConfiguration(configs,'config1') != null + getConfiguration(configs, 'config1') != null configs.size() == 1 } @@ -251,7 +250,7 @@ class OfflineRepositorySyncSpec extends Specification { thrown(CannotUseCurrentProjectException) } - private getConfiguration(final Iterable configs,final String name) { + private getConfiguration(final Iterable configs, final String name) { configs.find { it.name == name } diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolverSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolverSpec.groovy index 29652e4..939199c 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolverSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/internal/PatternBasedResolverSpec.groovy @@ -1,6 +1,6 @@ // // ============================================================================ -// (C) Copyright Schalk W. Cronje 2013-2018 +// (C) Copyright Schalk W. Cronje 2013-2019 // // This software is licensed under the Apache License 2.0 // See http://www.apache.org/licenses/LICENSE-2.0 for license details From 225bfd72e1bdaa9468feda2434919e418dd8f764 Mon Sep 17 00:00:00 2001 From: Schalk Cronje Date: Wed, 8 May 2019 00:53:17 +0200 Subject: [PATCH 2/4] Allow arbitrary binaries to be cached (#41) --- CHANGELOG.adoc | 3 +- README.adoc | 55 ++++++++- build.gradle | 9 ++ gradle/integration-tests.gradle | 8 +- src/gradleTest/basicTest/build.gradle | 13 ++ .../multiProject/proj1/build.gradle | 5 +- src/gradleTest/multiProject/sync/build.gradle | 7 ++ ...fflineRepositorySyncIntegrationSpec.groovy | 31 ++++- .../gradle/ivypot/BinaryPotBasePlugin.groovy | 17 +++ .../ivypot/OfflineRepositoryPlugin.groovy | 1 + .../ivypot/OfflineRepositorySync.groovy | 104 ++++++++++++++-- .../DependencyHandlerExtension.groovy | 27 +++++ .../OfflineRepositoryExtension.groovy | 2 +- .../DefaultBinaryArtifactDependency.groovy | 51 ++++++++ .../internal/DefaultBinaryRepository.groovy | 37 ++++++ .../ivypot/remote/BinaryDependency.groovy | 9 ++ .../ivypot/remote/BinaryDownloader.groovy | 112 ++++++++++++++++++ .../remote/BinaryRepositoryDescriptor.groovy | 9 ++ .../gradle/ivypot/remote/Downloader.groovy | 37 ++++++ .../gradle/ivypot/remote/ExecutionData.groovy | 4 + .../ysb33r/gradle/ivypot/remote/IvyAnt.groovy | 35 +----- .../binary/BinaryArtifactDependency.java | 14 +++ .../binary/BinaryRepository.groovy | 14 +++ .../binary/NamedBinaryArtifactDependency.java | 6 + .../org.ysb33r.ivypot.binary.base.properties | 1 + .../ivypot/remote/BinaryDownloaderSpec.groovy | 84 +++++++++++++ .../ivypot/BinaryPotBasePluginSpec.groovy | 42 +++++++ .../OfflineRepositoryExtensionSpec.groovy | 1 + .../ivypot/OfflineRepositorySyncSpec.groovy | 22 ++++ .../internal/BinaryDependencySpec.groovy | 25 ++++ 30 files changed, 731 insertions(+), 54 deletions(-) create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy rename src/main/groovy/org/ysb33r/gradle/ivypot/{ => extensions}/OfflineRepositoryExtension.groovy (98%) create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy create mode 100644 src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java create mode 100644 src/main/resources/META-INF/gradle-plugins/org.ysb33r.ivypot.binary.base.properties create mode 100644 src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy create mode 100644 src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy create mode 100644 src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1465d90..266389c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -5,7 +5,8 @@ === Improvements -* {issue}40[#40] Run repository synchronisation process in separate JVM +* {issue}40[#40] Run repository synchronisation process in separate JVM. +* {issue}41[#41] Allow arbitrary binaries to be cached. === Breaking changes diff --git a/README.adoc b/README.adoc index b9bfa57..376acca 100644 --- a/README.adoc +++ b/README.adoc @@ -16,6 +16,7 @@ security processes to be applied to the binary files before committing them to a == Previous versions +* https://github.com/ysb33r/ivypot-gradle-plugin/tree/RELEASE_0_9_0[Release 0.9] * https://github.com/ysb33r/ivypot-gradle-plugin/tree/RELEASE_0_8_0[Release 0.8] * https://github.com/ysb33r/ivypot-gradle-plugin/tree/RELEASE_0_7_0[Release 0.7] * https://github.com/ysb33r/ivypot-gradle-plugin/tree/RELEASE_0_6_0[Release 0.6] @@ -191,6 +192,57 @@ Now you just have to run `./gradlew syncRemoteRepositories` or `./gradlew :sync: NOTE: Also see `src/gradleTest/multiProject` as an example of how this works. (That's actually the compability test we use for multi-projects). +== Caching arbitrary binaries + +As from 0.10 it is now possible to cache arbitrary binarie files with a path that typically matches that on the server. This is especially useful for people that need to perform tests where binaries are downloaded many times. + +=== Configuring binary repositories + +[source,groovy] +---- +syncRemoteRepositories { + binaryRepositories { + nodejs { // <1> + rootUri = 'https://nodejs.org/dist/' // <2> + artifactPattern = 'v[revision]/[module]-v[revision]-[classifier].[ext]' // <3> + } + } +} +---- +<1> The name of the repository. This will also be treated as the group/organisation names. +<2> The root URI of the remote repository. Only `file` and `http(s)` schemes are supported. +<3> A pattern similar to that if Ivy for resolving the artifact path below the root URI. + This is also used to calculate a relative path for storing the binary locally. + Note that `classifier` is supported. + +=== Specifying binaries locally + +If you have one project then the easiest is to specify the binaries within the +`syncRemoteRepositories` task using the `cachedBinaries.add` DSL keyword. + +[source,groovy] +---- +syncRemoteRepositories { + cacheBinaries.add 'nodejs:node:7.10.0:linux-64@tar.xz' +} +---- + +=== Specifying binaries in other projects + +It might be useful to rather specify binaries to be cached within the subproject where they are required and then let the syncRemoteRepositories task discover them. In order to achieve this apply a plugin which adds an extension to the `dependencies` block. + +[source,groovy,subs="+attributes"] +---- +plugins { + id 'org.ysb33r.binarypot.base' version '{revnumber}' // <1> +} + +dependencies { + cachedBinaries.add 'nodejs:node:7.10.0:linux-64@tar.xz' //<2> +} +---- +<1> Adds the `cachedBinaries` extenion to `dependencies` block. +<2> The same syntax is used, but the organisation/group name must match that of a binary repository as defined in `syncRemoteRepositories. == Adding buildscript dependencies @@ -219,9 +271,10 @@ which relies on finding a compiler jar in a certain named way failed when used w == Flat directories The `flatDir` repository supported by Gradle is not supported as it does not make sense. The purpose of this plugin is -to cache remote repositories into a useable local repository. If a user already has a `flatDir` it does not need be be +to cache remote repositories into a usable local repository. If a user already has a `flatDir` it does not need be be cached and if need be it can simply be copied. + == Limitations * The resolution process cannot be fine-tuned at present - not to the level at least which is described diff --git a/build.gradle b/build.gradle index 24ad954..bf1e3ff 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,7 @@ dependencies { exclude group: 'org.codehaus.groovy' } + compileOnly "org.apache.ivy:ivy:${ivyVersion}" compile 'org.ysb33r.gradle:grolifant:0.12.1' compile gradleApi() compile localGroovy() @@ -115,6 +116,10 @@ pluginBundle { id = 'org.ysb33r.ivypot' displayName = 'Plugin for maintaining an offline respository' } + binaryPotBasePlugin { + id = 'org.ysb33r.ivypot.binary.base' + displayName = 'Plugin that allows specification of arbitrary binaries to be cached by the ivypot plugin' + } } mavenCoordinates { @@ -149,3 +154,7 @@ task release { description 'Life-cycle task for releasing the plugin' dependsOn build, publishPlugins } + +pluginManager.withPlugin('idea') { + tasks.ideaModule.dependsOn processResources +} \ No newline at end of file diff --git a/gradle/integration-tests.gradle b/gradle/integration-tests.gradle index f2c66c1..eeed846 100644 --- a/gradle/integration-tests.gradle +++ b/gradle/integration-tests.gradle @@ -4,15 +4,11 @@ sourceSets { integrationTest { -// java.srcDir file('src/integTest/java') -// groovy.srcDir file('src/integTest/groovy') -// resources.srcDir file('src/integTest/resources') -// compileClasspath = sourceSets.main.output + configurations.integrationTestCompile -// runtimeClasspath = output + compileClasspath + configurations.integrationTestRuntime } remoteTest { - compileClasspath = sourceSets.main.output +configurations.remoteTestCompile + compileClasspath = sourceSets.main.output + configurations.remoteTestCompile + runtimeClasspath = output + compileClasspath + configurations.remoteTestRuntime } } diff --git a/src/gradleTest/basicTest/build.gradle b/src/gradleTest/basicTest/build.gradle index eaaeea1..23b19ed 100644 --- a/src/gradleTest/basicTest/build.gradle +++ b/src/gradleTest/basicTest/build.gradle @@ -6,6 +6,10 @@ configurations { github } +repositories { + jcenter() +} + dependencies { // Copying this plugin as it has a nice svnkit dependency set simple 'at.bxm.gradleplugins:gradle-svntools-plugin:1.1' @@ -22,6 +26,15 @@ syncRemoteRepositories { } } + binaryRepositories { + nodejs { + rootUri = 'https://nodejs.org/dist/' + artifactPattern = 'v[revision]/[module]-v[revision]-[classifier].[ext]' + } + } + + cachedBinaries.add 'nodejs:node:7.10.0:linux-x64@tar.xz' + configurations 'simple' } diff --git a/src/gradleTest/multiProject/proj1/build.gradle b/src/gradleTest/multiProject/proj1/build.gradle index cc63ec0..a6f358f 100644 --- a/src/gradleTest/multiProject/proj1/build.gradle +++ b/src/gradleTest/multiProject/proj1/build.gradle @@ -1,4 +1,7 @@ -apply plugin : 'java' +plugins { + id 'java' + id 'org.ysb33r.ivypot.binary.base' +} dependencies { compile 'org.tukaani:xz:1.6' diff --git a/src/gradleTest/multiProject/sync/build.gradle b/src/gradleTest/multiProject/sync/build.gradle index 438e169..d4b7630 100644 --- a/src/gradleTest/multiProject/sync/build.gradle +++ b/src/gradleTest/multiProject/sync/build.gradle @@ -8,6 +8,13 @@ syncRemoteRepositories { jcenter() } + binaryRepositories { + nodejs { + rootUri = 'https://nodejs.org/dist/' + artifactPattern = 'v[revision]/[module]-v[revision]-[classifier].[ext]' + } + } + addAllProjects() } diff --git a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy index 75c4594..fe03014 100644 --- a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy +++ b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy @@ -257,12 +257,11 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { build() then: - file_exists'rubygems/colorize/0.7.7/ivys/ivy.xml' - file_exists'rubygems/colorize/0.7.7/gems/colorize.gem' + file_exists 'rubygems/colorize/0.7.7/ivys/ivy.xml' + file_exists 'rubygems/colorize/0.7.7/gems/colorize.gem' } void 'Can we sync from Google?'() { - setup: writeBuildFile """ configurations { @@ -286,8 +285,30 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { build() then: - file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' - file_exists 'commons-io/commons-io/2.4/commons-io-2.4.jar' + file_exists 'com.android.support.constraint/constraint-layout/1.0.2/constraint-layout-1.0.2.aar' + file_exists 'com.android.support.constraint/constraint-layout-solver/1.0.2/constraint-layout-solver-1.0.2.jar' + } + + void 'Sync a remote binary that is defined in the task'() { + setup: + writeBuildFile """ + syncRemoteRepositories { + binaryRepositories { + nodejs { + rootUri = 'https://nodejs.org/dist/' + artifactPattern = 'v[revision]/[module]-v[revision]-[classifier].[ext]' + } + } + + cachedBinaries.add 'nodejs:node:7.10.0:linux-x64@tar.xz' + } + """ + + when: + build() + + then: + file_exists 'binaries/nodejs/v7.10.0/node-v7.10.0-linux-x64.tar.xz' } private boolean file_exists(String path) { diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy new file mode 100644 index 0000000..70e0d40 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy @@ -0,0 +1,17 @@ +package org.ysb33r.gradle.ivypot + +import groovy.transform.CompileStatic +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.ysb33r.gradle.ivypot.extensions.DependencyHandlerExtension + +@CompileStatic +class BinaryPotBasePlugin implements Plugin { + + public final static String EXTENSION_NAME = 'cachedBinaries' + @Override + void apply(Project project) { + ((ExtensionAware)project.dependencies).extensions.create(EXTENSION_NAME, DependencyHandlerExtension, project) + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy index ebcc3b1..594301c 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryPlugin.groovy @@ -18,6 +18,7 @@ import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.util.GradleVersion +import org.ysb33r.gradle.ivypot.extensions.OfflineRepositoryExtension /** * @author Schalk W. Cronjé diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy index 0ba2337..802b3a9 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy @@ -16,24 +16,34 @@ package org.ysb33r.gradle.ivypot import groovy.transform.CompileDynamic import groovy.transform.CompileStatic -import groovy.xml.MarkupBuilder +import groovy.transform.Internal +import org.gradle.api.Action import org.gradle.api.DefaultTask +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.artifacts.repositories.IvyArtifactRepository +import org.gradle.api.plugins.ExtensionAware import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.process.JavaExecSpec +import org.ysb33r.gradle.ivypot.extensions.DependencyHandlerExtension +import org.ysb33r.gradle.ivypot.extensions.OfflineRepositoryExtension import org.ysb33r.gradle.ivypot.internal.AntLogLevel +import org.ysb33r.gradle.ivypot.internal.DefaultBinaryRepository import org.ysb33r.gradle.ivypot.internal.IvyUtils +import org.ysb33r.gradle.ivypot.remote.BinaryDependency +import org.ysb33r.gradle.ivypot.remote.BinaryRepositoryDescriptor +import org.ysb33r.gradle.ivypot.remote.Downloader import org.ysb33r.gradle.ivypot.remote.ExecutionData -import org.ysb33r.gradle.ivypot.remote.IvyAnt import org.ysb33r.gradle.ivypot.remote.IvyDependency import org.ysb33r.gradle.ivypot.repositories.RepositoryHandler +import org.ysb33r.gradle.ivypot.repositories.binary.BinaryArtifactDependency +import org.ysb33r.gradle.ivypot.repositories.binary.BinaryRepository import org.ysb33r.grolifant.api.FileUtils import org.ysb33r.grolifant.api.StringUtils @@ -48,7 +58,7 @@ class OfflineRepositorySync extends DefaultTask { repositories = new RepositoryHandler(project) - inputs.property('project configurations', { OfflineRepositorySync ors -> + inputs.properties.put('project configurations', { OfflineRepositorySync ors -> Set configs = ors.getConfigurations() configs.collect { Configuration c -> c.dependencies.collect { Dependency d -> @@ -56,6 +66,20 @@ class OfflineRepositorySync extends DefaultTask { }.join(',') }.join('|') }.curry(this)) + + inputs.properties.put('cached binaries' , { OfflineRepositorySync ors -> + ors.binaries*.toString().join('') + }.curry(this)) + + binaryRepositories = project.container(BinaryRepository) { String repoName -> + DefaultBinaryRepository.create(repoName, null, null) + } + + binaryRepositories.addRule('create new binary repository') { t -> + binaryRepositories.create(t) + } + + extBinaries = extensions.create('cachedBinaries', DependencyHandlerExtension, project) } /** The pattern that will be used to write artifacts into the target repository @@ -81,6 +105,25 @@ class OfflineRepositorySync extends DefaultTask { project.file(this.repoRoot).absoluteFile } + @OutputDirectory + File getBinaryRepoRoot() { + project.file(this.binaryRepoRoot).absoluteFile + } + + NamedDomainObjectContainer getBinaryRepositories() { + this.binaryRepositories + } + + void binaryRepositories(@DelegatesTo(NamedDomainObjectContainer) Closure cfg) { + cfg.delegate = this.binaryRepositories + cfg.resolveStrategy = Closure.DELEGATE_ONLY + cfg() + } + + void binaryRepositories(Action> action) { + action.execute(this.binaryRepositories) + } + void repoRoot(Object repo) { this.repoRoot = repo } @@ -243,6 +286,22 @@ class OfflineRepositorySync extends DefaultTask { repoRoot ? "${repoRoot}/${repoArtifactPattern}" : null } + @Internal + List getBinaries() { + List handlers = [extBinaries] + handlers.addAll( + projectConfigurations.keySet().collect { Project p -> + (DependencyHandlerExtension) ( + (ExtensionAware) p.dependencies).extensions.findByName(BinaryPotBasePlugin.EXTENSION_NAME + ) + }.findAll { it != null } + ) + + handlers.collectMany { DependencyHandlerExtension dhext -> + dhext.asMap.values() + } as List + } + @TaskAction void exec() { File executionDataFile = new File(project.buildDir, "tmp/${toSafeFileName(name)}/execution.data") @@ -255,24 +314,46 @@ class OfflineRepositorySync extends DefaultTask { ExecutionData executionData = new ExecutionData() executionData.with { overwrite = project.gradle.startParameter.isRefreshDependencies() - ivySettings = ivySettingsFile.canonicalFile - ivyRepoRoot = getRepoRoot().canonicalFile + ivySettings = ivySettingsFile + ivyRepoRoot = getRepoRoot() logLevel = AntLogLevel.fromGradleLogLevel(logging.level) dependencies.addAll(ivyDeps) } + executionData.binaryRepoRoot = getBinaryRepoRoot() + executionData.binaryRepositories.putAll(this.binaryRepositories.collectEntries { repo -> + [repo.name, new BinaryRepositoryDescriptor(rootUri: repo.rootUri, artifactPattern: repo.artifactPattern)] + }) + + executionData.binaries.addAll(binaries.collect { + binaryDependency(it) + }) + ExecutionData.serializeData(executionDataFile, executionData) Configuration ivyRuntime = offlineRepositorySync.configuration - File ivyAnt = resolveClassLocation(IvyAnt).file + File entryPointClasspath = resolveClassLocation(Downloader).file project.javaexec { JavaExecSpec jes -> jes.with { - main = IvyAnt.canonicalName - classpath ivyRuntime, ivyAnt + main = Downloader.canonicalName + classpath ivyRuntime, entryPointClasspath args executionDataFile.absolutePath } } } + private BinaryDependency binaryDependency(BinaryArtifactDependency dep) { + new BinaryDependency( + organisation: dep.group, + module: dep.module, + revision: dep.revision, + transitive: false, + typeFilter: dep.type, + confFilter: '*', + classifier: dep.classifier, + extension: dep.extension + ) + } + private IvyDependency ivyDependency(Dependency dep) { new IvyDependency( organisation: dep.group, @@ -291,14 +372,13 @@ class OfflineRepositorySync extends DefaultTask { private File createIvySettingsFile() { File target = new File(temporaryDir, 'ivysettings.xml') IvyUtils.writeSettingsFile( - target, + target, repositories, getRepoRoot(), new File(project.buildDir, "tmp/ivypot/${FileUtils.toSafeFileName(name)}"), repoIvyPattern, repoArtifactPattern, repositoryCredentials - ) target } @@ -348,8 +428,12 @@ class OfflineRepositorySync extends DefaultTask { } } + final NamedDomainObjectContainer binaryRepositories + private Object repoRoot + private Object binaryRepoRoot = { new File(getRepoRoot(), 'binaries') } private final RepositoryHandler repositories + private final DependencyHandlerExtension extBinaries private final List configurations = [] private final Map> projectConfigurations = [:] } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy new file mode 100644 index 0000000..74cb18f --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy @@ -0,0 +1,27 @@ +package org.ysb33r.gradle.ivypot.extensions + +import groovy.transform.CompileStatic +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.ysb33r.gradle.ivypot.internal.DefaultBinaryArtifactDependency +import org.ysb33r.gradle.ivypot.repositories.binary.NamedBinaryArtifactDependency + +@CompileStatic +class DependencyHandlerExtension implements NamedDomainObjectContainer { + + DependencyHandlerExtension(Project project) { + Closure instantiator = { Project p, String name -> + DefaultBinaryArtifactDependency.create(p, name) + }.curry(project) + + delegate = project.container(NamedBinaryArtifactDependency, instantiator) + delegate.addRule('new dependency', instantiator) + } + + void add(String notation) { + this.delegate.create(notation) + } + + @Delegate + private final NamedDomainObjectContainer delegate +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy similarity index 98% rename from src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy rename to src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy index 90dcada..a0782ef 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtension.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy @@ -12,7 +12,7 @@ // ============================================================================ // -package org.ysb33r.gradle.ivypot +package org.ysb33r.gradle.ivypot.extensions import groovy.transform.CompileStatic import org.gradle.api.Project diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy new file mode 100644 index 0000000..c59fa82 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy @@ -0,0 +1,51 @@ +package org.ysb33r.gradle.ivypot.internal + +import groovy.transform.CompileStatic +import org.gradle.api.Project +import org.gradle.api.artifacts.DependencyArtifact +import org.gradle.api.artifacts.ModuleDependency +import org.ysb33r.gradle.ivypot.repositories.binary.NamedBinaryArtifactDependency + +@CompileStatic +class DefaultBinaryArtifactDependency implements NamedBinaryArtifactDependency { + + private final DependencyArtifact artifactExtras + final String group + final String revision + final String module + + String getClassifier() { + this.artifactExtras?.classifier + } + + String getExtension() { + this.artifactExtras?.extension + } + + String getType() { + this.artifactExtras?.type + } + + @Override + String getName() { + "${group ?: ''}:${module}:${revision ?: ''}${classifier ? ':' + classifier : ''}" + + "${extension ? ':' + extension : ''}${type ? '/type=' + type : ''}" + } + + @Override + String toString() { + name + } + + private DefaultBinaryArtifactDependency(String organisation, String module, String version, DependencyArtifact dep) { + this.artifactExtras = dep + this.group = organisation + this.revision = version + this.module = module + } + + static DefaultBinaryArtifactDependency create(Project project, String s) { + ModuleDependency dep = (ModuleDependency) project.dependencies.create(s) + new DefaultBinaryArtifactDependency(dep.group, dep.name, dep.version, dep.artifacts[0]) + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy new file mode 100644 index 0000000..19a773e --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy @@ -0,0 +1,37 @@ +package org.ysb33r.gradle.ivypot.internal + +import groovy.transform.CompileStatic +import org.ysb33r.gradle.ivypot.repositories.binary.BinaryRepository + +@CompileStatic +class DefaultBinaryRepository implements BinaryRepository { + + final String name + String artifactPattern + private URI rootUri + + static BinaryRepository create(final String name, String baseURI, final String artifactPattern) { + new DefaultBinaryRepository(name,baseURI?.toURI(),artifactPattern) + } + + @Override + URI getRootUri() { + this.rootUri + } + + @Override + void setRootUri(URI uri) { + this.rootUri = uri + } + + @Override + void setRootUri(String uri) { + this.rootUri = uri?.toURI() + } + + private DefaultBinaryRepository(final String name, final URI baseURI, final String artifactPattern) { + this.name = name + this.rootUri = baseURI + this.artifactPattern = artifactPattern + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy new file mode 100644 index 0000000..6e11a20 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy @@ -0,0 +1,9 @@ +package org.ysb33r.gradle.ivypot.remote + +import groovy.transform.CompileStatic + +@CompileStatic +class BinaryDependency extends IvyDependency { + String extension + String classifier +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy new file mode 100644 index 0000000..1f4250c --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy @@ -0,0 +1,112 @@ +package org.ysb33r.gradle.ivypot.remote + +import groovy.transform.CompileStatic +import org.apache.ivy.core.IvyPatternHelper + +import java.nio.file.Files + +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE +import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING + +@CompileStatic +class BinaryDownloader { + + BinaryDownloader( + File repoRoot, + Map binaryRepositories, + boolean logProgress + ) { + this.repoRoot = repoRoot + repositories.putAll(binaryRepositories) + this.logProgress = logProgress + } + + void resolve( + List binaries, + boolean overwrite + ) { + repoRoot.mkdirs() + List artifacts = downloadableArtifacts(binaries) + + artifacts.each { + if (overwrite || !it.destinationPath.exists()) { + if (logProgress) { + println "Retrieving ${it.downloadUri}" + } + switch (it.downloadUri.scheme) { + case 'file': + copyFile(new File(it.downloadUri), it.destinationPath) + break + case 'http': + case 'https': + downloadFile(it.downloadUri, it.destinationPath) + break + default: + throw new RuntimeException("${it.downloadUri} is not a supported scheme") + } + } + } + } + + List downloadableArtifacts(List binaries) { + binaries.collect { + BinaryRepositoryDescriptor repo = repositories[it.organisation] + + if (repo == null) { + throw new RuntimeException("Organisation '${it.organisation}' is not mapped to a repository") + } + + String relativePath = makeRelativePath(repo.artifactPattern, it) + + new Artifact( + downloadUri: repo.rootUri.resolve(relativePath), + destinationPath: new File(repoRoot, "${it.organisation}/${relativePath}") + ) + } + } + + private void copyFile(File from, File to) { + Files.copy(from.toPath(), to.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING) + } + + private void downloadFile(URI from, File to) { + to.parentFile.mkdirs() + File tmpFile = new File("${to}.tmp") + try { + from.toURL().withInputStream { strm -> + tmpFile.withOutputStream { output -> + output << strm + } + } + Files.move(tmpFile.toPath(), to.toPath(), ATOMIC_MOVE, REPLACE_EXISTING) + } catch( Exception e ) { + tmpFile.delete() + throw e + } + } + + static String makeRelativePath(String artifactPattern, BinaryDependency dep) { + IvyPatternHelper.substitute( + artifactPattern, + dep.organisation, + dep.module, + dep.revision, + dep.module, + dep.typeFilter == '*' ? dep.extension : dep.typeFilter, + dep.extension, + null, + null, + dep.classifier ? ['m:classifier': dep.classifier] : [:] + ) + } + + static class Artifact { + URI downloadUri + File destinationPath + } + + private final boolean logProgress = false + private final File repoRoot + private final Map repositories = [:] +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy new file mode 100644 index 0000000..4c51374 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy @@ -0,0 +1,9 @@ +package org.ysb33r.gradle.ivypot.remote + +import groovy.transform.CompileStatic + +@CompileStatic +class BinaryRepositoryDescriptor implements Serializable { + URI rootUri + String artifactPattern +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy new file mode 100644 index 0000000..a3b5e12 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy @@ -0,0 +1,37 @@ +package org.ysb33r.gradle.ivypot.remote + +import groovy.transform.CompileStatic +import org.apache.tools.ant.Project + +@CompileStatic +class Downloader { + static void main(String[] args) { + if (args.size() != 1) { + throw new RuntimeException('No serialised location specified') + } + + ExecutionData executionData = ExecutionData.deserializeData(new File(args[0])) + + if(!executionData.dependencies.empty) { + IvyAnt ivyAnt = new IvyAnt(executionData.ivySettings) + ivyAnt.logLevel = executionData.logLevel + ivyAnt.resolve( + executionData.ivyRepoRoot, + executionData.dependencies, + executionData.overwrite + ) + } + + if(!executionData.binaries.empty) { + BinaryDownloader binaries = new BinaryDownloader( + executionData.binaryRepoRoot, + executionData.binaryRepositories, + executionData.logLevel >= Project.MSG_INFO + ) + binaries.resolve( + executionData.binaries, + executionData.overwrite + ) + } + } +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy index cdd3753..ce1670d 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/ExecutionData.groovy @@ -23,7 +23,11 @@ class ExecutionData implements Serializable { int logLevel File ivySettings File ivyRepoRoot + File binaryRepoRoot + List dependencies = [] + Map binaryRepositories = [:] + List binaries = [] static void serializeData(File destination, ExecutionData data) { destination.parentFile.mkdirs() diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy index 3ca65cd..1d1cf46 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy @@ -19,6 +19,7 @@ import groovy.transform.CompileStatic import org.apache.tools.ant.BuildListener import org.apache.tools.ant.DefaultLogger import org.apache.tools.ant.Project +import org.gradle.internal.impldep.org.apache.ivy.core.IvyPatternHelper @CompileStatic class IvyAnt { @@ -27,21 +28,6 @@ class IvyAnt { public static final String RESOLVE_TASK = 'ivyResolve' public static final String RESOLVE_CLASS = 'org.apache.ivy.ant.IvyResolve' - static void main(String[] args) { - if (args.size() != 1) { - throw new RuntimeException('No serialised location specified') - } - - ExecutionData executionData = ExecutionData.deserializeData(new File(args[0])) - def ivyAnt = new IvyAnt(executionData.ivySettings) - ivyAnt.logLevel = executionData.logLevel - ivyAnt.resolve( - executionData.ivyRepoRoot, - executionData.dependencies, - executionData.overwrite - ) - } - IvyAnt(File ivySettings) { this.ivySettings = ivySettings ivyAnt = new AntBuilder() @@ -71,13 +57,6 @@ class IvyAnt { ivyAnt."${CONFIGURE_TASK}" file: ivySettings.absolutePath } - /** - * - * @param dep - * @param overwrite - * - * @sa {@link https://ant.apache.org/ivy/history/trunk/use/resolve.html} - */ @CompileDynamic private void ivyInstall(IvyDependency dep, boolean overwrite) { ivyAnt."${RESOLVE_TASK}"( @@ -91,14 +70,12 @@ class IvyAnt { @CompileDynamic private void configureAnt() { + ivyAnt.taskdef(name: CONFIGURE_TASK, classname: CONFIGURE_CLASS) + ivyAnt.taskdef(name: RESOLVE_TASK, classname: RESOLVE_CLASS) + } - ivyAnt.taskdef name: CONFIGURE_TASK, - classname: CONFIGURE_CLASS -// classpath: ivyJar - - ivyAnt.taskdef name: RESOLVE_TASK, - classname: RESOLVE_CLASS -// classpath: ivyJar + void test() { + //IvyPatternHelper.substitute() } private final AntBuilder ivyAnt diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java new file mode 100644 index 0000000..c52f8db --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java @@ -0,0 +1,14 @@ +package org.ysb33r.gradle.ivypot.repositories.binary; + +public interface BinaryArtifactDependency { + String getGroup(); + String getRevision(); + + String getModule(); + + String getClassifier(); + + String getExtension(); + + String getType(); +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy new file mode 100644 index 0000000..a4f8528 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy @@ -0,0 +1,14 @@ +package org.ysb33r.gradle.ivypot.repositories.binary + +import groovy.transform.CompileStatic +import org.gradle.api.Named + +@CompileStatic +interface BinaryRepository extends Named { + URI getRootUri() + void setRootUri(String uri) + void setRootUri(URI uri) + + String getArtifactPattern() + void setArtifactPattern(String pattern) +} diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java new file mode 100644 index 0000000..947e529 --- /dev/null +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java @@ -0,0 +1,6 @@ +package org.ysb33r.gradle.ivypot.repositories.binary; + +import org.gradle.api.Named; + +public interface NamedBinaryArtifactDependency extends Named, BinaryArtifactDependency { +} diff --git a/src/main/resources/META-INF/gradle-plugins/org.ysb33r.ivypot.binary.base.properties b/src/main/resources/META-INF/gradle-plugins/org.ysb33r.ivypot.binary.base.properties new file mode 100644 index 0000000..16a9bb7 --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/org.ysb33r.ivypot.binary.base.properties @@ -0,0 +1 @@ +implementation-class=org.ysb33r.gradle.ivypot.BinaryPotBasePlugin \ No newline at end of file diff --git a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy new file mode 100644 index 0000000..f227e4f --- /dev/null +++ b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy @@ -0,0 +1,84 @@ +package org.ysb33r.gradle.ivypot.remote + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.IgnoreIf +import spock.lang.Specification + + +class BinaryDownloaderSpec extends Specification { + + @Rule + TemporaryFolder testFolder + + void 'Convert dependency to relativePath with classifier'() { + when: + def dep = new BinaryDependency( + organisation: 'foo', + module: 'bar', + revision: '1.2.3', + transitive: false, + extension: 'tar', + classifier: 'bin' + ) + def pat = '[module]:[revision]:([classifier]:)[ext]' + + then: + BinaryDownloader.makeRelativePath(pat,dep) == 'bar:1.2.3:bin:tar' + } + + void 'Convert dependency to relativePath with optional classifier'() { + when: + def dep = new BinaryDependency( + organisation: 'foo', + module: 'bar', + revision: '1.2.3', + transitive: false, + extension: 'tar' + ) + def pat = '[module]:[revision]:([classifier]:)[ext]' + + then: + BinaryDownloader.makeRelativePath(pat,dep) == 'bar:1.2.3:tar' + } + + @IgnoreIf({ System.getProperty('OFFLINE') }) + void 'Can download a binary'() { + setup: + File repoRoot = testFolder.root + def repositories = [ nodejs: new BinaryRepositoryDescriptor( + rootUri: 'https://nodejs.org/dist/'.toURI(), + artifactPattern: 'v[revision]/[module]-v[revision]-[classifier].[ext]' + )] + def downloader = new BinaryDownloader(repoRoot,repositories, true) + def binaries = [new BinaryDependency( + organisation: 'nodejs', + module: 'node', + revision: '7.10.0', + classifier: 'linux-x64', + extension: 'tar.xz' + )] + def expectedFile = new File(repoRoot,'nodejs/v7.10.0/node-v7.10.0-linux-x64.tar.xz') + + when: + downloader.resolve(binaries, true) + + then: + expectedFile.exists() + + when: + long lastModified = expectedFile.lastModified() + downloader.resolve(binaries, false) + + then: + expectedFile.exists() + lastModified == expectedFile.lastModified() + + when: + downloader.resolve(binaries, true) + + then: + expectedFile.exists() + lastModified != expectedFile.lastModified() + } +} diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy new file mode 100644 index 0000000..46501a6 --- /dev/null +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy @@ -0,0 +1,42 @@ +package org.ysb33r.gradle.ivypot + +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import spock.lang.Specification + +class BinaryPotBasePluginSpec extends Specification { + + Project project = ProjectBuilder.builder().build() + + void setup() { + project.apply plugin: 'org.ysb33r.ivypot.binary.base' + } + + void 'Add an external dependency'() { + + when: + project.allprojects { + dependencies { + cachedBinaries.add 'group1:module:version:classifier@ext' + cachedBinaries.add 'group2:module:version:classifier' + cachedBinaries.add 'group3:module:version' + } + } + + def bins = project.dependencies.cachedBinaries.asMap.values() + + then: + bins.size() == 3 + bins[0].group == 'group1' + bins[0].module == 'module' + bins[0].revision == 'version' + bins[0].classifier == 'classifier' + bins[0].extension == 'ext' + bins[1].group == 'group2' + bins[1].classifier == 'classifier' + bins[1].extension == 'jar' + bins[2].group == 'group3' + bins[2].classifier == null + bins[2].extension == null + } +} diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy index 8dab3cd..3b409ee 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositoryExtensionSpec.groovy @@ -16,6 +16,7 @@ package org.ysb33r.gradle.ivypot import org.gradle.api.Project import org.gradle.testfixtures.ProjectBuilder +import org.ysb33r.gradle.ivypot.extensions.OfflineRepositoryExtension import spock.lang.Specification diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy index 6ee4cde..91bbf21 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy @@ -250,6 +250,28 @@ class OfflineRepositorySyncSpec extends Specification { thrown(CannotUseCurrentProjectException) } + void 'Adding a binary repository'() { + when: + project.allprojects { + syncTask { + binaryRepositories { + gradleDist { + rootUri = 'https://services.gradle.org/distributions/' + artifactPattern = 'gradle-[revision]-[type].[ext]' + } + } + } + } + + def binaryRepo = syncTask.binaryRepositories.getByName('gradleDist') + + then: + verifyAll { + binaryRepo.rootUri == 'https://services.gradle.org/distributions/'.toURI() + binaryRepo.artifactPattern == 'gradle-[revision]-[type].[ext]' + } + } + private getConfiguration(final Iterable configs, final String name) { configs.find { it.name == name diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy new file mode 100644 index 0000000..996435c --- /dev/null +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy @@ -0,0 +1,25 @@ +package org.ysb33r.gradle.ivypot.internal + +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import spock.lang.Specification + +class BinaryDependencySpec extends Specification { + + Project project = ProjectBuilder.builder().build() + + void 'Create a binary dependency directly'() { + when: + DefaultBinaryArtifactDependency dep = DefaultBinaryArtifactDependency.create(project, "gradleDist:gradle:4.5.1:bin@zip") + + then: + verifyAll { + dep.group == 'gradleDist' + dep.module == 'gradle' + dep.revision == '4.5.1' + dep.classifier == 'bin' + dep.type == 'zip' + dep.extension == 'zip' + } + } +} \ No newline at end of file From 4cf905165485cb27011f5dc6b177d8c7932f4954 Mon Sep 17 00:00:00 2001 From: Schalk Cronje Date: Wed, 8 May 2019 11:46:18 +0200 Subject: [PATCH 3/4] Resolve classifiers and explicit extensions in Ivy (#34) --- CHANGELOG.adoc | 1 + ...fflineRepositorySyncIntegrationSpec.groovy | 29 +++++++++++ .../ivypot/OfflineRepositorySync.groovy | 32 ++++++++---- .../ivypot/remote/BinaryDependency.groovy | 6 ++- .../ysb33r/gradle/ivypot/remote/IvyAnt.groovy | 52 +++++++++++++++---- .../gradle/ivypot/remote/IvyDependency.groovy | 6 +-- .../ivypot/remote/BinaryDownloaderSpec.groovy | 2 - .../gradle/ivypot/remote/IvyAntSpec.groovy | 36 +++++++++++-- 8 files changed, 132 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 266389c..4e38767 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -5,6 +5,7 @@ === Improvements +* {issue}34[#34] Resolve classifiers and extensions correctly. * {issue}40[#40] Run repository synchronisation process in separate JVM. * {issue}41[#41] Allow arbitrary binaries to be cached. diff --git a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy index fe03014..0264fb4 100644 --- a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy +++ b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy @@ -289,6 +289,7 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { file_exists 'com.android.support.constraint/constraint-layout-solver/1.0.2/constraint-layout-solver-1.0.2.jar' } + @Issue('https://github.com/ysb33r/ivypot-gradle-plugin/issues/41') void 'Sync a remote binary that is defined in the task'() { setup: writeBuildFile """ @@ -311,6 +312,34 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { file_exists 'binaries/nodejs/v7.10.0/node-v7.10.0-linux-x64.tar.xz' } + @Issue('https://github.com/ysb33r/ivypot-gradle-plugin/issues/34') + void 'Use a different extension'() { + setup: + writeBuildFile """ + configurations { + karaf + } + + // tag::example_with_explicit_extension[] + dependencies { + karaf 'org.apache.karaf:apache-karaf:4.2.2@zip' + } + + syncRemoteRepositories { + repositories { + mavenCentral() + } + } + // end::example_with_explicit_extension[] + """ + + when: + build() + + then: + file_exists 'com.android.support.constraint/constraint-layout/1.0.2/constraint-layout-1.0.2.aar' + } + private boolean file_exists(String path) { new File(repoDir, path).exists() } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy index 802b3a9..8af1d7f 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy @@ -346,23 +346,35 @@ class OfflineRepositorySync extends DefaultTask { organisation: dep.group, module: dep.module, revision: dep.revision, - transitive: false, typeFilter: dep.type, - confFilter: '*', classifier: dep.classifier, extension: dep.extension ) } private IvyDependency ivyDependency(Dependency dep) { - new IvyDependency( - organisation: dep.group, - module: dep.name, - revision: dep.version, - transitive: dep instanceof ModuleDependency ? ((ModuleDependency) dep).transitive : true, - typeFilter: '*', - confFilter: '*' - ) + if(dep instanceof ModuleDependency) { + ModuleDependency modDep = (ModuleDependency)dep + new IvyDependency( + organisation: dep.group, + module: dep.name, + revision: dep.version, + transitive: modDep.transitive, + typeFilter: modDep.artifacts?.getAt(0)?.type ?: '*', + confFilter: '*', + classifier: modDep.artifacts?.getAt(0)?.classifier, + extension: modDep.artifacts?.getAt(0)?.extension + ) + } else { + new IvyDependency( + organisation: dep.group, + module: dep.name, + revision: dep.version, + transitive: true, + typeFilter: '*', + confFilter: '*' + ) + } } private OfflineRepositoryExtension getOfflineRepositorySync() { diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy index 6e11a20..88026c5 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy @@ -3,7 +3,11 @@ package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileStatic @CompileStatic -class BinaryDependency extends IvyDependency { +class BinaryDependency implements Serializable { + String organisation + String module + String revision String extension String classifier + String typeFilter } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy index 1d1cf46..5bba106 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyAnt.groovy @@ -16,10 +16,10 @@ package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder import org.apache.tools.ant.BuildListener import org.apache.tools.ant.DefaultLogger import org.apache.tools.ant.Project -import org.gradle.internal.impldep.org.apache.ivy.core.IvyPatternHelper @CompileStatic class IvyAnt { @@ -48,7 +48,7 @@ class IvyAnt { void resolve(File repoRoot, List deps, boolean overwrite) { repoRoot.mkdirs() deps.each { - ivyInstall(it, overwrite) + ivyInstall(it, overwrite, repoRoot) } } @@ -58,14 +58,46 @@ class IvyAnt { } @CompileDynamic - private void ivyInstall(IvyDependency dep, boolean overwrite) { - ivyAnt."${RESOLVE_TASK}"( - inline: true, - organisation: dep.organisation, module: dep.module, revision: dep.revision, - transitive: dep.transitive, - type: dep.typeFilter, - conf: dep.confFilter - ) + private void ivyInstall(IvyDependency dep, boolean overwrite, File repoRoot) { + + if (dep.extension || dep.classifier) { + File ivyFile = new File(repoRoot,"ivy-${dep.module}.xml") + Map artifactAttributes = [name: dep.module, type: dep.typeFilter] + if (dep.extension) { + artifactAttributes.'ext' = dep.extension + } + if (dep.classifier) { + artifactAttributes.'e:classifier' = dep.classifier + } + def writer = new StringWriter() + new MarkupBuilder(writer).'ivy-module'(version: '2.0', 'xmlns:e': 'http://ant.apache.org/ivy/extra') { + info( organisation:'ignore', module:'ignore') + dependencies { + dependency( + org: dep.organisation, + name: dep.module, + rev: dep.revision, + transitive: dep.transitive, + conf: dep.confFilter + ) { + artifact(artifactAttributes) + } + } + } + ivyFile.text = writer.toString() + ivyFile.deleteOnExit() + ivyAnt."${RESOLVE_TASK}"( + file: ivyFile.absolutePath + ) + } else { + ivyAnt."${RESOLVE_TASK}"( + inline: true, + organisation: dep.organisation, module: dep.module, revision: dep.revision, + transitive: dep.transitive, + type: dep.typeFilter, + conf: dep.confFilter + ) + } } @CompileDynamic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy index 8393764..5bc004d 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/IvyDependency.groovy @@ -17,11 +17,7 @@ package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileStatic @CompileStatic -class IvyDependency implements Serializable { - String organisation - String module - String revision +class IvyDependency extends BinaryDependency implements Serializable { boolean transitive - String typeFilter String confFilter } diff --git a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy index f227e4f..69ed3a6 100644 --- a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy +++ b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy @@ -17,7 +17,6 @@ class BinaryDownloaderSpec extends Specification { organisation: 'foo', module: 'bar', revision: '1.2.3', - transitive: false, extension: 'tar', classifier: 'bin' ) @@ -33,7 +32,6 @@ class BinaryDownloaderSpec extends Specification { organisation: 'foo', module: 'bar', revision: '1.2.3', - transitive: false, extension: 'tar' ) def pat = '[module]:[revision]:([classifier]:)[ext]' diff --git a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy index 613bb9c..953baf9 100644 --- a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy +++ b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/IvyAntSpec.groovy @@ -54,7 +54,7 @@ class IvyAntSpec extends Specification { void 'Can resolve an artifact'() { when: - def dep = new IvyDependency( + def dep1 = new IvyDependency( organisation: 'commons-io', module: 'commons-io', revision: '2.4', @@ -63,15 +63,42 @@ class IvyAntSpec extends Specification { confFilter: '*' ) - new IvyAnt(ivySettings).resolve(repoDir, [dep], true) + def dep2 = new IvyDependency( + organisation: 'org.apache.karaf', + module: 'apache-karaf', + revision: '4.2.2', + extension: 'zip', + transitive: true, + typeFilter: '*', + confFilter: '*' + ) + + def dep3 = new IvyDependency( + organisation: 'org.ysb33r.gradle', + module: 'grolifant', + revision: '0.12.1', + classifier: 'sources', + transitive: false, + extension: 'jar', + typeFilter: '*', + confFilter: '*' + ) + + new IvyAnt(ivySettings).resolve(repoDir, [dep3, dep2, dep1], true) then: verifyAll { - new File(repoDir, 'commons-io/commons-io/2.4/ivy-2.4.xml').exists() - new File(repoDir, 'commons-io/commons-io/2.4/commons-io-2.4.jar').exists() + file_exists 'commons-io/commons-io/2.4/ivy-2.4.xml' + file_exists 'commons-io/commons-io/2.4/commons-io-2.4.jar' + file_exists 'org.apache.karaf/apache-karaf/4.2.2/apache-karaf-4.2.2.zip' + file_exists 'org.ysb33r.gradle/grolifant/0.12.1/grolifant-0.12.1-sources.jar' } } + private boolean file_exists(String path) { + new File(repoDir, path).exists() + } + private void writeIvySettings() { ivySettings.withWriter { writer -> new MarkupBuilder(writer).ivysettings { @@ -85,6 +112,7 @@ class IvyAntSpec extends Specification { resolvers { chain(name: 'foobar', returnFirst: true) { ibiblio(name: 'MavenRepo', m2compatible: true) + ibiblio(name: 'BintrayJCenter', m2compatible: true, root: 'https://jcenter.bintray.com/') } } } From 30ddd3e5aaa2e563450a26bf0587787e8cf6d6e6 Mon Sep 17 00:00:00 2001 From: Schalk Cronje Date: Wed, 8 May 2019 16:15:55 +0200 Subject: [PATCH 4/4] Handle classifiers, extensions and realm-based authentication (#34,#29) --- CHANGELOG.adoc | 6 +++++ README.adoc | 2 +- build.gradle | 12 ++++++++- ...fflineRepositorySyncIntegrationSpec.groovy | 2 +- .../gradle/ivypot/BinaryPotBasePlugin.groovy | 14 ++++++++++ .../ivypot/OfflineRepositorySync.groovy | 26 ++++++++++++------- .../DependencyHandlerExtension.groovy | 14 ++++++++++ .../OfflineRepositoryExtension.groovy | 1 + .../DefaultBinaryArtifactDependency.groovy | 14 ++++++++++ .../internal/DefaultBinaryRepository.groovy | 14 ++++++++++ .../gradle/ivypot/internal/IvyUtils.groovy | 1 - .../ivypot/remote/BinaryDependency.groovy | 14 ++++++++++ .../ivypot/remote/BinaryDownloader.groovy | 14 ++++++++++ .../remote/BinaryRepositoryDescriptor.groovy | 14 ++++++++++ .../gradle/ivypot/remote/Downloader.groovy | 14 ++++++++++ .../binary/BinaryArtifactDependency.java | 13 ++++++++++ .../binary/BinaryRepository.groovy | 14 ++++++++++ .../binary/NamedBinaryArtifactDependency.java | 13 ++++++++++ .../ivypot/remote/BinaryDownloaderSpec.groovy | 17 +++++++++++- .../ivypot/BinaryPotBasePluginSpec.groovy | 14 ++++++++++ .../ivypot/OfflineRepositorySyncSpec.groovy | 2 ++ .../internal/BinaryDependencySpec.groovy | 14 ++++++++++ 22 files changed, 235 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4e38767..eea5769 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,10 +1,12 @@ = CHANGELOG :issue: link:https://github.com/ysb33r/ivypot-gradle-plugin/issues/ +:contributor: link:https://github.com/ == 0.10 - Roadmap === Improvements +* {issue}29[#29] Added realm parameter for credentials (Thank you Jakob Hendeß). * {issue}34[#34] Resolve classifiers and extensions correctly. * {issue}40[#40] Run repository synchronisation process in separate JVM. * {issue}41[#41] Allow arbitrary binaries to be cached. @@ -13,6 +15,10 @@ * Not binary compatible with 0.9. +=== Contributors + +* {contributor}jhendess[Jakob Hendeß] + == 0.9 === Compatibility diff --git a/README.adoc b/README.adoc index 376acca..5011014 100644 --- a/README.adoc +++ b/README.adoc @@ -194,7 +194,7 @@ NOTE: Also see `src/gradleTest/multiProject` as an example of how this works. (T == Caching arbitrary binaries -As from 0.10 it is now possible to cache arbitrary binarie files with a path that typically matches that on the server. This is especially useful for people that need to perform tests where binaries are downloaded many times. +As from 0.10 it is now possible to cache arbitrary binary files with a path that typically matches that on the server. This is especially useful for people that need to perform tests where binaries are downloaded many times. === Configuring binary repositories diff --git a/build.gradle b/build.gradle index bf1e3ff..9ef9898 100644 --- a/build.gradle +++ b/build.gradle @@ -101,7 +101,15 @@ license { groovy = 'DOUBLESLASH_STYLE' } ext.year = '2013-2019' - excludes(['**/*.ad', '**/*.asciidoc', '**/*.adoc', '**/*.md', '**/*.properties', '**/*CompatibilitySpec.groovy']) + excludes([ + '**/*.ad', + '**/*.asciidoc', + '**/*.adoc', + '**/*.md', + '**/*.properties', + '**/*.dsl.groovySpec.groovy', + '**/*.dsl.kotlinSpec.groovy', + ]) } pluginBundle { @@ -147,6 +155,8 @@ gradleTest { if (OS.windows) { gradleArguments '-g', file("${buildDir}/gradleTest/userHome").absolutePath.replaceAll(~$/\\/$, '/') } + + mustRunAfter test, integrationTest, remoteTest } task release { diff --git a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy index 0264fb4..05beb57 100644 --- a/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy +++ b/src/integrationTest/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncIntegrationSpec.groovy @@ -337,7 +337,7 @@ class OfflineRepositorySyncIntegrationSpec extends Specification { build() then: - file_exists 'com.android.support.constraint/constraint-layout/1.0.2/constraint-layout-1.0.2.aar' + file_exists 'org.apache.karaf/apache-karaf/4.2.2/apache-karaf-4.2.2.zip' } private boolean file_exists(String path) { diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy index 70e0d40..384e1fe 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePlugin.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy index 8af1d7f..084d8e1 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy @@ -67,7 +67,7 @@ class OfflineRepositorySync extends DefaultTask { }.join('|') }.curry(this)) - inputs.properties.put('cached binaries' , { OfflineRepositorySync ors -> + inputs.properties.put('cached binaries', { OfflineRepositorySync ors -> ors.binaries*.toString().join('') }.curry(this)) @@ -110,6 +110,7 @@ class OfflineRepositorySync extends DefaultTask { project.file(this.binaryRepoRoot).absoluteFile } + @Internal NamedDomainObjectContainer getBinaryRepositories() { this.binaryRepositories } @@ -137,6 +138,7 @@ class OfflineRepositorySync extends DefaultTask { * @return A project configuration container with all of the named configurations. Does not * return the {@code buildscript} configuration in here. The latter is made available directly to */ + @Internal Set getConfigurations() { Set configurationSet = [] projectConfigurations.collect { Project p, List configs -> @@ -253,6 +255,7 @@ class OfflineRepositorySync extends DefaultTask { * * @return A repository handler that Gradle users should be accustomed to. */ + @Internal RepositoryHandler getRepositories() { this.repositories } @@ -282,6 +285,7 @@ class OfflineRepositorySync extends DefaultTask { * * @return Artifact pattern or null in case repoRoot has not been set. */ + @Internal String getArtifactPattern() { repoRoot ? "${repoRoot}/${repoArtifactPattern}" : null } @@ -290,11 +294,11 @@ class OfflineRepositorySync extends DefaultTask { List getBinaries() { List handlers = [extBinaries] handlers.addAll( - projectConfigurations.keySet().collect { Project p -> - (DependencyHandlerExtension) ( - (ExtensionAware) p.dependencies).extensions.findByName(BinaryPotBasePlugin.EXTENSION_NAME - ) - }.findAll { it != null } + projectConfigurations.keySet().collect { Project p -> + (DependencyHandlerExtension) ( + (ExtensionAware) p.dependencies).extensions.findByName(BinaryPotBasePlugin.EXTENSION_NAME + ) + }.findAll { it != null } ) handlers.collectMany { DependencyHandlerExtension dhext -> @@ -353,8 +357,8 @@ class OfflineRepositorySync extends DefaultTask { } private IvyDependency ivyDependency(Dependency dep) { - if(dep instanceof ModuleDependency) { - ModuleDependency modDep = (ModuleDependency)dep + if (dep instanceof ModuleDependency) { + ModuleDependency modDep = (ModuleDependency) dep new IvyDependency( organisation: dep.group, module: dep.name, @@ -400,11 +404,15 @@ class OfflineRepositorySync extends DefaultTask { this.repositories.findAll { repo -> repo.metaClass.respondsTo(repo, 'getRepositoryCredentials') && repo.credentials?.username && repo.credentials?.password }.collect { repo -> - [ + def ret = [ host : repo.url.host, username: repo.credentials.username, password: repo.credentials.password ] as Map + if (repo.credentials.realm) { + ret['realm'] = it.credentials.realm + } + ret } } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy index 74cb18f..1ed768d 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/DependencyHandlerExtension.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.extensions import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy index a0782ef..53babd1 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/extensions/OfflineRepositoryExtension.groovy @@ -47,6 +47,7 @@ class OfflineRepositoryExtension { Configuration getConfiguration() { def deps = [ createDependency("${GROOVY_GROUP}:groovy-ant:${groovyVersion}"), + createDependency("${GROOVY_GROUP}:groovy-xml:${groovyVersion}"), createDependency("${IVY_GROUP}:ivy:${ivyVersion}") ] diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy index c59fa82..336b54c 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryArtifactDependency.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.internal import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy index 19a773e..cd18677 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/DefaultBinaryRepository.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.internal import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy index a4862af..be413cb 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/IvyUtils.groovy @@ -48,7 +48,6 @@ class IvyUtils { repositoryCredentials.each { repo -> credentials(repo) } - resolvers { chain(name: REMOTECHAINNAME, returnFirst: true) { repositories.each { repo -> diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy index 88026c5..4041854 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDependency.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy index 1f4250c..79a2639 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloader.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy index 4c51374..17752f3 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/BinaryRepositoryDescriptor.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy index a3b5e12..67bbaa1 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/remote/Downloader.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.remote import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java index c52f8db..2dc48ea 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryArtifactDependency.java @@ -1,3 +1,16 @@ +/** + * ============================================================================ + * (C) Copyright Schalk W. Cronje 2013-2019 + * + * This software is licensed under the Apache License 2.0 + * See http://www.apache.org/licenses/LICENSE-2.0 for license details + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + * + * ============================================================================ + */ package org.ysb33r.gradle.ivypot.repositories.binary; public interface BinaryArtifactDependency { diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy index a4f8528..11ea601 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/BinaryRepository.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.repositories.binary import groovy.transform.CompileStatic diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java index 947e529..4367b6c 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/repositories/binary/NamedBinaryArtifactDependency.java @@ -1,3 +1,16 @@ +/** + * ============================================================================ + * (C) Copyright Schalk W. Cronje 2013-2019 + * + * This software is licensed under the Apache License 2.0 + * See http://www.apache.org/licenses/LICENSE-2.0 for license details + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + * + * ============================================================================ + */ package org.ysb33r.gradle.ivypot.repositories.binary; import org.gradle.api.Named; diff --git a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy index 69ed3a6..65f4846 100644 --- a/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy +++ b/src/remoteTest/groovy/org/ysb33r/gradle/ivypot/remote/BinaryDownloaderSpec.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.remote import org.junit.Rule @@ -77,6 +91,7 @@ class BinaryDownloaderSpec extends Specification { then: expectedFile.exists() - lastModified != expectedFile.lastModified() + // The Travis CI environment does not deal with lastModified correctly + lastModified != expectedFile.lastModified() || System.getenv('TRAVIS') } } diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy index 46501a6..92cfd77 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/BinaryPotBasePluginSpec.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot import org.gradle.api.Project diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy index 91bbf21..8317c20 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy @@ -83,6 +83,7 @@ class OfflineRepositorySyncSpec extends Specification { credentials { username 'the' password 'pig' + realm 'sty' } } @@ -159,6 +160,7 @@ class OfflineRepositorySyncSpec extends Specification { """ ivyIvy.credentials.username == 'the' ivyIvy.credentials.password == 'pig' + ivyIvy.credentials.realm == 'sty' and: 'ivy with pattern layout loaded' ivyPattern.resolverXml() == ''' diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy index 996435c..ccb6b46 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/internal/BinaryDependencySpec.groovy @@ -1,3 +1,17 @@ +// +// ============================================================================ +// (C) Copyright Schalk W. Cronje 2013-2019 +// +// This software is licensed under the Apache License 2.0 +// See http://www.apache.org/licenses/LICENSE-2.0 for license details +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and limitations under the License. +// +// ============================================================================ +// + package org.ysb33r.gradle.ivypot.internal import org.gradle.api.Project