From b0c880edbed628ce50ea5ee26e939897c6fb3cf7 Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Mon, 4 Dec 2017 11:37:33 +0100 Subject: [PATCH] Fix #12: Add support for auto-generated DSL and extensions Add support for the auto-generated DSL [1] and Job DSL extensions [2]. This is implemented using the Jenkins test-harness to execute the Job DSL scripts in a temporary Jenkins instance. This way the discovery of plugin extension points works the same way as it does in a Job DSL seed job. The list of plugins can be configured in the jenkinsPlugins configuration in Gradle. Also change the next version to 3.0.0 because this is a major change. [1] https://github.com/jenkinsci/job-dsl-plugin/wiki/Automatically-Generated-DSL [2] https://github.com/jenkinsci/job-dsl-plugin/wiki/Extending-the-DSL --- CHANGELOG.md | 5 +- plugin/build.gradle | 16 ++++ plugin/gradle.properties | 2 +- .../plugins/jobdsl/JobDslPluginTest.groovy | 28 ++++++ .../jobdsl/tasks/GenerateXmlTest.groovy | 94 +++++++++++++++---- .../jobdsl/tasks/UpdateJenkinsTest.groovy | 81 +++++++++++++++- .../build-with-generated-dsl.gradle | 14 +++ .../build-with-jobdsl-extension.gradle | 14 +++ .../resources/generateXml/build.gradle | 2 + .../resources/generateXml/empty-list-view.xml | 19 ++-- .../resources/generateXml/filter-jobs.groovy | 1 + .../resources/generateXml/filter-views.groovy | 1 + .../generateXml/job-in-filtered-folder.groovy | 2 - .../generateXml/job-with-generated-dsl.groovy | 42 +++++++++ .../generateXml/job-with-generated-dsl.xml | 50 ++++++++++ .../job-with-job-dsl-extension.groovy | 15 +++ .../job-with-job-dsl-extension.xml | 30 ++++++ .../build-with-generated-dsl.gradle | 14 +++ .../build-with-jobdsl-extension.gradle | 14 +++ .../job-with-generated-dsl.groovy | 42 +++++++++ .../updateJenkins/job-with-generated-dsl.xml | 50 ++++++++++ .../job-with-job-dsl-extension.groovy | 15 +++ .../job-with-job-dsl-extension.xml | 30 ++++++ .../FilteringJenkinsJobManagement.groovy | 31 ++++++ .../FilteringMemoryJobManagement.groovy | 30 ------ .../gradle/plugins/jobdsl/JobDslPlugin.groovy | 64 +++++++++++-- .../plugins/jobdsl/RestJobManagement.groovy | 20 +++- .../jobdsl/tasks/AbstractDslTask.groovy | 10 +- .../tasks/runners/AbstractTaskRunner.groovy | 75 +++++++++++---- .../tasks/runners/GenerateXmlRunner.groovy | 70 ++++++++++++-- .../tasks/runners/UpdateJenkinsRunner.groovy | 11 ++- 31 files changed, 790 insertions(+), 102 deletions(-) create mode 100644 plugin/src/functionalTest/resources/generateXml/build-with-generated-dsl.gradle create mode 100644 plugin/src/functionalTest/resources/generateXml/build-with-jobdsl-extension.gradle delete mode 100644 plugin/src/functionalTest/resources/generateXml/job-in-filtered-folder.groovy create mode 100644 plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.groovy create mode 100644 plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.xml create mode 100644 plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.groovy create mode 100644 plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.xml create mode 100644 plugin/src/functionalTest/resources/updateJenkins/build-with-generated-dsl.gradle create mode 100644 plugin/src/functionalTest/resources/updateJenkins/build-with-jobdsl-extension.gradle create mode 100644 plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.groovy create mode 100644 plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.xml create mode 100644 plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.groovy create mode 100644 plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.xml create mode 100644 plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringJenkinsJobManagement.groovy delete mode 100644 plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringMemoryJobManagement.groovy diff --git a/CHANGELOG.md b/CHANGELOG.md index 05302ee..26eb48b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## 2.2.0 (under development) +## 3.0.0 (under development) + +- Add support for auto-generated DSL and Job DSL extensions by executing the Job DSL scripts in a local Jenkins + instance. ## 2.1.1 (2017-12-04) diff --git a/plugin/build.gradle b/plugin/build.gradle index c4d3875..86e8381 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -54,6 +54,7 @@ dependencies { compile('org.jenkins-ci.plugins:job-dsl-core:1.66') { exclude(module: 'groovy-all') } + compile 'org.jenkins-ci.plugins:job-dsl:1.66@jar' compile 'org.jenkins-ci:version-number:1.4' compile('org.codehaus.groovy.modules.http-builder:http-builder:0.7.2') { @@ -61,6 +62,20 @@ dependencies { exclude(module: 'xercesImpl') } + // Need to exclude XML parsers: https://issues.jenkins-ci.org/browse/JENKINS-35638 + compile('org.jenkins-ci.main:jenkins-test-harness:2.32') { + exclude(module: 'org-netbeans-insane') + exclude(module: 'serializer') + exclude(module: 'xalan') + exclude(module: 'xercesImpl') + } + + compile('org.jenkins-ci.main:jenkins-war:2.92') { + exclude(module: 'groovy-all') + exclude(module: 'slf4j-jdk14') + exclude(module: 'xalan') + } + testCompile('org.spockframework:spock-core:1.1-groovy-2.4') { exclude(module: 'groovy-all') } @@ -74,6 +89,7 @@ dependencies { exclude(module: 'slf4j-jdk14') } + // Need to exclude transitive dependency because of: https://github.com/gradle/gradle/issues/3666 functionalTestCompile('org.jenkins-ci.main:jenkins-test-harness:2.32') { exclude(module: 'org-netbeans-insane') } diff --git a/plugin/gradle.properties b/plugin/gradle.properties index 4c25375..e098874 100644 --- a/plugin/gradle.properties +++ b/plugin/gradle.properties @@ -1,6 +1,6 @@ # Maven configuration maven.groupId=com.here.gradle.plugins maven.artifactId=gradle-jenkins-jobdsl-plugin -maven.version=2.2.0-SNAPSHOT +maven.version=3.0.0-SNAPSHOT # Set this flag to true for release builds. maven.release=false diff --git a/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/JobDslPluginTest.groovy b/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/JobDslPluginTest.groovy index d9c42ab..8b00f46 100644 --- a/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/JobDslPluginTest.groovy +++ b/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/JobDslPluginTest.groovy @@ -3,6 +3,7 @@ package com.here.gradle.plugins.jobdsl import com.here.gradle.plugins.jobdsl.tasks.GenerateXmlTask import com.here.gradle.plugins.jobdsl.tasks.UpdateJenkinsTask import org.gradle.api.Project +import org.gradle.api.tasks.Copy import org.gradle.testfixtures.ProjectBuilder import spock.lang.Specification @@ -17,12 +18,21 @@ class JobDslPluginTest extends Specification { project = ProjectBuilder.builder().build() } + def 'resolveJenkinsPlugins task is created'() { + when: + project.pluginManager.apply('com.here.jobdsl') + + then: + project.tasks.resolveJenkinsPlugins instanceof Copy + } + def 'dslGenerateXml task is created'() { when: project.pluginManager.apply('com.here.jobdsl') then: project.tasks.dslGenerateXml instanceof GenerateXmlTask + project.tasks.dslUpdateJenkins.dependsOn.contains('resolveJenkinsPlugins') } def 'dslUpdateJenkins task is created'() { @@ -31,6 +41,7 @@ class JobDslPluginTest extends Specification { then: project.tasks.dslUpdateJenkins instanceof UpdateJenkinsTask + project.tasks.dslUpdateJenkins.dependsOn.contains('resolveJenkinsPlugins') } def 'groovy plugin is applied'() { @@ -41,6 +52,15 @@ class JobDslPluginTest extends Specification { project.plugins.hasPlugin('groovy') } + def 'required repositories are added'() { + when: + project.pluginManager.apply('com.here.jobdsl') + + then: + project.repositories.find { it.url.toString() == 'https://repo.jenkins-ci.org/releases/' } + project.repositories.find { it.url.toString() == 'https://repo1.maven.org/maven2/' } + } + def 'jobdsl source set is created'() { when: project.pluginManager.apply('com.here.jobdsl') @@ -49,6 +69,14 @@ class JobDslPluginTest extends Specification { project.sourceSets.findByName('jobdsl') } + def 'jenkinsPlugins configuration is created'() { + when: + project.pluginManager.apply('com.here.jobdsl') + + then: + project.configurations.findByName('jenkinsPlugins') + } + def 'jobdsl extension is created'() { when: project.pluginManager.apply('com.here.jobdsl') diff --git a/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/GenerateXmlTest.groovy b/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/GenerateXmlTest.groovy index a2b87ca..af48783 100644 --- a/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/GenerateXmlTest.groovy +++ b/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/GenerateXmlTest.groovy @@ -111,7 +111,7 @@ class GenerateXmlTest extends AbstractTaskTest { when: def result = gradleRunner - .withArguments('dslGenerateXml', '--filter=.*unfiltered.*') + .withArguments('dslGenerateXml', '--filter=(folder|.*unfiltered.*)') .build() then: @@ -130,7 +130,7 @@ class GenerateXmlTest extends AbstractTaskTest { when: def result = gradleRunner - .withArguments('dslGenerateXml', '--filter=.*unfiltered.*') + .withArguments('dslGenerateXml', '--filter=(folder|.*unfiltered.*)') .build() then: @@ -142,23 +142,6 @@ class GenerateXmlTest extends AbstractTaskTest { !new File(testProjectDir.root, 'build/jobdsl/xml/folder/view-filtered.xml').file } - def 'can create job in filtered folder'() { - given: - buildFile << readBuildGradle('generateXml/build.gradle') - copyResourceToTestDir('generateXml/job-in-filtered-folder.groovy') - - when: - def result = gradleRunner - .withArguments('dslGenerateXml', '--filter=.*job.*') - .build() - - then: - result.task(':dslGenerateXml').outcome == TaskOutcome.SUCCESS - - !new File(testProjectDir.root, 'build/jobdsl/xml/folder.xml').file - new File(testProjectDir.root, 'build/jobdsl/xml/folder/job.xml').file - } - def 'global configuration is available in DSL scripts'() { given: buildFile << readBuildGradle('generateXml/build-with-configuration.gradle') @@ -288,4 +271,77 @@ class GenerateXmlTest extends AbstractTaskTest { XMLUnit.compareXML(expectedText, actualText).identical() } + def 'job with generated DSL is generated correctly'() { + given: + buildFile << readBuildGradle('generateXml/build-with-generated-dsl.gradle') + copyResourceToTestDir('generateXml/job-with-generated-dsl.groovy') + + when: + def result = gradleRunner + .withArguments('dslGenerateXml') + .build() + + then: + result.task(':dslGenerateXml').outcome == TaskOutcome.SUCCESS + + def generatedFile = new File(testProjectDir.root, 'build/jobdsl/xml/job.xml') + generatedFile.file + def actualText = generatedFile.getText('UTF-8') + def expectedText = readResource('generateXml/job-with-generated-dsl.xml') + XMLUnit.compareXML(expectedText, actualText).identical() + } + + def 'task fails when plugin for generated DSL is missing'() { + given: + buildFile << readBuildGradle('generateXml/build.gradle') + copyResourceToTestDir('generateXml/job-with-generated-dsl.groovy') + + when: + def result = gradleRunner + .withArguments('dslGenerateXml') + .buildAndFail() + + then: + result.task(':dslGenerateXml').outcome == TaskOutcome.FAILED + result.output.contains('No signature of method: javaposse.jobdsl.dsl.helpers.ScmContext.cvsscm() is ' + + 'applicable for argument types') + } + + def 'job with Job DSL extension is generated correctly'() { + given: + buildFile << readBuildGradle('generateXml/build-with-jobdsl-extension.gradle') + copyResourceToTestDir('generateXml/job-with-job-dsl-extension.groovy') + + when: + def result = gradleRunner + .withArguments('dslGenerateXml') + .build() + + then: + result.task(':dslGenerateXml').outcome == TaskOutcome.SUCCESS + + def generatedFile = new File(testProjectDir.root, 'build/jobdsl/xml/job.xml') + generatedFile.file + def actualText = generatedFile.getText('UTF-8') + def expectedText = readResource('generateXml/job-with-job-dsl-extension.xml') + XMLUnit.compareXML(expectedText, actualText).identical() + } + + def 'task fails when plugin for Job DSL extension is missing'() { + given: + buildFile << readBuildGradle('generateXml/build.gradle') + copyResourceToTestDir('generateXml/job-with-job-dsl-extension.groovy') + + when: + def result = gradleRunner + .withArguments('dslGenerateXml') + .buildAndFail() + + then: + result.task(':dslGenerateXml').outcome == TaskOutcome.FAILED + result.output.contains( + 'No signature of method: javaposse.jobdsl.dsl.helpers.publisher.PublisherContext.jgivenReports() is ' + + 'applicable for argument types') + } + } diff --git a/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/UpdateJenkinsTest.groovy b/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/UpdateJenkinsTest.groovy index ef92c03..6335d38 100644 --- a/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/UpdateJenkinsTest.groovy +++ b/plugin/src/functionalTest/groovy/com/here/gradle/plugins/jobdsl/tasks/UpdateJenkinsTest.groovy @@ -332,7 +332,7 @@ class UpdateJenkinsTest extends AbstractTaskTest { then: result.task(':dslUpdateJenkins').outcome == TaskOutcome.SUCCESS - gradleSectionOutput(result.output, 'Deprecated plugins:') == [ 'htmlpublisher' ] + gradleSectionOutput(result.output, 'Deprecated plugins:') == ['htmlpublisher'] } def 'missing plugins are reported'() { @@ -345,7 +345,7 @@ class UpdateJenkinsTest extends AbstractTaskTest { then: result.task(':dslUpdateJenkins').outcome == TaskOutcome.SUCCESS - gradleSectionOutput(result.output, 'Missing plugins:') == [ 'gradle', 'timestamper' ] + gradleSectionOutput(result.output, 'Missing plugins:') == ['gradle', 'timestamper'] } @WithPlugin('gradle-1.22.hpi') @@ -359,7 +359,7 @@ class UpdateJenkinsTest extends AbstractTaskTest { then: result.task(':dslUpdateJenkins').outcome == TaskOutcome.SUCCESS - gradleSectionOutput(result.output, 'Outdated plugins:') == [ 'gradle' ] + gradleSectionOutput(result.output, 'Outdated plugins:') == ['gradle'] } def 'groovy postbuild step with UTF-8 characters is uploaded correctly'() { @@ -380,4 +380,79 @@ class UpdateJenkinsTest extends AbstractTaskTest { ).identical() } + def 'job with generated DSL is uploaded correctly'() { + given: + buildFile << readBuildGradle('updateJenkins/build-with-generated-dsl.gradle') + copyResourceToTestDir('updateJenkins/job-with-generated-dsl.groovy') + + when: + def result = gradleRunner + .withArguments('dslUpdateJenkins', jenkinsUrlParam()) + .build() + + then: + result.task(':dslUpdateJenkins').outcome == TaskOutcome.SUCCESS + + Item item = jenkinsRule.jenkins.getItemByFullName('job') + item instanceof FreeStyleProject + XMLUnit.compareXML( + readResource('updateJenkins/job-with-generated-dsl.xml'), + item.configFile.asString() + ).identical() + } + + def 'task fails when plugin for generated DSL is missing'() { + given: + buildFile << readBuildGradle('updateJenkins/build.gradle') + copyResourceToTestDir('updateJenkins/job-with-generated-dsl.groovy') + + when: + def result = gradleRunner + .withArguments('dslUpdateJenkins', jenkinsUrlParam()) + .buildAndFail() + + then: + result.task(':dslUpdateJenkins').outcome == TaskOutcome.FAILED + result.output.contains('No signature of method: javaposse.jobdsl.dsl.helpers.ScmContext.cvsscm() is ' + + 'applicable for argument types') + } + + def 'job with Job DSL extension is uploaded correctly'() { + given: + buildFile << readBuildGradle('updateJenkins/build-with-jobdsl-extension.gradle') + copyResourceToTestDir('updateJenkins/job-with-job-dsl-extension.groovy') + + when: + def result = gradleRunner + .withArguments('dslUpdateJenkins', jenkinsUrlParam()) + .build() + + then: + result.task(':dslUpdateJenkins').outcome == TaskOutcome.SUCCESS + + Item item = jenkinsRule.jenkins.getItemByFullName('job') + item instanceof FreeStyleProject + XMLUnit.compareXML( + readResource('updateJenkins/job-with-job-dsl-extension.xml'), + item.configFile.asString() + ).identical() + } + + def 'task fails when plugin for Job DSL extension is missing'() { + given: + buildFile << readBuildGradle('updateJenkins/build.gradle') + copyResourceToTestDir('updateJenkins/job-with-job-dsl-extension.groovy') + + when: + def result = gradleRunner + .withArguments('dslUpdateJenkins', jenkinsUrlParam()) + .buildAndFail() + + then: + result.task(':dslUpdateJenkins').outcome == TaskOutcome.FAILED + result.output.contains( + 'No signature of method: javaposse.jobdsl.dsl.helpers.publisher.PublisherContext.jgivenReports() is ' + + 'applicable for argument types') + } + } diff --git a/plugin/src/functionalTest/resources/generateXml/build-with-generated-dsl.gradle b/plugin/src/functionalTest/resources/generateXml/build-with-generated-dsl.gradle new file mode 100644 index 0000000..a811517 --- /dev/null +++ b/plugin/src/functionalTest/resources/generateXml/build-with-generated-dsl.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(CLASSPATH_STRING) + } +} + +apply plugin: 'com.here.jobdsl' + +dependencies { + compile localGroovy() + + jenkinsPlugins 'org.jenkins-ci.plugins:cloudbees-folder:6.2.1' + jenkinsPlugins 'org.jenkins-ci.plugins:cvs:2.13' +} diff --git a/plugin/src/functionalTest/resources/generateXml/build-with-jobdsl-extension.gradle b/plugin/src/functionalTest/resources/generateXml/build-with-jobdsl-extension.gradle new file mode 100644 index 0000000..e6161c2 --- /dev/null +++ b/plugin/src/functionalTest/resources/generateXml/build-with-jobdsl-extension.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(CLASSPATH_STRING) + } +} + +apply plugin: 'com.here.jobdsl' + +dependencies { + compile localGroovy() + + jenkinsPlugins 'org.jenkins-ci.plugins:cloudbees-folder:6.2.1' + jenkinsPlugins 'org.jenkins-ci.plugins:jgiven:0.15.1' +} diff --git a/plugin/src/functionalTest/resources/generateXml/build.gradle b/plugin/src/functionalTest/resources/generateXml/build.gradle index 1ac8b3c..c7d4e4a 100644 --- a/plugin/src/functionalTest/resources/generateXml/build.gradle +++ b/plugin/src/functionalTest/resources/generateXml/build.gradle @@ -8,4 +8,6 @@ apply plugin: 'com.here.jobdsl' dependencies { compile localGroovy() + + jenkinsPlugins 'org.jenkins-ci.plugins:cloudbees-folder:6.2.1' } diff --git a/plugin/src/functionalTest/resources/generateXml/empty-list-view.xml b/plugin/src/functionalTest/resources/generateXml/empty-list-view.xml index 14aed1b..2d229e2 100644 --- a/plugin/src/functionalTest/resources/generateXml/empty-list-view.xml +++ b/plugin/src/functionalTest/resources/generateXml/empty-list-view.xml @@ -1,10 +1,13 @@ + - false - false - - - - - - + view + false + false + + + + + + + false diff --git a/plugin/src/functionalTest/resources/generateXml/filter-jobs.groovy b/plugin/src/functionalTest/resources/generateXml/filter-jobs.groovy index 977c5a8..b4c8f39 100644 --- a/plugin/src/functionalTest/resources/generateXml/filter-jobs.groovy +++ b/plugin/src/functionalTest/resources/generateXml/filter-jobs.groovy @@ -1,3 +1,4 @@ +folder('folder') freeStyleJob('job-filtered') freeStyleJob('job-unfiltered') freeStyleJob('folder/job-filtered') diff --git a/plugin/src/functionalTest/resources/generateXml/filter-views.groovy b/plugin/src/functionalTest/resources/generateXml/filter-views.groovy index 7b13759..d611d52 100644 --- a/plugin/src/functionalTest/resources/generateXml/filter-views.groovy +++ b/plugin/src/functionalTest/resources/generateXml/filter-views.groovy @@ -1,3 +1,4 @@ +folder('folder') listView('view-filtered') listView('view-unfiltered') listView('folder/view-filtered') diff --git a/plugin/src/functionalTest/resources/generateXml/job-in-filtered-folder.groovy b/plugin/src/functionalTest/resources/generateXml/job-in-filtered-folder.groovy deleted file mode 100644 index 41ea089..0000000 --- a/plugin/src/functionalTest/resources/generateXml/job-in-filtered-folder.groovy +++ /dev/null @@ -1,2 +0,0 @@ -folder('folder') -freeStyleJob('folder/job') diff --git a/plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.groovy b/plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.groovy new file mode 100644 index 0000000..d00a17a --- /dev/null +++ b/plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.groovy @@ -0,0 +1,42 @@ +// Example taken from: https://github.com/jenkinsci/job-dsl-plugin/wiki/Automatically-Generated-DSL + +freeStyleJob('job') { + scm { + cvsscm { + repositories { + cvsRepository { + cvsRoot(':pserver:username@hostname:/opt/path/to/a/repo') + passwordRequired(false) + password(null) + compressionLevel(-1) + repositoryBrowser {} + repositoryItems { + cvsRepositoryItem { + modules { + cvsModule { + localName('bar') + projectsetFileName('bar') + remoteName('foo') + } + } + location { + tagRepositoryLocation { + tagName('test') + useHeadIfNotFound(false) + } + } + } + } + } + } + checkoutCurrentTimestamp(true) + canUseUpdate(true) + pruneEmptyDirectories(true) + legacy(false) + skipChangeLog(false) + disableCvsQuiet(false) + cleanOnFailedUpdate(false) + forceCleanCopy(false) + } + } +} diff --git a/plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.xml b/plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.xml new file mode 100644 index 0000000..c4306fe --- /dev/null +++ b/plugin/src/functionalTest/resources/generateXml/job-with-generated-dsl.xml @@ -0,0 +1,50 @@ + + + + + false + + true + false + false + false + + false + + + + + true + + + :pserver:username@hostname:/opt/path/to/a/repo + + + + + bar + foo + bar + + + + TAG + test + false + + + + -1 + + false + + + true + false + true + false + false + false + true + + diff --git a/plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.groovy b/plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.groovy new file mode 100644 index 0000000..8454af2 --- /dev/null +++ b/plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.groovy @@ -0,0 +1,15 @@ +// Example taken from: https://plugins.jenkins.io/jgiven + +freeStyleJob('job') { + publishers { + jgivenReports { + excludeEmptyScenarios() // Since 0.10 + html { + customCss 'some.css' + customJs 'some.js' + title 'My Custom Title' + } + results '**/*.json' + } + } +} diff --git a/plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.xml b/plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.xml new file mode 100644 index 0000000..e512f5e --- /dev/null +++ b/plugin/src/functionalTest/resources/generateXml/job-with-job-dsl-extension.xml @@ -0,0 +1,30 @@ + + + + + false + + + true + false + false + false + + false + + + + + + HTML + some.css + some.js + My Custom Title + + + true + **/*.json + + + + diff --git a/plugin/src/functionalTest/resources/updateJenkins/build-with-generated-dsl.gradle b/plugin/src/functionalTest/resources/updateJenkins/build-with-generated-dsl.gradle new file mode 100644 index 0000000..a811517 --- /dev/null +++ b/plugin/src/functionalTest/resources/updateJenkins/build-with-generated-dsl.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(CLASSPATH_STRING) + } +} + +apply plugin: 'com.here.jobdsl' + +dependencies { + compile localGroovy() + + jenkinsPlugins 'org.jenkins-ci.plugins:cloudbees-folder:6.2.1' + jenkinsPlugins 'org.jenkins-ci.plugins:cvs:2.13' +} diff --git a/plugin/src/functionalTest/resources/updateJenkins/build-with-jobdsl-extension.gradle b/plugin/src/functionalTest/resources/updateJenkins/build-with-jobdsl-extension.gradle new file mode 100644 index 0000000..e6161c2 --- /dev/null +++ b/plugin/src/functionalTest/resources/updateJenkins/build-with-jobdsl-extension.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(CLASSPATH_STRING) + } +} + +apply plugin: 'com.here.jobdsl' + +dependencies { + compile localGroovy() + + jenkinsPlugins 'org.jenkins-ci.plugins:cloudbees-folder:6.2.1' + jenkinsPlugins 'org.jenkins-ci.plugins:jgiven:0.15.1' +} diff --git a/plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.groovy b/plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.groovy new file mode 100644 index 0000000..d00a17a --- /dev/null +++ b/plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.groovy @@ -0,0 +1,42 @@ +// Example taken from: https://github.com/jenkinsci/job-dsl-plugin/wiki/Automatically-Generated-DSL + +freeStyleJob('job') { + scm { + cvsscm { + repositories { + cvsRepository { + cvsRoot(':pserver:username@hostname:/opt/path/to/a/repo') + passwordRequired(false) + password(null) + compressionLevel(-1) + repositoryBrowser {} + repositoryItems { + cvsRepositoryItem { + modules { + cvsModule { + localName('bar') + projectsetFileName('bar') + remoteName('foo') + } + } + location { + tagRepositoryLocation { + tagName('test') + useHeadIfNotFound(false) + } + } + } + } + } + } + checkoutCurrentTimestamp(true) + canUseUpdate(true) + pruneEmptyDirectories(true) + legacy(false) + skipChangeLog(false) + disableCvsQuiet(false) + cleanOnFailedUpdate(false) + forceCleanCopy(false) + } + } +} diff --git a/plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.xml b/plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.xml new file mode 100644 index 0000000..c4306fe --- /dev/null +++ b/plugin/src/functionalTest/resources/updateJenkins/job-with-generated-dsl.xml @@ -0,0 +1,50 @@ + + + + + false + + true + false + false + false + + false + + + + + true + + + :pserver:username@hostname:/opt/path/to/a/repo + + + + + bar + foo + bar + + + + TAG + test + false + + + + -1 + + false + + + true + false + true + false + false + false + true + + diff --git a/plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.groovy b/plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.groovy new file mode 100644 index 0000000..8454af2 --- /dev/null +++ b/plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.groovy @@ -0,0 +1,15 @@ +// Example taken from: https://plugins.jenkins.io/jgiven + +freeStyleJob('job') { + publishers { + jgivenReports { + excludeEmptyScenarios() // Since 0.10 + html { + customCss 'some.css' + customJs 'some.js' + title 'My Custom Title' + } + results '**/*.json' + } + } +} diff --git a/plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.xml b/plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.xml new file mode 100644 index 0000000..e512f5e --- /dev/null +++ b/plugin/src/functionalTest/resources/updateJenkins/job-with-job-dsl-extension.xml @@ -0,0 +1,30 @@ + + + + + false + + + true + false + false + false + + false + + + + + + HTML + some.css + some.js + My Custom Title + + + true + **/*.json + + + + diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringJenkinsJobManagement.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringJenkinsJobManagement.groovy new file mode 100644 index 0000000..eb49d0d --- /dev/null +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringJenkinsJobManagement.groovy @@ -0,0 +1,31 @@ +package com.here.gradle.plugins.jobdsl + +import javaposse.jobdsl.dsl.Item +import javaposse.jobdsl.dsl.NameNotProvidedException +import javaposse.jobdsl.plugin.JenkinsJobManagement + +/** + * Extends {@link JenkinsJobManagement} to add support for an {@link ItemFilter}. + */ +class FilteringJenkinsJobManagement extends JenkinsJobManagement { + + private final ItemFilter filter + + FilteringJenkinsJobManagement(ItemFilter filter, PrintStream outputLogger, Map envVars, File workspace) { + super(outputLogger, envVars, workspace) + this.filter = filter + } + + @Override + boolean createOrUpdateConfig(Item dslItem, boolean ignoreExisting) throws NameNotProvidedException { + filter.matches(dslItem.name) ? super.createOrUpdateConfig(dslItem, ignoreExisting) : true + } + + @Override + void createOrUpdateView(String path, String config, boolean ignoreExisting) { + if (filter.matches(path)) { + super.createOrUpdateView(path, config, ignoreExisting) + } + } + +} diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringMemoryJobManagement.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringMemoryJobManagement.groovy deleted file mode 100644 index 5befafc..0000000 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/FilteringMemoryJobManagement.groovy +++ /dev/null @@ -1,30 +0,0 @@ -package com.here.gradle.plugins.jobdsl - -import javaposse.jobdsl.dsl.Item -import javaposse.jobdsl.dsl.MemoryJobManagement -import javaposse.jobdsl.dsl.NameNotProvidedException - -/** - * Implementation of {@link MemoryJobManagement} that supports an {@link ItemFilter} to filter items. - */ -class FilteringMemoryJobManagement extends MemoryJobManagement { - - private final ItemFilter filter - - FilteringMemoryJobManagement(ItemFilter filter) { - this.filter = filter - } - - @Override - boolean createOrUpdateConfig(Item item, boolean ignoreExisting) throws NameNotProvidedException { - filter.matches(item.name) ? super.createOrUpdateConfig(item, ignoreExisting) : true - } - - @Override - void createOrUpdateView(String viewName, String config, boolean ignoreExisting) { - if (filter.matches(viewName)) { - super.createOrUpdateView(viewName, config, ignoreExisting) - } - } - -} diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/JobDslPlugin.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/JobDslPlugin.groovy index b54bf8f..777a178 100644 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/JobDslPlugin.groovy +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/JobDslPlugin.groovy @@ -2,8 +2,10 @@ package com.here.gradle.plugins.jobdsl import com.here.gradle.plugins.jobdsl.tasks.GenerateXmlTask import com.here.gradle.plugins.jobdsl.tasks.UpdateJenkinsTask + import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.tasks.Copy /** * Gradle plugin configuration for the Job DSL plugin. Configures tasks, extensions, and source sets. @@ -11,19 +13,12 @@ import org.gradle.api.Project class JobDslPlugin implements Plugin { @Override + @SuppressWarnings('DuplicateMapLiteral') void apply(Project project) { project.extensions.create('jobdsl', JobDslPluginExtension, project) - def execTasks = [] - execTasks += project.task('dslGenerateXml', type: GenerateXmlTask) - execTasks += project.task('dslUpdateJenkins', type: UpdateJenkinsTask) - project.plugins.apply('groovy') - execTasks.each { task -> - task.dependsOn 'classes' - } - project.sourceSets { jobdsl { groovy { @@ -31,6 +26,59 @@ class JobDslPlugin implements Plugin { } } } + + project.configurations { + jenkinsPlugins { + description 'Jenkins plugins that are required to process the Job DSL scripts.' + } + } + + project.repositories { + mavenCentral() + + maven { + url 'https://repo.jenkins-ci.org/releases/' + } + } + + project.dependencies { + // This Jenkins plugin needs to be loaded by the Jenkins instance to discover Job DSL extensions and support + // the auto-generated DSL. + jenkinsPlugins('org.jenkins-ci.plugins:job-dsl:1.66') { + exclude(module: 'groovy-all') + } + + // These JAR dependencies are required by the Gradle Exec task which executes the DSL scripts. + jenkinsPlugins('org.jenkins-ci.plugins:job-dsl:1.66@jar') { + exclude(module: 'groovy-all') + } + jenkinsPlugins 'org.jenkins-ci.plugins:structs:1.9@jar' + } + + project.task('resolveJenkinsPlugins', type: Copy) { + from project.configurations.jenkinsPlugins + into new File(project.sourceSets.main.output.resourcesDir, 'test-dependencies') + include '*.hpi' + include '*.jpi' + + doLast { + def baseNames = source.collect { it.name.take(it.name.lastIndexOf('.')) } + new File(destinationDir, 'index').setText(baseNames.join('\n'), 'UTF-8') + } + } + + def execTasks = [] + execTasks += project.task('dslGenerateXml', type: GenerateXmlTask) { + buildDirectory(project.buildDir.absolutePath) + } + + execTasks += project.task('dslUpdateJenkins', type: UpdateJenkinsTask) { + buildDirectory(project.buildDir.absolutePath) + } + + execTasks.each { task -> + task.dependsOn 'classes', 'resolveJenkinsPlugins' + } } } diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/RestJobManagement.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/RestJobManagement.groovy index 6755d53..7beb03f 100644 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/RestJobManagement.groovy +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/RestJobManagement.groovy @@ -3,7 +3,9 @@ package com.here.gradle.plugins.jobdsl import groovyx.net.http.ContentType import groovyx.net.http.HttpResponseDecorator import groovyx.net.http.RESTClient + import hudson.util.VersionNumber + import javaposse.jobdsl.dsl.AbstractJobManagement import javaposse.jobdsl.dsl.ConfigurationMissingException import javaposse.jobdsl.dsl.DslScriptException @@ -13,12 +15,18 @@ import javaposse.jobdsl.dsl.Item import javaposse.jobdsl.dsl.JobConfigurationNotFoundException import javaposse.jobdsl.dsl.NameNotProvidedException import javaposse.jobdsl.dsl.UserContent +import javaposse.jobdsl.plugin.JenkinsJobManagement + +import jenkins.model.Jenkins + import org.apache.http.HttpRequest import org.apache.http.HttpRequestInterceptor import org.apache.http.HttpStatus import org.apache.http.protocol.HttpContext import org.custommonkey.xmlunit.XMLUnit +import java.nio.file.Files + /** * Implementation of {@link javaposse.jobdsl.dsl.JobManagement} that performs all actions using the REST API of Jenkins. * Only methods required by the plugin are implemented, all others throw an {@link UnsupportedOperationException}. @@ -62,6 +70,8 @@ class RestJobManagement extends AbstractJobManagement implements DeferredJobMana boolean dryRun ItemFilter filter String jenkinsUrl + Jenkins jenkins + JenkinsJobManagement jenkinsJobManagement VersionNumber jenkinsVersion RESTClient restClient List plugins @@ -74,13 +84,14 @@ class RestJobManagement extends AbstractJobManagement implements DeferredJobMana @SuppressWarnings('ParameterCount') RestJobManagement(ItemFilter filter, boolean disablePluginChecks, boolean dryRun, String jenkinsUrl, - String jenkinsUser, String jenkinsApiToken) { + String jenkinsUser, String jenkinsApiToken, Jenkins jenkins) { super(System.out) this.disablePluginChecks = disablePluginChecks this.dryRun = dryRun this.filter = filter this.jenkinsUrl = jenkinsUrl + this.jenkins = jenkins if (!this.jenkinsUrl.endsWith('/')) { this.jenkinsUrl += '/' @@ -94,6 +105,11 @@ class RestJobManagement extends AbstractJobManagement implements DeferredJobMana itemRequests = [] viewRequests = [] + def workspace = Files.createTempDirectory('jobdsl').toFile() + workspace.deleteOnExit() + + jenkinsJobManagement = new JenkinsJobManagement(System.out, [:], workspace) + restClient = new RESTClient(jenkinsUrl) restClient.encoder.charset = 'UTF-8' restClient.handler.failure = { it } @@ -262,7 +278,7 @@ class RestJobManagement extends AbstractJobManagement implements DeferredJobMana @Override Node callExtension(String name, Item item, Class contextType, Object... args) throws Throwable { - return null + return jenkinsJobManagement.callExtension(name, item, contextType, args) } VersionNumber requestJenkinsVersion() { diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/AbstractDslTask.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/AbstractDslTask.groovy index 59ef473..94154bc 100644 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/AbstractDslTask.groovy +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/AbstractDslTask.groovy @@ -14,6 +14,7 @@ import org.gradle.api.tasks.JavaExec */ abstract class AbstractDslTask extends JavaExec { + private String buildDirectoryPath = '' protected String filter = '' protected ServerDefinition server protected String serverName @@ -23,6 +24,11 @@ abstract class AbstractDslTask extends JavaExec { group = 'Job DSL' } + AbstractDslTask buildDirectory(String buildDirectoryPath) { + this.buildDirectoryPath = buildDirectoryPath + return this + } + @Override @SuppressWarnings('UnnecessaryGetter') // getProperties() is much more readable than just properties in this case. void exec() { @@ -41,6 +47,7 @@ abstract class AbstractDslTask extends JavaExec { } def properties = getProperties() + properties['buildDirectory'] = buildDirectoryPath properties['configuration'] = encodeBase64(new JsonBuilder(project.jobdsl.configuration).toString()) properties['filter'] = encodeBase64(filter) properties['inputFiles'] = project.sourceSets.jobdsl.allGroovy.asPath @@ -49,7 +56,8 @@ abstract class AbstractDslTask extends JavaExec { systemProperties = properties main = mainClass - classpath = project.sourceSets.main.runtimeClasspath + project.buildscript.configurations.classpath + classpath = project.sourceSets.main.runtimeClasspath + project.buildscript.configurations.classpath + + project.configurations.jenkinsPlugins super.exec() } diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/AbstractTaskRunner.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/AbstractTaskRunner.groovy index b2cfd08..f4c3ad8 100644 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/AbstractTaskRunner.groovy +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/AbstractTaskRunner.groovy @@ -4,21 +4,37 @@ import com.here.gradle.plugins.jobdsl.DeferredJobManagement import com.here.gradle.plugins.jobdsl.GradleJobDslPluginException import com.here.gradle.plugins.jobdsl.ItemFilter import com.here.gradle.plugins.jobdsl.util.DslConfig + import groovy.json.JsonSlurper + import javaposse.jobdsl.dsl.DslScriptLoader +import javaposse.jobdsl.dsl.GeneratedItems import javaposse.jobdsl.dsl.JobManagement import javaposse.jobdsl.dsl.ScriptRequest +import jenkins.model.Jenkins + +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.jvnet.hudson.test.JenkinsRule + /** * Common code for all tasks that extend {@link com.here.gradle.plugins.jobdsl.tasks.AbstractDslTask} to perform their * actions in another process. This class takes care of receiving and decoding the configuration options for the tasks. */ abstract class AbstractTaskRunner { + public static final int EXIT_SUCCESS = 0 + public static final int EXIT_FAILURE = 1 + public static final int EXIT_EXCEPTION = 2 + protected JobManagement jobManagement protected Properties runProperties - @SuppressWarnings('Instanceof') // No other way to check if jobManagement is a DeferredJobManagement. + @SuppressWarnings('CatchException') + @SuppressWarnings('Instanceof') + @SuppressWarnings('PrintStackTrace') + @SuppressWarnings('SystemExit') void run() { runProperties = System.properties @@ -32,30 +48,57 @@ abstract class AbstractTaskRunner { DslConfig.serverConfiguration = serverConfiguration def filter = new ItemFilter(decodeBase64(runProperties['filter'])) - jobManagement = createJobManagement(filter) - DslScriptLoader loader = new DslScriptLoader(jobManagement) - if (!runProperties['inputFiles']) { - throw new GradleJobDslPluginException('No files found in JobDSL source folder.') - } + def jenkinsRule = new JenkinsRule() + jenkinsRule.contextPath = '/jenkins' - runProperties['inputFiles'].split(File.pathSeparator).each { String filename -> - println "Loading ${filename}" - ScriptRequest scriptRequest = new ScriptRequest(new File(filename).getText('UTF-8')) - loader.runScripts([scriptRequest]) - } + def description = Description.createSuiteDescription('Run Job DSL Task', []) - if (jobManagement instanceof DeferredJobManagement) { - jobManagement.applyChanges() + def statement = new Statement() { + @Override + void evaluate() throws Throwable { + def plugins = jenkinsRule.pluginManager.plugins.collect { "${it.shortName}:${it.version}" } + println("Installed plugins: $plugins") + + jobManagement = createJobManagement(jenkinsRule.jenkins, filter) + DslScriptLoader loader = new DslScriptLoader(jobManagement) + + if (!runProperties['inputFiles']) { + throw new GradleJobDslPluginException('No files found in JobDSL source folder.') + } + + List generatedItems = [] + + runProperties['inputFiles'].split(File.pathSeparator).each { String filename -> + println "Loading ${filename}" + ScriptRequest scriptRequest = new ScriptRequest(new File(filename).getText('UTF-8')) + generatedItems += loader.runScripts([scriptRequest]) + } + + if (jobManagement instanceof DeferredJobManagement) { + jobManagement.applyChanges() + } + + postProcess(jenkinsRule.jenkins, generatedItems, filter) + } } - postProcess() + try { + jenkinsRule.apply(statement, description).evaluate() + System.exit(EXIT_SUCCESS) + } catch (Exception e) { + e.printStackTrace() + System.exit(EXIT_EXCEPTION) + } finally { + System.exit(EXIT_FAILURE) + } } - abstract JobManagement createJobManagement(ItemFilter filter) + abstract JobManagement createJobManagement(Jenkins jenkins, ItemFilter filter) @SuppressWarnings('EmptyMethodInAbstractClass') - void postProcess() { + @SuppressWarnings('UnusedMethodParameter') + void postProcess(Jenkins jenkins, List generatedItems, ItemFilter filter) { // do nothing by default } diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/GenerateXmlRunner.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/GenerateXmlRunner.groovy index e690fea..cb93299 100644 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/GenerateXmlRunner.groovy +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/GenerateXmlRunner.groovy @@ -1,9 +1,20 @@ package com.here.gradle.plugins.jobdsl.tasks.runners -import com.here.gradle.plugins.jobdsl.FilteringMemoryJobManagement +import com.here.gradle.plugins.jobdsl.FilteringJenkinsJobManagement +import com.here.gradle.plugins.jobdsl.GradleJobDslPluginException import com.here.gradle.plugins.jobdsl.ItemFilter + +import hudson.model.AbstractItem +import hudson.model.Item +import hudson.model.ItemGroup + +import java.nio.file.Files + +import javaposse.jobdsl.dsl.GeneratedItems import javaposse.jobdsl.dsl.JobManagement +import jenkins.model.Jenkins + /** * Performs the action of the {@link com.here.gradle.plugins.jobdsl.tasks.GenerateXmlTask} to generate XML files for all * items and views. @@ -15,21 +26,64 @@ class GenerateXmlRunner extends AbstractTaskRunner { } @Override - JobManagement createJobManagement(ItemFilter filter) { - new FilteringMemoryJobManagement(filter) + JobManagement createJobManagement(Jenkins jenkins, ItemFilter filter) { + def workspace = Files.createTempDirectory('jobdsl').toFile() + workspace.deleteOnExit() + + new FilteringJenkinsJobManagement(filter, System.out, [:], workspace) } @Override - void postProcess() { + @SuppressWarnings('Instanceof') + void postProcess(Jenkins jenkins, List generatedItems, ItemFilter filter) { def outputDirectory = new File(runProperties['outputDirectory']) outputDirectory.deleteDir() outputDirectory.mkdirs() - jobManagement.savedConfigs.each { String name, String xml -> - writeXml(outputDirectory, name, xml) + + generatedItems.each { + it.jobs.each { generatedJob -> + if (filter.matches(generatedJob.jobName)) { + def item = jenkins.getItemByFullName(generatedJob.jobName) + if (item instanceof AbstractItem) { + writeXml(outputDirectory, item.fullName, item.configFile.asString()) + } else { + throw new GradleJobDslPluginException( + "Unsupported item type for item '${generatedJob.jobName}': ${item.getClass().name}") + } + } + } } - jobManagement.savedViews.each { String name, String xml -> - writeXml(outputDirectory, name, xml) + generatedItems.each { + it.views.each { generatedView -> + if (filter.matches(generatedView.name)) { + def parent = findParentItem(jenkins, generatedView.name) + def view = parent.getView(nameWithoutFolders(generatedView.name)) + def viewName = parent == jenkins ? view.viewName : "${parent.fullName}/${view.viewName}" + def outputStream = new ByteArrayOutputStream() + view.writeXml(outputStream) + def xml = new String(outputStream.toByteArray(), 'UTF-8') + writeXml(outputDirectory, viewName, xml) + } + } + } + } + + def nameWithoutFolders(String name) { + name.split('/').last() + } + + @SuppressWarnings('Instanceof') + def findParentItem(Jenkins jenkins, String name) { + int index = name.lastIndexOf('/') + switch (index) { + case -1: + case 0: + return jenkins + default: + def parentName = name[0..index - 1] + Item item = jenkins.getItemByFullName(parentName) + return item instanceof ItemGroup ? (ItemGroup) item : null } } diff --git a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/UpdateJenkinsRunner.groovy b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/UpdateJenkinsRunner.groovy index e044e22..ecde7fb 100644 --- a/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/UpdateJenkinsRunner.groovy +++ b/plugin/src/main/groovy/com/here/gradle/plugins/jobdsl/tasks/runners/UpdateJenkinsRunner.groovy @@ -3,8 +3,12 @@ package com.here.gradle.plugins.jobdsl.tasks.runners import com.here.gradle.plugins.jobdsl.GradleJobDslPluginException import com.here.gradle.plugins.jobdsl.ItemFilter import com.here.gradle.plugins.jobdsl.RestJobManagement + +import javaposse.jobdsl.dsl.GeneratedItems import javaposse.jobdsl.dsl.JobManagement +import jenkins.model.Jenkins + /** * Performs the action of the {@link com.here.gradle.plugins.jobdsl.tasks.UpdateJenkinsTask} to upload all items and * views to a Jenkins instance using the REST API. @@ -16,18 +20,18 @@ class UpdateJenkinsRunner extends AbstractTaskRunner { } @Override - JobManagement createJobManagement(ItemFilter filter) { + JobManagement createJobManagement(Jenkins jenkins, ItemFilter filter) { boolean disablePluginChecks = runProperties['disablePluginChecks'].toBoolean() boolean dryRun = runProperties['dryRun'].toBoolean() String jenkinsUrl = runProperties['jenkinsUrl'] String jenkinsUser = runProperties['jenkinsUser'] String jenkinsApiToken = runProperties['jenkinsApiToken'] - new RestJobManagement(filter, disablePluginChecks, dryRun, jenkinsUrl, jenkinsUser, jenkinsApiToken) + new RestJobManagement(filter, disablePluginChecks, dryRun, jenkinsUrl, jenkinsUser, jenkinsApiToken, jenkins) } @Override - void postProcess() { + void postProcess(Jenkins jenkins, List generatedItems, ItemFilter filter) { println '\ndslUpdateJenkins results:' def restJobManagement = (RestJobManagement) jobManagement @@ -45,6 +49,7 @@ class UpdateJenkinsRunner extends AbstractTaskRunner { printPluginList(restJobManagement.deprecatedPlugins, 'Deprecated') printPluginList(restJobManagement.missingPlugins, 'Missing') printPluginList(restJobManagement.outdatedPlugins, 'Outdated') + println '' } if (restJobManagement.statusCounter[RestJobManagement.STATUS_COULD_NOT_CREATE] > 0