diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b0fb063fa8..274f230468 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,18 @@ +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + version: 2 updates: -- package-ecosystem: "maven" - directory: "/" - schedule: - interval: "weekly" + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + ignore: + # Must remain within Jetty 9.x until Java 8 support is removed + - dependency-name: "org.eclipse.jetty:jetty-maven-plugin" + versions: [">=10.0.0"] + # Requires Java 11 starting with version 1711.v5b_1b_03f0fcf2. + - dependency-name: "org.kohsuke.stapler:stapler-groovy" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index c119c69843..5bf31fdc4f 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,2 +1,4 @@ +# https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc + _extends: .github tag-template: maven-hpi-plugin-$NEXT_MINOR_VERSION diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000000..832442dd35 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,16 @@ +# Automates creation of Release Drafts using Release Drafter +# More Info: https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc + +on: + push: + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml new file mode 100644 index 0000000000..9ac2968bca --- /dev/null +++ b/.mvn/extensions.xml @@ -0,0 +1,7 @@ + + + io.jenkins.tools.incrementals + git-changelist-maven-extension + 1.4 + + diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000000..2a0299c486 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1,2 @@ +-Pconsume-incrementals +-Pmight-produce-incrementals diff --git a/Jenkinsfile b/Jenkinsfile index db5c8363bf..265bf17c44 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,12 +1,43 @@ -properties([buildDiscarder(logRotator(numToKeepStr: '20'))]) -node('maven') { - checkout scm - timeout(time: 1, unit: 'HOURS') { - // TODO Azure mirror - ansiColor('xterm') { - withEnv(['MAVEN_OPTS=-Djansi.force=true']) { - sh 'mvn -B -Dstyle.color=always -ntp -Prun-its clean install site' +properties([ + buildDiscarder(logRotator(numToKeepStr: '20')), + disableConcurrentBuilds(abortPrevious: true) +]) + +def runTests(Map params = [:]) { + return { + def agentContainerLabel = params['jdk'] == 8 ? 'maven' : 'maven-' + params['jdk'] + boolean publishing = params['jdk'] == 8 + node(agentContainerLabel) { + timeout(time: 1, unit: 'HOURS') { + def stageIdentifier = params['platform'] + '-' + params['jdk'] + stage("Checkout (${stageIdentifier})") { + checkout scm + } + stage("Build (${stageIdentifier})") { + ansiColor('xterm') { + def args = ['-Dstyle.color=always', '-Prun-its', '-Dmaven.test.failure.ignore', 'clean', 'install', 'site'] + if (publishing) { + args += '-Dset.changelist' + } + // Needed for correct computation of JenkinsHome in RunMojo#execute. + withEnv(['JENKINS_HOME=', 'HUDSON_HOME=']) { + infra.runMaven(args, params['jdk']) } + } + } + stage("Archive (${stageIdentifier})") { + junit 'target/invoker-reports/TEST-*.xml' + if (publishing) { + infra.prepareToPublishIncrementals() + } } + } } + } } + +parallel( + 'linux-8': runTests(platform: 'linux', jdk: 8), + 'linux-11': runTests(platform: 'linux', jdk: 11) +) +infra.maybePublishIncrementals() diff --git a/README.md b/README.md index f9d002cbe1..600455d9e5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Maven plugin to build Jenkins plugins. See the [developer guide](https://jenkins.io/doc/developer/plugin-development/) for details. -[Mojo documentation](http://jenkinsci.github.io/maven-hpi-plugin/) +[Mojo documentation](https://jenkinsci.github.io/maven-hpi-plugin/) ## Changelog @@ -11,93 +11,9 @@ See the [developer guide](https://jenkins.io/doc/developer/plugin-development/) See [GitHub Releases](https://github.com/jenkinsci/maven-hpi-plugin/releases) -### 3.5 (2019-03-28) +### Older versions -* Treat POM loading errors in dependencies as nonfatal. - -### 3.3 (2019-01-31) - -* [JENKINS-54807](https://issues.jenkins-ci.org/browse/JENKINS-54807) - -Fix classloading of Java-internal modules when running `hpi:run` with Java 11 - -### 3.2 (2019-01-16) - -* [PR #90](https://github.com/jenkinsci/maven-hpi-plugin/pull/90) - -Introduce a new `hpi.compatibleSinceVersion` property to support -[marking plugins as incompatible](https://wiki.jenkins.io/display/JENKINS/Marking+a+new+plugin+version+as+incompatible+with+older+versions) -without the plugin configuration override - * Prevents issues like [JENKINS-55562](https://issues.jenkins-ci.org/browse/JENKINS-55562) -* [JENKINS-54949](https://issues.jenkins-ci.org/browse/JENKINS-54949) - -Add support of adding the current pom.xml to the custom WAR -in the `hpi:custom-war` mojo - -### 3.1 (2018-12-14) - -* `hpi:run` was broken since 3.0 since `minimumJavaVersion` was not being properly propagated. - -### 3.0 (2018-12-05) - -* [JENKINS-20679](https://issues.jenkins-ci.org/browse/JENKINS-20679) - -Inject `Minimum-Java-Version` into the manifest. - * It is set by a new mandatory `minimumJavaVersion` parameter in `hpi:hpi`, `hpi:jar` and `hpi:hpl` - * Format: `java.specification.version` according to [Java JEP-223](https://openjdk.java.net/jeps/223). - Examples: `1.6`, `1.7`, `1.8`, `6`, `7`, `8`, `9`, `11`, ... -* [PR #83](https://github.com/jenkinsci/maven-hpi-plugin/pull/83) - -Improve the error message when an improper JAR file is passed -* Internal: Move manifest-related parameters and logic to `AbstractJenkinsManifestMojo` - -### 2.7 (2018-10-30) - -* Delete `work/plugins/*.jpl` where the current `test`-scoped dependency is not in fact a snapshot. -* Use a more specific temp dir for Jetty. - -### 2.6 (2018 Jun 01) - -* Bugs in the dependency copy of `mvn hpi:run` could lead to anomalies such as `work/plugins/null.jpi`. - -### 2.5 (2018 May 11) - -* Option to override a snapshot plugin version with a more informative string. - -### 2.3 (2018 Apr 19) - -* Using a newer standard `VersionNumber` that precisely matches the behavior of the Jenkins plugin manager. - -### 2.2 (2018 Jan 30) - -* Fix `mvn clean hpi:run` and some similar special goal sequences. - -### 2.1 (2017 Sep 26) - -* Jenkins plugin archetypes are no longer bundled with this Maven plugin. Instead use the [new project](https://github.com/jenkinsci/archetypes/blob/master/README.md#introduction). -* Making `-DwebAppFile=…` work. -* Fixing unchecked/rawtypes warnings in `InjectedTest`. -* No more special handling of artifacts with `-ea` in the version. - -### 2.0 (2017 May 25) - -* Updated integrated Jetty server to 9.x. This means that JDK 8 is now required at build time. (Plugins may continue to target older Java baselines using the `java.level` property in the 2.x parent POM.) -* [JENKINS-24064](https://issues.jenkins-ci.org/browse/JENKINS-24064) Added `executable-war` artifact type, permitting Jenkins to stop deploying the wasteful `jenkins-war-*-war-for-test.jar` artifact, which was identical to `jenkins-war-*.war`. - -### 1.122 (2017 Apr 12) - -* Fixed HTML escaping for Javadoc created for taglibs so it can be processed by JDK 8. -* Logging the current artifact for `InjectedTest`. -* More fixes to mojos that assumed that plugin artifacts used a short name identical to the `artifactId`. -* Minor archetype updates. - -### 1.121 (2016 Dec 16) - -* Fixing a problem with plugin dependency resolution affecting users of jitpack.io. - -### 1.120 (2016 Sep 26) - -* Allowing `hpi:run` to pick up compiled classes & saved resources from core or plugin snapshot dependencies in addition to the plugin under test itself. -* Ensuring `Plugin-Dependencies` appears in a consistent order from build to build. - -### 1.119 and earlier - -Not recorded. +See [archive](https://github.com/jenkinsci/maven-hpi-plugin/tree/24b27178f4dbdb9eeb395f35fc94774d514c980a#35-2019-03-28). ## For maintainers diff --git a/pom.xml b/pom.xml index 832adf4a64..218962a1ad 100644 --- a/pom.xml +++ b/pom.xml @@ -1,20 +1,22 @@ + 4.0.0 org.jenkins-ci jenkins - 1.62 + 1.88 org.jenkins-ci.tools maven-hpi-plugin - 3.18-SNAPSHOT + ${revision}${changelist} maven-plugin Maven Jenkins Plugin Maven2 plugin for developing Jenkins plugins + https://github.com/jenkinsci/${project.artifactId} The Apache Software License, Version 2.0 @@ -23,15 +25,11 @@ - scm:git:git://github.com/jenkinsci/maven-hpi-plugin.git - scm:git:ssh://git@github.com/jenkinsci/maven-hpi-plugin.git - https://github.com/jenkinsci/maven-hpi-plugin - HEAD + scm:git:https://github.com/${gitHubRepo}.git + scm:git:git@github.com:${gitHubRepo}.git + ${scmTag} + https://github.com/${gitHubRepo} - - jenkins - https://buildhive.cloudbees.com/job/jenkinsci/job/maven-hpi-plugin/ - github.com - gitsite:git@github.com/jenkinsci/maven-hpi-plugin.git + gitsite:git@github.com/${gitHubRepo}.git + 3.36 + -SNAPSHOT ${${$}}{ $} $ { UTF-8 - 8 - - 2.5.3 - 2.2.0 - 3.6.0 - true - true + jenkinsci/${project.artifactId} + 1.1.0 + 3.8.6 + 3.6.4 + 2.1.1 + 1.1.0 + ${project.basedir}/src/spotbugs/spotbugs-excludes.xml - - - repo.jenkins-ci.org - https://repo.jenkins-ci.org/public/ - - - - - repo.jenkins-ci.org - https://repo.jenkins-ci.org/public/ - - + + + + javax.annotation + javax.annotation-api + 1.3.2 + + + org.apache.maven + maven-artifact + ${maven.version} + + + org.apache.maven + maven-core + ${maven.version} + + + org.apache.maven + maven-model + ${maven.version} + + + org.apache.maven.shared + maven-shared-utils + 3.3.4 + + + org.eclipse.aether + aether-api + ${aether.version} + + + org.eclipse.aether + aether-impl + ${aether.version} + + + org.eclipse.aether + aether-spi + ${aether.version} + + + org.eclipse.aether + aether-util + ${aether.version} + + + - org.apache.maven.plugin-tools - maven-plugin-annotations - ${maven-plugin-tools.version} - - provided + com.github.spotbugs + spotbugs-annotations + true + + + com.google.code.findbugs + jsr305 + + + + + com.google.guava + guava + 31.1-jre + + + com.sun.codemodel + codemodel + 2.6 + + + io.jenkins.lib + support-log-formatter + 1.1 + + + net.java.sezpoz + sezpoz + 1.13 + + + org.apache.ant + ant + 1.10.12 + + + org.apache.maven + maven-archiver + 3.6.0 + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.3.0 + + + org.apache.maven.shared + maven-artifact-transfer + 0.13.1 + + + org.apache.maven.shared + maven-dependency-tree + 3.2.0 + + + org.codehaus.plexus + plexus-component-annotations + ${plexus-containers.version} + + + org.codehaus.plexus + plexus-interactivity-api + 1.1 org.codehaus.plexus plexus-utils - 3.3.0 + 3.4.2 - com.ibm.icu - icu4j - 67.1 - + org.eclipse.jetty + jetty-maven-plugin + 9.4.49.v20220914 - com.sun.codemodel - codemodel - 2.6 + org.jenkins-ci + version-number + 1.10 org.kohsuke.stapler stapler-groovy - 1.260 + 1685.v3b_5035c4ce05 - org.apache.maven - maven-plugin-api - ${maven.version} + org.twdata.maven + mojo-executor + 2.4.0 org.apache.maven - maven-project - ${maven.version} + maven-artifact + provided org.apache.maven - maven-archiver - 2.0.1 + maven-builder-support + ${maven.version} + provided org.apache.maven - maven-artifact - ${maven.version} + maven-core + provided - org.eclipse.jetty - jetty-maven-plugin - 9.4.32.v20200930 + org.apache.maven + maven-model + provided - net.java.sezpoz - sezpoz - 1.13 + org.apache.maven + maven-model-builder + ${maven.version} + provided - - org.apache.maven.plugins - maven-dependency-plugin - 2.10 + org.apache.maven + maven-plugin-api + ${maven.version} + provided - org.codehaus.plexus - plexus-interactivity-api - 1.0 + org.apache.maven + maven-repository-metadata + ${maven.version} + provided - - org.kohsuke.stapler - maven-stapler-plugin - 1.17 - - - - javax.servlet - servlet-api - - + org.apache.maven + maven-resolver-provider + ${maven.version} + provided - org.codehaus.plexus - plexus-component-annotations - 2.1.0 + org.apache.maven + maven-settings + ${maven.version} + provided - org.jenkins-ci - version-number - 1.7 + org.apache.maven + maven-settings-builder + ${maven.version} + provided - io.jenkins.lib - support-log-formatter - 1.0 + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-tools.version} + + provided + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + org.apache.maven.plugins maven-site-plugin - 3.9.1 com.github.stephenc.wagon wagon-gitsite 0.5 - - org.apache.velocity - velocity - 1.5 - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.maven.plugins - maven-antrun-plugin - [1.3,) - - run - - - - - - - - - - - org.apache.maven.plugins - maven-plugin-plugin - ${maven-plugin-tools.version} - - hpi - true - - + maven-enforcer-plugin - mojo-descriptor - - descriptor - - - - help-goal - - helpmojo - + display-info + + + + + commons-io:commons-io + commons-lang:commons-lang + org.apache.commons:commons-compress + org.apache.maven.wagon:wagon-provider-api + org.codehaus.plexus:plexus-container-default + org.eclipse.sisu:org.eclipse.sisu.plexus + org.ow2.asm:asm + org.slf4j:slf4j-api + + + + + [1.8.0,] + + + 1.8 + + + - - org.codehaus.mojo - mrm-maven-plugin - 1.2.0 - - repository.proxy.url - - - - - org.apache.maven.plugins maven-invoker-plugin - 3.2.1 + 3.3.0 src/it ${project.build.directory}/its ${basedir}/target/local-repo src/it/settings.xml - true - true */pom.xml @@ -276,40 +348,51 @@ ${repository.proxy.url} + + + + - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.19 + org.apache.maven.plugins + maven-plugin-plugin + ${maven-plugin-tools.version} + + hpi + true + + + help-goal - check + helpmojo + + + + mojo-descriptor + + descriptor - - - org.codehaus.mojo.signature - java1${java.level} - 1.0 - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 + org.codehaus.mojo + mrm-maven-plugin + 1.4.1 - 1.${java.level} - 1.${java.level} + repository.proxy.url + + + org.codehaus.plexus plexus-component-metadata - 2.1.0 + ${plexus-containers.version} @@ -323,7 +406,7 @@ org.apache.maven.wagon wagon-ssh - 3.4.1 + 3.5.3 @@ -344,37 +427,106 @@ - org.codehaus.mojo - mrm-maven-plugin + maven-invoker-plugin + integration-test - start - stop + install + run - maven-invoker-plugin + org.codehaus.mojo + mrm-maven-plugin - integration-test - install - run + start + stop - - true - - - - + + all-tests + + + !invoker.test + + + + true + io.jenkins.tools.maven_hpi_plugin.its + + + + specific-tests + + + invoker.test + + + + true + + + + + jdk-8-and-below + + (,1.8] + + + + + maven-compiler-plugin + + 1.8 + 1.8 + 1.8 + 1.8 + + + + + + maven-javadoc-plugin + + 1.8 + + + + + + + + jdk-9-and-above + + [9,) + + + + + maven-compiler-plugin + + 8 + 8 + + + + maven-javadoc-plugin + + 8 + + + + + diff --git a/src/it/JENKINS-45740-metadata/invoker.properties b/src/it/JENKINS-45740-metadata/invoker.properties index ccb70fca53..7335f6c9fa 100644 --- a/src/it/JENKINS-45740-metadata/invoker.properties +++ b/src/it/JENKINS-45740-metadata/invoker.properties @@ -1,3 +1,3 @@ # install, not verify, because we want to check the artifact as we would be about to deploy it # release.skipTests normally set in jenkins-release profile since release:perform would do the tests -invoker.goals=-Dstyle.color=always -ntp -Pjenkins-release -Drelease.skipTests=false clean install +invoker.goals=-ntp -Pjenkins-release -Drelease.skipTests=false clean install diff --git a/src/it/JENKINS-45740-metadata/pom.xml b/src/it/JENKINS-45740-metadata/pom.xml index a0575a897f..1628e756ff 100644 --- a/src/it/JENKINS-45740-metadata/pom.xml +++ b/src/it/JENKINS-45740-metadata/pom.xml @@ -5,14 +5,15 @@ org.jenkins-ci.plugins plugin - 3.47 + 4.40 + org.jenkins-ci.tools.hpi.its JENKINS-45740-metadata 1.0-SNAPSHOT hpi - 8 + 2.249.1 @project.version@ https://github.com/jenkinsci/configuration-as-code-plugin/releases @@ -20,16 +21,16 @@ - - MIT License - https://opensource.org/licenses/MIT - repo - - - MY License - https://mylicense.txt - Hello, world! - + + MIT License + https://opensource.org/licenses/MIT + repo + + + MY License + https://mylicense.txt + Hello, world! + @@ -46,9 +47,9 @@ - scm:git:git://github.com/jenkinsci/${project.artifctId}-plugin.git - scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git - https://github.com/jenkinsci/${project.artifactId}-plugin - HEAD + scm:git:https://github.com/jenkinsci/maven-hpi-plugin.git + scm:git:git@github.com:jenkinsci/maven-hpi-plugin.git + https://github.com/jenkinsci/${project.artifactId}-plugin + HEAD diff --git a/src/it/JENKINS-58771-packaged-plugins-2/invoker.properties b/src/it/JENKINS-58771-packaged-plugins-2/invoker.properties index de21ce6dfe..a5788e324f 100644 --- a/src/it/JENKINS-58771-packaged-plugins-2/invoker.properties +++ b/src/it/JENKINS-58771-packaged-plugins-2/invoker.properties @@ -1 +1 @@ -invoker.goals=-Dstyle.color=always -ntp clean install +invoker.goals=-ntp clean install diff --git a/src/it/JENKINS-58771-packaged-plugins-2/pom.xml b/src/it/JENKINS-58771-packaged-plugins-2/pom.xml index d4b21ebd50..14dcb7c29b 100644 --- a/src/it/JENKINS-58771-packaged-plugins-2/pom.xml +++ b/src/it/JENKINS-58771-packaged-plugins-2/pom.xml @@ -5,15 +5,15 @@ org.jenkins-ci.plugins plugin - 3.57 + 4.40 + org.jenkins-ci.tools.hpi.its JENKINS-58771-packaged-plugins 1.0-SNAPSHOT hpi - 8 - 2.176.1 + 2.249.1 @project.version@ @@ -40,21 +40,42 @@ org.jenkins-ci.plugins.workflow workflow-cps - - - - - - + + + + + + + + + + + + + + + + + org.jenkins-ci.plugins.workflow workflow-support tests test - - - + + + + + + + + + + + + + @@ -62,8 +83,8 @@ io.jenkins.tools.bom - bom-2.176.x - 5 + bom-2.249.x + 984.vb5eaac999a7e import pom diff --git a/src/it/JENKINS-58771-packaged-plugins-3/invoker.properties b/src/it/JENKINS-58771-packaged-plugins-3/invoker.properties index de21ce6dfe..a5788e324f 100644 --- a/src/it/JENKINS-58771-packaged-plugins-3/invoker.properties +++ b/src/it/JENKINS-58771-packaged-plugins-3/invoker.properties @@ -1 +1 @@ -invoker.goals=-Dstyle.color=always -ntp clean install +invoker.goals=-ntp clean install diff --git a/src/it/JENKINS-58771-packaged-plugins-3/pom.xml b/src/it/JENKINS-58771-packaged-plugins-3/pom.xml index 79df6294d0..a562ee85e4 100644 --- a/src/it/JENKINS-58771-packaged-plugins-3/pom.xml +++ b/src/it/JENKINS-58771-packaged-plugins-3/pom.xml @@ -6,15 +6,17 @@ org.jenkins-ci.plugins plugin 3.57 + org.jenkins-ci.tools.hpi.its JENKINS-58771-packaged-plugins 1.0-SNAPSHOT hpi + 2.249.1 8 - 2.176.1 @project.version@ + true @@ -41,7 +43,7 @@ org.jenkins-ci.plugins google-oauth-plugin - 0.8 + 0.10 @@ -49,16 +51,11 @@ io.jenkins.tools.bom - bom-2.176.x - 5 + bom-2.249.x + 984.vb5eaac999a7e import pom - - commons-io - commons-io - 2.6 - diff --git a/src/it/JENKINS-58771-packaged-plugins/invoker.properties b/src/it/JENKINS-58771-packaged-plugins/invoker.properties index de21ce6dfe..a5788e324f 100644 --- a/src/it/JENKINS-58771-packaged-plugins/invoker.properties +++ b/src/it/JENKINS-58771-packaged-plugins/invoker.properties @@ -1 +1 @@ -invoker.goals=-Dstyle.color=always -ntp clean install +invoker.goals=-ntp clean install diff --git a/src/it/JENKINS-58771-packaged-plugins/pom.xml b/src/it/JENKINS-58771-packaged-plugins/pom.xml index 0de87c88fc..08c2c179c6 100644 --- a/src/it/JENKINS-58771-packaged-plugins/pom.xml +++ b/src/it/JENKINS-58771-packaged-plugins/pom.xml @@ -5,15 +5,15 @@ org.jenkins-ci.plugins plugin - 3.47 + 4.40 + org.jenkins-ci.tools.hpi.its JENKINS-58771-packaged-plugins 1.0-SNAPSHOT hpi - 8 - 2.176.1 + 2.249.1 @project.version@ @@ -34,13 +34,11 @@ org.jenkins-ci.plugins git - 3.11.0 test org.jenkinsci.plugins pipeline-model-extensions - 1.3.7 @@ -48,8 +46,8 @@ io.jenkins.tools.bom - bom - 2.176.2 + bom-2.249.x + 984.vb5eaac999a7e import pom diff --git a/src/it/assemble-dependencies-as-jpi/invoker.properties b/src/it/assemble-dependencies-as-jpi/invoker.properties index 1aa153b3a9..9295005f6b 100644 --- a/src/it/assemble-dependencies-as-jpi/invoker.properties +++ b/src/it/assemble-dependencies-as-jpi/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean generate-resources +invoker.goals=-ntp clean generate-resources diff --git a/src/it/assemble-dependencies-as-jpi/pom.xml b/src/it/assemble-dependencies-as-jpi/pom.xml index 758279afda..85eff1d330 100644 --- a/src/it/assemble-dependencies-as-jpi/pom.xml +++ b/src/it/assemble-dependencies-as-jpi/pom.xml @@ -1,19 +1,42 @@ + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its bundle-it 1.0-SNAPSHOT pom + + 2.249.1 + @project.version@ + + + + + + org.jenkins-ci + symbol-annotation + 1.7 + + + + org.jenkins-ci.plugins ssh-credentials - 1.10 + 1.18.1 @@ -23,7 +46,6 @@ @project.groupId@ @project.artifactId@ - @project.version@ generate-resources diff --git a/src/it/assemble-dependencies/invoker.properties b/src/it/assemble-dependencies/invoker.properties index 1aa153b3a9..9295005f6b 100644 --- a/src/it/assemble-dependencies/invoker.properties +++ b/src/it/assemble-dependencies/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean generate-resources +invoker.goals=-ntp clean generate-resources diff --git a/src/it/assemble-dependencies/pom.xml b/src/it/assemble-dependencies/pom.xml index 757f1f880b..caa95e0259 100644 --- a/src/it/assemble-dependencies/pom.xml +++ b/src/it/assemble-dependencies/pom.xml @@ -1,19 +1,42 @@ + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its bundle-it 1.0-SNAPSHOT pom + + 2.249.1 + @project.version@ + + + + + + org.jenkins-ci + symbol-annotation + 1.7 + + + + org.jenkins-ci.plugins ssh-credentials - 1.10 + 1.18.1 @@ -23,7 +46,6 @@ @project.groupId@ @project.artifactId@ - @project.version@ generate-resources diff --git a/src/it/bad-bundle/pom.xml b/src/it/bad-bundle/pom.xml deleted file mode 100644 index 42c9b76420..0000000000 --- a/src/it/bad-bundle/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - 4.0.0 - - org.jenkins-ci.tools.hpi.its - bundle-it - 1.0-SNAPSHOT - - pom - - - - org.jenkins-ci.plugins - ssh-credentials - 1.10 - - - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - generate-resources - - bundle-plugins - - - - - - - - diff --git a/src/it/bundle-fails-optional-conflict/pom.xml b/src/it/bundle-fails-optional-conflict/pom.xml deleted file mode 100644 index c9580d6b57..0000000000 --- a/src/it/bundle-fails-optional-conflict/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - 4.0.0 - - org.jenkins-ci.tools.hpi.its - bundle-it - 1.0-SNAPSHOT - - pom - - - - org.jenkins-ci.plugins - parameterized-trigger - 2.26 - hpi - true - - - org.jenkins-ci.plugins - matrix-project - 1.2 - hpi - - - - - ${project.artifactId} - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - generate-resources - - bundle-plugins - - - - - - - - diff --git a/src/it/bundle-with-optional-deps/pom.xml b/src/it/bundle-with-optional-deps/pom.xml deleted file mode 100644 index 1226e36363..0000000000 --- a/src/it/bundle-with-optional-deps/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - 4.0.0 - - org.jenkins-ci.tools.hpi.its - bundle-it - 1.0-SNAPSHOT - - pom - - - - org.jenkins-ci.plugins - ssh-credentials - 1.10 - hpi - - - org.jenkins-ci.plugins - ssh-slaves - 1.6 - hpi - true - - - org.jenkins-ci.plugins - support-core - 2.18 - hpi - true - - - - - - ${project.artifactId} - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - generate-resources - - bundle-plugins - - - - - - - - diff --git a/src/it/bundle-with-optional-deps/verify.groovy b/src/it/bundle-with-optional-deps/verify.groovy deleted file mode 100644 index ee90cbba1e..0000000000 --- a/src/it/bundle-with-optional-deps/verify.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -assert new File(basedir, 'target/bundle-it/WEB-INF/plugins/ssh-credentials.hpi').exists(); -assert new File(basedir, 'target/bundle-it/WEB-INF/plugins/credentials.hpi').exists(); -assert new File(basedir, 'target/bundle-it/WEB-INF/optional-plugins/ssh-slaves.hpi').exists(); -assert new File(basedir, 'target/bundle-it/WEB-INF/optional-plugins/support-core.hpi').exists(); -assert new File(basedir, 'target/bundle-it/WEB-INF/optional-plugins/metrics.hpi').exists(); - -String manifest = new File(basedir, 'target/plugin-manifest.txt').text - -assert manifest =~ /ssh-credentials\s+1\.10/ : "direct non-optional dependency on ssh-credentials" -assert manifest =~ /credentials\s+1\.16\.1/ : "transitive non-optional dependency of ssh-credentials" -assert manifest =~ /ssh-slaves\s+1\.6/ : "direct optional dependency on ssh-slaves" -assert ! (manifest =~ /credentials\s+1\.9\.4/) : "don't pull in the transitive dependency from ssh-slaves as already pulled in" -assert manifest =~ /support-core\s+2\.18/ : "direct optional dependency on support-core" -assert manifest =~ /metrics\s+3\.0\.0/ : "transitive optional dependency on metrics" - -return true; diff --git a/src/it/check-core-version-failure/invoker.properties b/src/it/check-core-version-failure/invoker.properties index cc8c3232ee..871413e046 100644 --- a/src/it/check-core-version-failure/invoker.properties +++ b/src/it/check-core-version-failure/invoker.properties @@ -17,5 +17,5 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp validate +invoker.goals=-ntp validate invoker.buildResult = failure diff --git a/src/it/check-core-version-failure/pom.xml b/src/it/check-core-version-failure/pom.xml index b4d4a75024..f6a2c27129 100644 --- a/src/it/check-core-version-failure/pom.xml +++ b/src/it/check-core-version-failure/pom.xml @@ -5,38 +5,27 @@ org.jenkins-ci.plugins plugin - 4.6 + 4.40 check-core-version-failure 1.0-SNAPSHOT hpi - 8 - 2.204 + 2.249.1 + @project.version@ org.jenkins-ci.plugins jackson2-api - 2.11.2 + 2.13.2-260.v43d711474c77 io.jenkins configuration-as-code - 1.36 + 1414.v878271fc496f test - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - - diff --git a/src/it/check-core-version-failure/verify.groovy b/src/it/check-core-version-failure/verify.groovy index b26d6be85b..dbea516cf1 100644 --- a/src/it/check-core-version-failure/verify.groovy +++ b/src/it/check-core-version-failure/verify.groovy @@ -17,6 +17,6 @@ * under the License. */ -assert new File(basedir, 'build.log').getText('UTF-8').contains("Dependency org.jenkins-ci.plugins:jackson2-api:jar:2.11.2 requires Jenkins 2.222.4 or higher.") +assert new File(basedir, 'build.log').getText('UTF-8').contains("Dependency io.jenkins:configuration-as-code:jar:1414.v878271fc496f requires Jenkins 2.289.3 or higher.") return true; diff --git a/src/it/check-core-version-success/invoker.properties b/src/it/check-core-version-success/invoker.properties index ecb1c83ea4..ea9aa6b6b3 100644 --- a/src/it/check-core-version-success/invoker.properties +++ b/src/it/check-core-version-success/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp validate +invoker.goals=-ntp validate diff --git a/src/it/check-core-version-success/pom.xml b/src/it/check-core-version-success/pom.xml index 1844377200..c5abff179f 100644 --- a/src/it/check-core-version-success/pom.xml +++ b/src/it/check-core-version-success/pom.xml @@ -5,32 +5,21 @@ org.jenkins-ci.plugins plugin - 4.6 + 4.40 check-core-version-success 1.0-SNAPSHOT hpi - 8 - 2.222.4 + 2.249.1 + @project.version@ org.jenkins-ci.plugins jackson2-api - 2.11.2 + 2.13.2-260.v43d711474c77 - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - - diff --git a/src/it/compile-fork-it/invoker.properties b/src/it/compile-fork-it/invoker.properties index 1f11d01bf9..b36692a49f 100644 --- a/src/it/compile-fork-it/invoker.properties +++ b/src/it/compile-fork-it/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean compile +invoker.goals=-ntp clean compile diff --git a/src/it/compile-fork-it/pom.xml b/src/it/compile-fork-it/pom.xml index 66e7dfcc31..071190f8f1 100644 --- a/src/it/compile-fork-it/pom.xml +++ b/src/it/compile-fork-it/pom.xml @@ -1,44 +1,36 @@ + - - 4.0.0 + + 4.0.0 org.jenkins-ci.plugins plugin - 1.424 + 4.40 + - org.jenkins-ci.tools.hpi.its - compile-it - 1.0-SNAPSHOT + org.jenkins-ci.tools.hpi.its + compile-it + 1.0-SNAPSHOT + + hpi - hpi + MyNewPlugin - MyNewPlugin + + 2.249.1 + @project.version@ + - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - true - - - - org.kohsuke - access-modifier-checker - 1.0 - - - @project.groupId@ @project.artifactId@ - @project.version@ + + true + diff --git a/src/it/compile-it/invoker.properties b/src/it/compile-it/invoker.properties index 1f11d01bf9..b36692a49f 100644 --- a/src/it/compile-it/invoker.properties +++ b/src/it/compile-it/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean compile +invoker.goals=-ntp clean compile diff --git a/src/it/compile-it/pom.xml b/src/it/compile-it/pom.xml index 6310a19489..7ec95ff8cb 100644 --- a/src/it/compile-it/pom.xml +++ b/src/it/compile-it/pom.xml @@ -1,12 +1,14 @@ + 4.0.0 - - org.jenkins-ci.plugins - plugin - 1.424 - + + org.jenkins-ci.plugins + plugin + 4.40 + + org.jenkins-ci.tools.hpi.its compile-it @@ -16,28 +18,8 @@ MyNewPlugin - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - org.kohsuke - access-modifier-checker - 1.0 - - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - - + + 2.249.1 + @project.version@ + diff --git a/src/it/bundle-with-optional-deps/invoker.properties b/src/it/compile-multimodule-it/invoker.properties similarity index 92% rename from src/it/bundle-with-optional-deps/invoker.properties rename to src/it/compile-multimodule-it/invoker.properties index 1aa153b3a9..b36692a49f 100644 --- a/src/it/bundle-with-optional-deps/invoker.properties +++ b/src/it/compile-multimodule-it/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean generate-resources +invoker.goals=-ntp clean compile diff --git a/src/it/compile-multimodule-it/plugin1/pom.xml b/src/it/compile-multimodule-it/plugin1/pom.xml new file mode 100644 index 0000000000..800383cb92 --- /dev/null +++ b/src/it/compile-multimodule-it/plugin1/pom.xml @@ -0,0 +1,15 @@ + + + + 4.0.0 + + + org.jenkins-ci.tools.hpi.its + compile-multimodule-it-parent + 1.0-SNAPSHOT + + + compile-multimodule-it-plugin1 + 1.0-SNAPSHOT + hpi + diff --git a/src/it/compile-multimodule-it/plugin2/pom.xml b/src/it/compile-multimodule-it/plugin2/pom.xml new file mode 100644 index 0000000000..d83c6d0982 --- /dev/null +++ b/src/it/compile-multimodule-it/plugin2/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + + org.jenkins-ci.tools.hpi.its + compile-multimodule-it-parent + 1.0-SNAPSHOT + + + compile-multimodule-it-plugin2 + hpi + + + + ${project.groupId} + compile-multimodule-it-plugin1 + ${project.version} + + + diff --git a/src/it/compile-multimodule-it/pom.xml b/src/it/compile-multimodule-it/pom.xml new file mode 100644 index 0000000000..0aa8e84eb7 --- /dev/null +++ b/src/it/compile-multimodule-it/pom.xml @@ -0,0 +1,27 @@ + + + + 4.0.0 + + + org.jenkins-ci.plugins + plugin + 4.40 + + + + org.jenkins-ci.tools.hpi.its + compile-multimodule-it-parent + 1.0-SNAPSHOT + + pom + + 2.249.1 + @project.version@ + + + + plugin1 + plugin2 + + diff --git a/src/it/bad-bundle/invoker.properties b/src/it/java-level/invoker.properties similarity index 89% rename from src/it/bad-bundle/invoker.properties rename to src/it/java-level/invoker.properties index d49748eb4a..9b17b269c5 100644 --- a/src/it/bad-bundle/invoker.properties +++ b/src/it/java-level/invoker.properties @@ -17,5 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean generate-resources -invoker.buildResult = failure +invoker.goals=-ntp clean verify diff --git a/src/it/java-level/pom.xml b/src/it/java-level/pom.xml new file mode 100644 index 0000000000..1ad49b1a6f --- /dev/null +++ b/src/it/java-level/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its + java-level + 1.0-SNAPSHOT + hpi + MyNewPlugin + + 2.249.1 + 8 + @project.version@ + + diff --git a/src/it/java-level/src/main/java/org/jenkinsci/tools/hpi/its/X.java b/src/it/java-level/src/main/java/org/jenkinsci/tools/hpi/its/X.java new file mode 100644 index 0000000000..a39719b66a --- /dev/null +++ b/src/it/java-level/src/main/java/org/jenkinsci/tools/hpi/its/X.java @@ -0,0 +1,2 @@ +package org.jenkinsci.tools.hpi.its; +public class X {} diff --git a/src/it/java-level/src/main/resources/index.jelly b/src/it/java-level/src/main/resources/index.jelly new file mode 100644 index 0000000000..2f655e510a --- /dev/null +++ b/src/it/java-level/src/main/resources/index.jelly @@ -0,0 +1,2 @@ + +
diff --git a/src/it/java-level/verify.groovy b/src/it/java-level/verify.groovy new file mode 100644 index 0000000000..425b87cc1b --- /dev/null +++ b/src/it/java-level/verify.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +assert new File(basedir, 'build.log').getText('UTF-8').contains("Ignoring deprecated java.level property. This property should be removed from your plugin's POM. In the future this warning will be changed to an error and will break the build.") + +return true diff --git a/src/it/bundle-fails-optional-conflict/invoker.properties b/src/it/jenkins-version/invoker.properties similarity index 89% rename from src/it/bundle-fails-optional-conflict/invoker.properties rename to src/it/jenkins-version/invoker.properties index d49748eb4a..a1c7c93753 100644 --- a/src/it/bundle-fails-optional-conflict/invoker.properties +++ b/src/it/jenkins-version/invoker.properties @@ -17,5 +17,5 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean generate-resources -invoker.buildResult = failure +invoker.goals=-ntp clean package +invoker.buildResult=failure diff --git a/src/it/jenkins-version/pom.xml b/src/it/jenkins-version/pom.xml new file mode 100644 index 0000000000..979fad605e --- /dev/null +++ b/src/it/jenkins-version/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 3.57 + + + org.jenkins-ci.tools.hpi.its + jenkins-version + 1.0-SNAPSHOT + hpi + MyNewPlugin + + 2.200 + 8 + @project.version@ + + diff --git a/src/it/jenkins-version/src/main/java/org/jenkinsci/tools/hpi/its/X.java b/src/it/jenkins-version/src/main/java/org/jenkinsci/tools/hpi/its/X.java new file mode 100644 index 0000000000..a39719b66a --- /dev/null +++ b/src/it/jenkins-version/src/main/java/org/jenkinsci/tools/hpi/its/X.java @@ -0,0 +1,2 @@ +package org.jenkinsci.tools.hpi.its; +public class X {} diff --git a/src/it/jenkins-version/src/main/resources/index.jelly b/src/it/jenkins-version/src/main/resources/index.jelly new file mode 100644 index 0000000000..2f655e510a --- /dev/null +++ b/src/it/jenkins-version/src/main/resources/index.jelly @@ -0,0 +1,2 @@ + +
diff --git a/src/it/jenkins-version/verify.groovy b/src/it/jenkins-version/verify.groovy new file mode 100644 index 0000000000..c5c0a5176d --- /dev/null +++ b/src/it/jenkins-version/verify.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +assert new File(basedir, 'build.log').getText('UTF-8').contains('This version of maven-hpi-plugin requires Jenkins 2.204 or later') + +return true diff --git a/src/it/minimum-java-version/invoker.properties b/src/it/minimum-java-version/invoker.properties new file mode 100644 index 0000000000..2208ccc2de --- /dev/null +++ b/src/it/minimum-java-version/invoker.properties @@ -0,0 +1 @@ +invoker.goals=-ntp clean verify diff --git a/src/it/minimum-java-version/pom.xml b/src/it/minimum-java-version/pom.xml new file mode 100644 index 0000000000..32ac09971b --- /dev/null +++ b/src/it/minimum-java-version/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its + minimum-java-version + 1.0-SNAPSHOT + hpi + MyNewPlugin + + 2.249.1 + @project.version@ + + + + + org.jenkins-ci.tools + maven-hpi-plugin + + 8 + + + + + diff --git a/src/it/minimum-java-version/src/main/java/org/jenkinsci/tools/hpi/its/X.java b/src/it/minimum-java-version/src/main/java/org/jenkinsci/tools/hpi/its/X.java new file mode 100644 index 0000000000..a39719b66a --- /dev/null +++ b/src/it/minimum-java-version/src/main/java/org/jenkinsci/tools/hpi/its/X.java @@ -0,0 +1,2 @@ +package org.jenkinsci.tools.hpi.its; +public class X {} diff --git a/src/it/minimum-java-version/src/main/resources/index.jelly b/src/it/minimum-java-version/src/main/resources/index.jelly new file mode 100644 index 0000000000..2f655e510a --- /dev/null +++ b/src/it/minimum-java-version/src/main/resources/index.jelly @@ -0,0 +1,2 @@ + +
diff --git a/src/it/minimum-java-version/verify.groovy b/src/it/minimum-java-version/verify.groovy new file mode 100644 index 0000000000..7269bf3af5 --- /dev/null +++ b/src/it/minimum-java-version/verify.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +assert new File(basedir, 'build.log').getText('UTF-8').contains("Ignoring deprecated minimumJavaVersion parameter. This property should be removed from your plugin's POM. In the future this warning will be changed to an error and will break the build.") + +return true diff --git a/src/it/missing-index/invoker.properties b/src/it/missing-index/invoker.properties new file mode 100644 index 0000000000..a1c7c93753 --- /dev/null +++ b/src/it/missing-index/invoker.properties @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. +# + +invoker.goals=-ntp clean package +invoker.buildResult=failure diff --git a/src/it/missing-index/pom.xml b/src/it/missing-index/pom.xml new file mode 100644 index 0000000000..411917fe15 --- /dev/null +++ b/src/it/missing-index/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its + missing-index + 1.0-SNAPSHOT + hpi + MyNewPlugin + Deprecated spot for plugin description. + + 2.249.1 + @project.version@ + + diff --git a/src/it/missing-index/src/main/java/org/jenkinsci/tools/hpi/its/X.java b/src/it/missing-index/src/main/java/org/jenkinsci/tools/hpi/its/X.java new file mode 100644 index 0000000000..a39719b66a --- /dev/null +++ b/src/it/missing-index/src/main/java/org/jenkinsci/tools/hpi/its/X.java @@ -0,0 +1,2 @@ +package org.jenkinsci.tools.hpi.its; +public class X {} diff --git a/src/it/missing-index/verify.groovy b/src/it/missing-index/verify.groovy new file mode 100644 index 0000000000..767a1ca96c --- /dev/null +++ b/src/it/missing-index/verify.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +assert new File(basedir, 'build.log').getText('UTF-8').contains("create src/main/resources/index.jelly:"); + +return true; diff --git a/src/it/override-test-dependencies-release-failure/invoker.properties b/src/it/override-test-dependencies-release-failure/invoker.properties new file mode 100644 index 0000000000..536d500ccb --- /dev/null +++ b/src/it/override-test-dependencies-release-failure/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals=-ntp -DoverrideVersions=org.jenkins-ci.plugins.workflow:workflow-step-api:2.11 deploy +invoker.buildResult = failure diff --git a/src/it/override-test-dependencies-release-failure/pom.xml b/src/it/override-test-dependencies-release-failure/pom.xml new file mode 100644 index 0000000000..a6f7654a7d --- /dev/null +++ b/src/it/override-test-dependencies-release-failure/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + + org.jenkins-ci.plugins + plugin + 4.40 + + + check-core-version-failure + 1.0-SNAPSHOT + hpi + + 2.249.1 + @project.version@ + + + + org.jenkins-ci.plugins + jackson2-api + 2.13.2-260.v43d711474c77 + + + diff --git a/src/it/override-test-dependencies-release-failure/src/main/java/org/jenkinsci/tools/hpi/its/HelloWorldBuilder.java b/src/it/override-test-dependencies-release-failure/src/main/java/org/jenkinsci/tools/hpi/its/HelloWorldBuilder.java new file mode 100644 index 0000000000..75555635d5 --- /dev/null +++ b/src/it/override-test-dependencies-release-failure/src/main/java/org/jenkinsci/tools/hpi/its/HelloWorldBuilder.java @@ -0,0 +1,144 @@ +package org.jenkinsci.tools.hpi.its; + +import com.fasterxml.jackson.databind.ObjectMapper; +import hudson.Launcher; +import hudson.Extension; +import hudson.util.FormValidation; +import hudson.model.AbstractBuild; +import hudson.model.BuildListener; +import hudson.model.AbstractProject; +import hudson.tasks.Builder; +import hudson.tasks.BuildStepDescriptor; +import java.lang.RuntimeException; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.QueryParameter; + +import javax.servlet.ServletException; +import java.io.IOException; + +/** + * Sample {@link Builder}. + * + *

+ * When the user configures the project and enables this builder, + * {@link org.jenkins.HelloWorldBuilder.DescriptorImpl#newInstance(StaplerRequest)} is invoked + * and a new {@link org.jenkins.HelloWorldBuilder} is created. The created + * instance is persisted to the project configuration XML by using + * XStream, so this allows you to use instance fields (like {@link #name}) + * to remember the configuration. + * + *

+ * When a build is performed, the {@link #perform(AbstractBuild, Launcher, BuildListener)} method + * will be invoked. + * + * @author Kohsuke Kawaguchi + */ +public class HelloWorldBuilder extends Builder { + + private final String name; + + // Fields in config.jelly must match the parameter names in the "DataBoundConstructor" + @DataBoundConstructor + public HelloWorldBuilder(String name) { + this.name = name; + } + + /** + * We'll use this from the {@code config.jelly}. + */ + public String getName() { + return name; + } + + @Override + public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) { + // this is where you 'build' the project + // since this is a dummy, we just say 'hello world' and call that a build + + // this also shows how you can consult the global configuration of the builder + if (getDescriptor().useFrench()) + listener.getLogger().println("Bonjour, " + name + "!"); + else + listener.getLogger().println("Hello, " + name + "!"); + return true; + } + + // overrided for better type safety. + // if your plugin doesn't really define any property on Descriptor, + // you don't have to do this. + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) super.getDescriptor(); + } + + /** + * Descriptor for {@link org.jenkins.HelloWorldBuilder}. Used as a singleton. + * The class is marked as public so that it can be accessed from views. + * + *

+ * See {@code views/hudson/plugins/hello_world/HelloWorldBuilder/*.jelly} + * for the actual HTML fragment for the configuration screen. + */ + @Extension // this marker indicates Hudson that this is an implementation of an extension point. + public static final class DescriptorImpl extends BuildStepDescriptor { + /** + * To persist global configuration information, + * simply store it in a field and call save(). + * + *

+ * If you don't want fields to be persisted, use {@code transient}. + */ + private boolean useFrench; + + /** + * Performs on-the-fly validation of the form field 'name'. + * + * @param value This parameter receives the value that the user has typed. + * @return Indicates the outcome of the validation. This is sent to the browser. + */ + public FormValidation doCheckName(@QueryParameter String value) throws IOException, ServletException { + if (value.length() == 0) + return FormValidation.error("Please set a name"); + if (value.length() < 4) + return FormValidation.warning("Isn't the name too short?"); + return FormValidation.ok(); + } + + public boolean isApplicable(Class aClass) { + // indicates that this builder can be used with all kinds of project types + return true; + } + + /** + * This human readable name is used in the configuration screen. + */ + public String getDisplayName() { + try { + return new ObjectMapper().writeValueAsString("Say hello world"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { + // To persist global configuration information, + // set that to properties and call save(). + useFrench = formData.getBoolean("useFrench"); + // ^Can also use req.bindJSON(this, formData); + // (easier when there are many fields; need set* methods for this, like setUseFrench) + save(); + return super.configure(req, formData); + } + + /** + * This method returns true if the global configuration says we should speak French. + */ + public boolean useFrench() { + return useFrench; + } + } +} + diff --git a/src/it/override-test-dependencies-release-failure/verify.groovy b/src/it/override-test-dependencies-release-failure/verify.groovy new file mode 100644 index 0000000000..72affbede7 --- /dev/null +++ b/src/it/override-test-dependencies-release-failure/verify.groovy @@ -0,0 +1,3 @@ +assert new File(basedir, 'build.log').getText('UTF-8').contains('Cannot override dependencies when doing a release') + +return true diff --git a/src/it/override-test-dependencies-smokes/invoker.properties b/src/it/override-test-dependencies-smokes/invoker.properties new file mode 100644 index 0000000000..384bd1fafb --- /dev/null +++ b/src/it/override-test-dependencies-smokes/invoker.properties @@ -0,0 +1 @@ +invoker.goals=-ntp test diff --git a/src/it/override-test-dependencies-smokes/pom.xml b/src/it/override-test-dependencies-smokes/pom.xml new file mode 100644 index 0000000000..ca03a5e2ab --- /dev/null +++ b/src/it/override-test-dependencies-smokes/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its + override-test-dependencies-smokes + 1.0-SNAPSHOT + hpi + + 2.249 + @project.version@ + + org.jenkins-ci.plugins.workflow:workflow-step-api:2.11,org.jenkins-ci.plugins.workflow:workflow-api:2.17,org.jenkins-ci.plugins.workflow:workflow-cps:2.32 + SampleTest + false + 0 + 2.9 + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + org.jenkins-ci.plugins + structs + 1.6 + + + org.jenkins-ci.plugins.workflow + workflow-step-api + ${workflow-step-api-plugin.version} + + + org.jenkins-ci.plugins.workflow + workflow-step-api + ${workflow-step-api-plugin.version} + tests + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + 2.30 + test + + + antlr + antlr + + + + + diff --git a/src/it/override-test-dependencies-smokes/src/main/resources/index.jelly b/src/it/override-test-dependencies-smokes/src/main/resources/index.jelly new file mode 100644 index 0000000000..2f655e510a --- /dev/null +++ b/src/it/override-test-dependencies-smokes/src/main/resources/index.jelly @@ -0,0 +1,2 @@ + +

diff --git a/src/it/override-test-dependencies-smokes/src/test/java/test/SampleTest.java b/src/it/override-test-dependencies-smokes/src/test/java/test/SampleTest.java new file mode 100644 index 0000000000..a0a94d0658 --- /dev/null +++ b/src/it/override-test-dependencies-smokes/src/test/java/test/SampleTest.java @@ -0,0 +1,42 @@ +package test; + +import com.google.common.collect.ImmutableMap; +import hudson.remoting.Which; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Map; +import java.util.jar.Manifest; +import org.jenkinsci.plugins.workflow.steps.StepConfigTester; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.jvnet.hudson.test.JenkinsRule; + +public class SampleTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void smokes() throws Exception { + Map expectedVersions = ImmutableMap.of("workflow-step-api", "2.11", "workflow-api", "2.17", "workflow-cps", "2.32"); + Enumeration manifests = SampleTest.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); + while (manifests.hasMoreElements()) { + URL url = manifests.nextElement(); + try (InputStream is = url.openStream()) { + Manifest mf = new Manifest(is); + String pluginName = mf.getMainAttributes().getValue("Short-Name"); + String expectedVersion = expectedVersions.get(pluginName); + if (expectedVersion != null) { + assertEquals("wrong version for " + pluginName + " as classpath entry", expectedVersion, mf.getMainAttributes().getValue("Plugin-Version")); + } + } + } + for (Map.Entry entry : expectedVersions.entrySet()) { + assertEquals("wrong version for " + entry.getKey() + " as plugin", entry.getValue(), r.jenkins.pluginManager.getPlugin(entry.getKey()).getVersion()); + } + assertEquals("workflow-step-api-2.11-tests.jar", Which.jarFile(StepConfigTester.class).getName()); + } + +} diff --git a/src/it/override-test-dependencies-smokes/verify.groovy b/src/it/override-test-dependencies-smokes/verify.groovy new file mode 100644 index 0000000000..a4b7f510d8 --- /dev/null +++ b/src/it/override-test-dependencies-smokes/verify.groovy @@ -0,0 +1,3 @@ +def log = new File(basedir, 'build.log').text +// TODO add anything needed, or delete this file +true \ No newline at end of file diff --git a/src/it/override-test-dependencies-useUpperBounds/invoker.properties b/src/it/override-test-dependencies-useUpperBounds/invoker.properties new file mode 100644 index 0000000000..384bd1fafb --- /dev/null +++ b/src/it/override-test-dependencies-useUpperBounds/invoker.properties @@ -0,0 +1 @@ +invoker.goals=-ntp test diff --git a/src/it/override-test-dependencies-useUpperBounds/pom.xml b/src/it/override-test-dependencies-useUpperBounds/pom.xml new file mode 100644 index 0000000000..fb9708b866 --- /dev/null +++ b/src/it/override-test-dependencies-useUpperBounds/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 4.40 + + + org.jenkins-ci.tools.hpi.its + override-test-dependencies-useUpperBounds + 1.0-SNAPSHOT + hpi + + 2.249 + @project.version@ + org.jenkins-ci.plugins.workflow:workflow-cps:2.33 + true + SampleTest + false + 0 + 2.9 + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + org.jenkins-ci.plugins + structs + 1.6 + + + org.jenkins-ci.plugins.workflow + workflow-step-api + ${workflow-step-api-plugin.version} + + + org.jenkins-ci.plugins.workflow + workflow-step-api + ${workflow-step-api-plugin.version} + tests + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + 2.30 + test + + + antlr + antlr + + + + + diff --git a/src/it/override-test-dependencies-useUpperBounds/src/main/resources/index.jelly b/src/it/override-test-dependencies-useUpperBounds/src/main/resources/index.jelly new file mode 100644 index 0000000000..2f655e510a --- /dev/null +++ b/src/it/override-test-dependencies-useUpperBounds/src/main/resources/index.jelly @@ -0,0 +1,2 @@ + +
diff --git a/src/it/override-test-dependencies-useUpperBounds/src/test/java/test/SampleTest.java b/src/it/override-test-dependencies-useUpperBounds/src/test/java/test/SampleTest.java new file mode 100644 index 0000000000..24ad880e7b --- /dev/null +++ b/src/it/override-test-dependencies-useUpperBounds/src/test/java/test/SampleTest.java @@ -0,0 +1,42 @@ +package test; + +import com.google.common.collect.ImmutableMap; +import hudson.remoting.Which; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Map; +import java.util.jar.Manifest; +import org.jenkinsci.plugins.workflow.steps.StepConfigTester; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.jvnet.hudson.test.JenkinsRule; + +public class SampleTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void smokes() throws Exception { + Map expectedVersions = ImmutableMap.of("structs", "1.7", "workflow-step-api", "2.10", "workflow-api", "2.16", "workflow-cps", "2.33"); + Enumeration manifests = SampleTest.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); + while (manifests.hasMoreElements()) { + URL url = manifests.nextElement(); + try (InputStream is = url.openStream()) { + Manifest mf = new Manifest(is); + String pluginName = mf.getMainAttributes().getValue("Short-Name"); + String expectedVersion = expectedVersions.get(pluginName); + if (expectedVersion != null) { + assertEquals("wrong version for " + pluginName + " as classpath entry", expectedVersion, mf.getMainAttributes().getValue("Plugin-Version")); + } + } + } + for (Map.Entry entry : expectedVersions.entrySet()) { + assertEquals("wrong version for " + entry.getKey() + " as plugin", entry.getValue(), r.jenkins.pluginManager.getPlugin(entry.getKey()).getVersion()); + } + assertEquals("workflow-step-api-2.10-tests.jar", Which.jarFile(StepConfigTester.class).getName()); + } + +} diff --git a/src/it/override-test-dependencies-useUpperBounds/verify.groovy b/src/it/override-test-dependencies-useUpperBounds/verify.groovy new file mode 100644 index 0000000000..a4b7f510d8 --- /dev/null +++ b/src/it/override-test-dependencies-useUpperBounds/verify.groovy @@ -0,0 +1,3 @@ +def log = new File(basedir, 'build.log').text +// TODO add anything needed, or delete this file +true \ No newline at end of file diff --git a/src/it/parent-3x/invoker.properties b/src/it/parent-3x/invoker.properties index 6585e7e052..47b870f5b9 100644 --- a/src/it/parent-3x/invoker.properties +++ b/src/it/parent-3x/invoker.properties @@ -1,3 +1,3 @@ # install, not verify, because we want to check the artifact as we would be about to deploy it # release.skipTests normally set in jenkins-release profile since release:perform would do the tests -invoker.goals=-Dstyle.color=always -ntp -Pjenkins-release -Drelease.skipTests=false clean install hpi:run +invoker.goals=-ntp -Pjenkins-release -Drelease.skipTests=false clean install hpi:run diff --git a/src/it/parent-3x/pom.xml b/src/it/parent-3x/pom.xml index 6e80246c42..38fa691236 100644 --- a/src/it/parent-3x/pom.xml +++ b/src/it/parent-3x/pom.xml @@ -5,17 +5,18 @@ org.jenkins-ci.plugins plugin - 3.29 - + 3.57 + org.jenkins-ci.tools.hpi.its parent-3x 1.0-SNAPSHOT hpi - 2.60.3 + 2.249.1 8 @project.version@ + true false @@ -34,7 +35,7 @@ org.jenkins-ci.plugins structs - 1.5 + 308.v852b473a2b8c diff --git a/src/it/parent-3x/verify.groovy b/src/it/parent-3x/verify.groovy index fa4517e675..239c1cdbc2 100644 --- a/src/it/parent-3x/verify.groovy +++ b/src/it/parent-3x/verify.groovy @@ -25,7 +25,7 @@ checkArtifact(installed, 'parent-3x-1.0-SNAPSHOT.hpi', ['WEB-INF/lib/parent-3x.jar'], // TODO still some problems with unwanted transitive JAR dependencies creeping in, e.g. WEB-INF/lib/jboss-marshalling-1.4.9.Final.jar in workflow-multibranch.hpi, or all kinds of junk in parameterized-trigger.hpi ['test/SampleRootAction.class', 'WEB-INF/lib/symbol-annotation-1.5.jar'], - ['Short-Name': 'parent-3x', 'Group-Id': 'org.jenkins-ci.tools.hpi.its', 'Jenkins-Version': '2.60.3' /* Plugin-Version unpredictable for a snapshot */, 'Plugin-Dependencies': 'structs:1.5']) + ['Short-Name': 'parent-3x', 'Group-Id': 'org.jenkins-ci.tools.hpi.its', 'Jenkins-Version': '2.249.1' /* Plugin-Version unpredictable for a snapshot */, 'Plugin-Dependencies': 'structs:308.v852b473a2b8c']) checkArtifact(installed, 'parent-3x-1.0-SNAPSHOT.jar', ['META-INF/annotations/hudson.Extension', 'test/SampleRootAction.class', 'index.jelly'], diff --git a/src/it/process-jar/invoker.properties b/src/it/process-jar/invoker.properties index 3df8a6127f..9b17b269c5 100644 --- a/src/it/process-jar/invoker.properties +++ b/src/it/process-jar/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean verify +invoker.goals=-ntp clean verify diff --git a/src/it/process-jar/pom.xml b/src/it/process-jar/pom.xml index aba4aeca90..62a0e67e01 100644 --- a/src/it/process-jar/pom.xml +++ b/src/it/process-jar/pom.xml @@ -1,3 +1,4 @@ + @@ -6,7 +7,8 @@ org.jenkins-ci.plugins plugin - 3.29 + 4.40 + org.jenkins-ci.tools.hpi.its @@ -16,8 +18,8 @@ hpi - 2.60.3 - 8 + 2.249.1 + @project.version@ MyNewPlugin @@ -26,21 +28,12 @@ com.fasterxml.jackson.core jackson-databind - 2.9.10.7 + 2.13.2.1 true - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - @project.groupId@ @@ -60,7 +53,6 @@ shaded - 8 @@ -91,7 +83,7 @@ org.apache.maven.plugins maven-jarsigner-plugin - 1.4 + 3.0.0 sign diff --git a/src/it/process-jar/verify.groovy b/src/it/process-jar/verify.groovy index 51283cd6bd..0177257994 100644 --- a/src/it/process-jar/verify.groovy +++ b/src/it/process-jar/verify.groovy @@ -25,9 +25,9 @@ assert new File(basedir, 'target/classes/org/jenkinsci/tools/hpi/its/HelloWorldB assert new File(basedir, 'target/classes/org/jenkinsci/tools/hpi/its/HelloWorldBuilder$DescriptorImpl.class').exists(); assert new File(basedir, 'target/classes/org/jenkinsci/tools/hpi/its/HelloWorldBuilder.stapler').exists(); assert new File(basedir, 'target/classes/org/jenkinsci/tools/hpi/its/Messages.class').exists(); -assert !new File(basedir, 'target/process-jar/WEB-INF/lib/jackson-annotations-2.6.0.jar').exists(); -assert !new File(basedir, 'target/process-jar/WEB-INF/lib/jackson-core-2.6.3.jar').exists(); -assert !new File(basedir, 'target/process-jar/WEB-INF/lib/jackson-databind-2.6.3').exists(); +assert !new File(basedir, 'target/process-jar/WEB-INF/lib/jackson-annotations-2.13.2').exists(); +assert !new File(basedir, 'target/process-jar/WEB-INF/lib/jackson-core-2.13.2.jar').exists(); +assert !new File(basedir, 'target/process-jar/WEB-INF/lib/jackson-databind-2.13.2.1.jar').exists(); assert new File(basedir, 'target/process-jar.hpi').exists(); assert new File(basedir, 'target/process-jar.jar').exists(); diff --git a/src/it/snapshot-version-override/invoker.properties b/src/it/snapshot-version-override/invoker.properties index 3df8a6127f..9b17b269c5 100644 --- a/src/it/snapshot-version-override/invoker.properties +++ b/src/it/snapshot-version-override/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean verify +invoker.goals=-ntp clean verify diff --git a/src/it/snapshot-version-override/pom.xml b/src/it/snapshot-version-override/pom.xml index daafdbe3c4..2a903962e9 100644 --- a/src/it/snapshot-version-override/pom.xml +++ b/src/it/snapshot-version-override/pom.xml @@ -1,43 +1,36 @@ + - - 4.0.0 + + 4.0.0 org.jenkins-ci.plugins plugin - 3.29 + 4.40 + - org.jenkins-ci.tools.hpi.its - snapshot-version-override - 1.x-SNAPSHOT + org.jenkins-ci.tools.hpi.its + snapshot-version-override + 1.x-SNAPSHOT - hpi + hpi - MyNewPlugin + MyNewPlugin - 2.60.3 - 8 + 2.249.1 + @project.version@ - scm:git:git://github.com/jenkinsci/maven-hpi-plugin.git - scm:git:ssh://git@github.com/jenkinsci/maven-hpi-plugin.git + scm:git:https://github.com/jenkinsci/maven-hpi-plugin.git + scm:git:git@github.com:jenkinsci/maven-hpi-plugin.git https://github.com/jenkinsci/maven-hpi-plugin HEAD - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - com.github.stephenc.continuous @@ -58,7 +51,6 @@ @project.groupId@ @project.artifactId@ - @project.version@ ${actual-version} diff --git a/src/it/verify-it/invoker.properties b/src/it/verify-it/invoker.properties index 3df8a6127f..9b17b269c5 100644 --- a/src/it/verify-it/invoker.properties +++ b/src/it/verify-it/invoker.properties @@ -17,4 +17,4 @@ # under the License. # -invoker.goals=-Dstyle.color=always -ntp clean verify +invoker.goals=-ntp clean verify diff --git a/src/it/verify-it/pom.xml b/src/it/verify-it/pom.xml index fb25610692..f0687a3251 100644 --- a/src/it/verify-it/pom.xml +++ b/src/it/verify-it/pom.xml @@ -1,12 +1,14 @@ + 4.0.0 - - org.jenkins-ci.plugins - plugin - 3.29 - + + org.jenkins-ci.plugins + plugin + 4.40 + + org.jenkins-ci.tools.hpi.its verify-it @@ -15,29 +17,33 @@ hpi MyNewPlugin + My New Plugin + https://github.com/jenkinsci/verify-it-plugin - - 2.60.3 - 8 - - - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - + + + MIT License + https://opensource.org/licenses/MIT + + + + + nchomsky + Noam Chomsky + nchomsky@example.com + + + + + scm:git:https://github.com/jenkinsci/verify-it-plugin.git + scm:git:git@github.com:jenkinsci/verify-it-plugin.git + HEAD + https://github.com/jenkinsci/verify-it-plugin + + + + 2.249.1 + @project.version@ + diff --git a/src/it/verify-it/verify.groovy b/src/it/verify-it/verify.groovy index 24bc104c41..6fc6509213 100644 --- a/src/it/verify-it/verify.groovy +++ b/src/it/verify-it/verify.groovy @@ -17,6 +17,9 @@ * under the License. */ +import java.nio.file.Files +import java.util.jar.Manifest + assert new File(basedir, 'target/classes').exists(); assert new File(basedir, 'target/classes/org/jenkinsci/tools/hpi/its').exists(); assert new File(basedir, 'target/classes/org/jenkinsci/tools/hpi/its/HelloWorldBuilder.class').exists(); @@ -31,6 +34,30 @@ assert new File(basedir, 'target/generated-sources/localizer/org/jenkinsci/tools content = new File(basedir, 'target/generated-sources/localizer/org/jenkinsci/tools/hpi/its/Messages.java').text; assert content.contains(" holder.format(\"it.msg\");"); +assert new File(basedir, 'target/verify-it/META-INF/MANIFEST.MF').exists() + +Files.newInputStream(new File(basedir, 'target/verify-it/META-INF/MANIFEST.MF').toPath()).withCloseable { is -> + Manifest manifest = new Manifest(is) + assert !manifest.getMainAttributes().getValue('Build-Jdk-Spec').isEmpty() + assert manifest.getMainAttributes().getValue('Created-By').startsWith('Maven Archiver') + assert manifest.getMainAttributes().getValue('Extension-Name') == null // was provided by Maven 2, but core prefers Short-Name + assert manifest.getMainAttributes().getValue('Group-Id').equals('org.jenkins-ci.tools.hpi.its') + assert manifest.getMainAttributes().getValue('Hudson-Version').equals('2.249.1') + assert manifest.getMainAttributes().getValue('Implementation-Title').equals('MyNewPlugin') // was project.artifactId in previous versions, now project.name + assert manifest.getMainAttributes().getValue('Implementation-Version').equals('1.0-SNAPSHOT') + assert manifest.getMainAttributes().getValue('Jenkins-Version').equals('2.249.1') + assert manifest.getMainAttributes().getValue('Long-Name').equals('MyNewPlugin') + assert manifest.getMainAttributes().getValue('Manifest-Version').equals('1.0') + assert manifest.getMainAttributes().getValue('Plugin-Developers').equals('Noam Chomsky:nchomsky:nchomsky@example.com') + assert manifest.getMainAttributes().getValue('Plugin-License-Name').equals('MIT License') + assert manifest.getMainAttributes().getValue('Plugin-License-Url').equals('https://opensource.org/licenses/MIT') + assert manifest.getMainAttributes().getValue('Plugin-ScmUrl').equals('https://github.com/jenkinsci/verify-it-plugin') + assert manifest.getMainAttributes().getValue('Plugin-Version').startsWith('1.0-SNAPSHOT') + assert manifest.getMainAttributes().getValue('Short-Name').equals('verify-it') + assert manifest.getMainAttributes().getValue('Specification-Title').equals('MyNewPlugin') // was project.description in previous versions, now project.name + assert manifest.getMainAttributes().getValue('Url').equals('https://github.com/jenkinsci/verify-it-plugin') +} + // TODO add some test on hpi file content return true; \ No newline at end of file diff --git a/src/main/java/hudson/Extension.java b/src/main/java/hudson/Extension.java index 4b1744a1cb..7269834d4c 100644 --- a/src/main/java/hudson/Extension.java +++ b/src/main/java/hudson/Extension.java @@ -25,13 +25,11 @@ import jenkins.YesNoMaybe; -import static jenkins.YesNoMaybe.MAYBE; - /** * Minimal clone to convince Sezpoz. * * @author Kohsuke Kawaguchi */ public @interface Extension { - YesNoMaybe dynamicLoadable() default MAYBE; + YesNoMaybe dynamicLoadable() default YesNoMaybe.MAYBE; } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractDependencyGraphTraversingMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractDependencyGraphTraversingMojo.java index ce8bdcc152..9556e649df 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractDependencyGraphTraversingMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractDependencyGraphTraversingMojo.java @@ -1,6 +1,8 @@ package org.jenkinsci.maven.plugins.hpi; import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException; import org.apache.maven.shared.dependency.graph.DependencyNode; @@ -16,7 +18,10 @@ public abstract class AbstractDependencyGraphTraversingMojo extends AbstractJenk * Traverses the whole dependency tree rooted at the project. */ protected void traverseProject() throws DependencyGraphBuilderException { - visit(graphBuilder.buildDependencyGraph(project, null)); + ProjectBuildingRequest buildingRequest = + new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); + buildingRequest.setProject(project); + visit(graphBuilder.buildDependencyGraph(buildingRequest, null)); } /** diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractHpiMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractHpiMojo.java index ded67fc44c..54c0f7eb54 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractHpiMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractHpiMojo.java @@ -17,13 +17,12 @@ */ import com.google.common.collect.Sets; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; @@ -31,12 +30,14 @@ import java.io.Writer; import java.net.URL; import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashSet; -import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.TreeSet; @@ -61,6 +62,7 @@ import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.PropertyUtils; import org.codehaus.plexus.util.StringUtils; public abstract class AbstractHpiMojo extends AbstractJenkinsMojo { @@ -129,7 +131,7 @@ public abstract class AbstractHpiMojo extends AbstractJenkinsMojo { * The directory where the webapp is built. */ @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}") - protected File webappDirectory; + private File webappDirectory; /** * Single directory for extra files to include in the WAR. @@ -279,7 +281,7 @@ public void setContainerConfigXML(File containerConfigXML) { * @return an array of tokens to exclude */ protected String[] getExcludes() { - List excludeList = new ArrayList(); + List excludeList = new ArrayList<>(); if (StringUtils.isNotEmpty(warSourceExcludes)) { excludeList.addAll(Arrays.asList(StringUtils.split(warSourceExcludes, ","))); } @@ -299,7 +301,7 @@ protected String[] getExcludes() { * @return an array of tokens to include */ protected String[] getIncludes() { - return StringUtils.split(StringUtils.defaultString(warSourceIncludes), ","); + return StringUtils.split(Objects.toString(warSourceIncludes), ","); } /** @@ -325,20 +327,32 @@ protected String[] getDependentWarExcludes() { * @return an array of tokens to include */ protected String[] getDependentWarIncludes() { - return StringUtils.split(StringUtils.defaultString(dependentWarIncludes), ","); + return StringUtils.split(Objects.toString(dependentWarIncludes), ","); } public void buildExplodedWebapp(File webappDirectory, File jarFile) throws MojoExecutionException { getLog().info("Exploding webapp..."); - webappDirectory.mkdirs(); + try { + Files.createDirectories(webappDirectory.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + webappDirectory + "'", e); + } File webinfDir = new File(webappDirectory, WEB_INF); - webinfDir.mkdirs(); + try { + Files.createDirectories(webinfDir.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + webinfDir + "'", e); + } File metainfDir = new File(webappDirectory, META_INF); - metainfDir.mkdirs(); + try { + Files.createDirectories(metainfDir.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + metainfDir + "'", e); + } try { List webResources = this.webResources != null ? Arrays.asList(this.webResources) : null; @@ -354,12 +368,12 @@ public void buildExplodedWebapp(File webappDirectory, File jarFile) if (containerConfigXML != null && StringUtils.isNotEmpty(containerConfigXML.getName())) { metainfDir = new File(webappDirectory, META_INF); String xmlFileName = containerConfigXML.getName(); - copyFileIfModified(containerConfigXML, new File(metainfDir, xmlFileName)); + FileUtils.copyFileIfModified(containerConfigXML, new File(metainfDir, xmlFileName)); } buildWebapp(project, webappDirectory); - copyFileIfModified(jarFile, new File(getWebappDirectory(),"WEB-INF/lib/"+jarFile.getName())); + FileUtils.copyFileIfModified(jarFile, new File(getWebappDirectory(),"WEB-INF/lib/"+jarFile.getName())); } catch (IOException e) { throw new MojoExecutionException("Could not explode webapp...", e); @@ -376,7 +390,7 @@ private Properties getBuildFilterProperties() for (String filter : filters) { try { - Properties properties = PropertyUtils.loadPropertyFile(new File(filter), true, true); + Properties properties = PropertyUtils.loadProperties(new File(filter)); filterProperties.putAll(properties); } @@ -397,7 +411,6 @@ private Properties getBuildFilterProperties() * * @param resource the resource to copy * @param webappDirectory the target directory - * @param filterProperties * @throws java.io.IOException if an error occurred while copying webResources */ public void copyResources(Resource resource, File webappDirectory, Properties filterProperties) @@ -407,17 +420,13 @@ public void copyResources(Resource resource, File webappDirectory, Properties fi if (webappDirectory.exists()) { String[] fileNames = getWarFiles(resource); for (String fileName : fileNames) { - File targetDirectory = webappDirectory; - if (StringUtils.isNotBlank(resource.getTargetPath())) { - targetDirectory = new File(webappDirectory, resource.getTargetPath()); - } if (resource.isFiltering()) { copyFilteredFile(new File(resource.getDirectory(), fileName), - new File(targetDirectory, fileName), null, getFilterWrappers(), + new File(webappDirectory, fileName), null, getFilterWrappers(), filterProperties); } else { - copyFileIfModified(new File(resource.getDirectory(), fileName), - new File(targetDirectory, fileName)); + FileUtils.copyFileIfModified(new File(resource.getDirectory(), fileName), + new File(webappDirectory, fileName)); } } } @@ -443,7 +452,7 @@ public void copyResources(File sourceDirectory, File webappDirectory) if (warSourceDirectory.exists()) { String[] fileNames = getWarFiles(sourceDirectory); for (String fileName : fileNames) { - copyFileIfModified(new File(sourceDirectory, fileName), + FileUtils.copyFileIfModified(new File(sourceDirectory, fileName), new File(webappDirectory, fileName)); } } @@ -479,7 +488,6 @@ protected Set wrap(Iterable artifacts) { * the {@code webappDirectory} during this phase. * * @param project the maven project - * @param webappDirectory * @throws java.io.IOException if an error occurred while building the webapp */ public void buildWebapp(MavenProject project, File webappDirectory) @@ -497,7 +505,7 @@ public void buildWebapp(MavenProject project, File webappDirectory) List duplicates = findDuplicates(artifacts); - List dependentWarDirectories = new ArrayList(); + List dependentWarDirectories = new ArrayList<>(); // List up IDs of Jenkins plugin dependencies Set jenkinsPlugins = new HashSet<>(); @@ -554,10 +562,10 @@ public void buildWebapp(MavenProject project, File webappDirectory) } String type = artifact.getType(); if ("tld".equals(type)) { - copyFileIfModified(artifact.getFile(), new File(tldDirectory, targetFileName)); + FileUtils.copyFileIfModified(artifact.getFile(), new File(tldDirectory, targetFileName)); } else { if ("jar".equals(type) || "ejb".equals(type) || "ejb-client".equals(type)) { - copyFileIfModified(artifact.getFile(), new File(libDirectory, targetFileName)); + FileUtils.copyFileIfModified(artifact.getFile(), new File(libDirectory, targetFileName)); } else { if ("par".equals(type)) { targetFileName = targetFileName.substring(0, targetFileName.lastIndexOf('.')) + ".jar"; @@ -565,7 +573,7 @@ public void buildWebapp(MavenProject project, File webappDirectory) getLog().debug( "Copying " + artifact.getFile() + " to " + new File(libDirectory, targetFileName)); - copyFileIfModified(artifact.getFile(), new File(libDirectory, targetFileName)); + FileUtils.copyFileIfModified(artifact.getFile(), new File(libDirectory, targetFileName)); } else { if ("war".equals(type)) { dependentWarDirectories.add(unpackWarToTempDirectory(artifact)); @@ -582,8 +590,8 @@ public void buildWebapp(MavenProject project, File webappDirectory) getLog().info("Overlaying " + dependentWarDirectories.size() + " war(s)."); // overlay dependent wars - for (Iterator iter = dependentWarDirectories.iterator(); iter.hasNext();) { - copyDependentWarContents((File) iter.next(), webappDirectory); + for (File dependentWarDirectory : dependentWarDirectories) { + copyDependentWarContents(dependentWarDirectory, webappDirectory); } } } @@ -595,8 +603,8 @@ public void buildWebapp(MavenProject project, File webappDirectory) * @return List of duplicated artifacts */ private List findDuplicates(Set artifacts) { - List duplicates = new ArrayList(); - List identifiers = new ArrayList(); + List duplicates = new ArrayList<>(); + List identifiers = new ArrayList<>(); for (MavenArtifact artifact : artifacts) { String candidate = artifact.getDefaultFinalName(); if (identifiers.contains(candidate)) { @@ -614,7 +622,6 @@ private List findDuplicates(Set artifacts) { * * @param artifact War artifact to unpack. * @return Directory containing the unpacked war. - * @throws MojoExecutionException */ private File unpackWarToTempDirectory(MavenArtifact artifact) throws MojoExecutionException { @@ -622,8 +629,12 @@ private File unpackWarToTempDirectory(MavenArtifact artifact) File tempLocation = new File(workDirectory, name.substring(0, name.length() - 4)); boolean process = false; - if (!tempLocation.exists()) { - tempLocation.mkdirs(); + if (!Files.isDirectory(tempLocation.toPath())) { + try { + Files.createDirectories(tempLocation.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + tempLocation + "'", e); + } process = true; } else if (artifact.getFile().lastModified() > tempLocation.lastModified()) { process = true; @@ -658,9 +669,6 @@ private void unpack(File file, File location) unArchiver.setDestDirectory(location); unArchiver.extract(); } - catch (IOException e) { - throw new MojoExecutionException("Error unpacking file: " + file + "to: " + location, e); - } catch (ArchiverException e) { throw new MojoExecutionException("Error unpacking file: " + file + "to: " + location, e); } @@ -685,17 +693,26 @@ private void copyDependentWarContents(File srcDir, File targetDir) scanner.scan(); for (String dir : scanner.getIncludedDirectories()) { - new File(targetDir, dir).mkdirs(); + File includeDir = new File(targetDir, dir); + try { + Files.createDirectories(includeDir.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + includeDir + "'", e); + } } for (String file : scanner.getIncludedFiles()) { File targetFile = new File(targetDir, file); // Do not overwrite existing files. - if (!targetFile.exists()) { + if (!Files.exists(targetFile.toPath())) { try { - targetFile.getParentFile().mkdirs(); - copyFileIfModified(new File(srcDir, file), targetFile); + Files.createDirectories(targetFile.toPath().getParent()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create parent directories for '" + targetFile + "'", e); + } + try { + FileUtils.copyFileIfModified(new File(srcDir, file), targetFile); } catch (IOException e) { throw new MojoExecutionException("Error copying file '" + file + "' to '" + targetFile + "'", @@ -736,12 +753,12 @@ private String[] getWarFiles(Resource resource) { DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(resource.getDirectory()); if (resource.getIncludes() != null && !resource.getIncludes().isEmpty()) { - scanner.setIncludes((String[]) resource.getIncludes().toArray(EMPTY_STRING_ARRAY)); + scanner.setIncludes(resource.getIncludes().toArray(EMPTY_STRING_ARRAY)); } else { scanner.setIncludes(DEFAULT_INCLUDES); } if (resource.getExcludes() != null && !resource.getExcludes().isEmpty()) { - scanner.setExcludes((String[]) resource.getExcludes().toArray(EMPTY_STRING_ARRAY)); + scanner.setExcludes(resource.getExcludes().toArray(EMPTY_STRING_ARRAY)); } scanner.addDefaultExcludes(); @@ -751,42 +768,18 @@ private String[] getWarFiles(Resource resource) { return scanner.getIncludedFiles(); } - /** - * Copy file from source to destination only if source is newer than the target file. - * If destinationDirectory does not exist, it - * (and any parent directories) will be created. If a file source in - * destinationDirectory exists, it will be overwritten. - * - * @param source An existing File to copy. - * @param destinationDirectory A directory to copy source into. - * @throws java.io.FileNotFoundException if source isn't a normal file. - * @throws IllegalArgumentException if destinationDirectory isn't a directory. - * @throws java.io.IOException if source does not exist, the file in - * destinationDirectory cannot be written to, or an IO error occurs during copying. - *

- * TO DO: Remove this method when Maven moves to plexus-utils version 1.4 - */ - private static void copyFileToDirectoryIfModified(File source, File destinationDirectory) - throws IOException { - // TO DO: Remove this method and use the method in WarFileUtils when Maven 2 changes - // to plexus-utils 1.2. - if (destinationDirectory.exists() && !destinationDirectory.isDirectory()) { - throw new IllegalArgumentException("Destination is not a directory"); - } - - copyFileIfModified(source, new File(destinationDirectory, source.getName())); - } - private FilterWrapper[] getFilterWrappers() { return new FilterWrapper[]{ // support ${token} new FilterWrapper() { + @Override public Reader getReader(Reader fileReader, Properties filterProperties) { return new InterpolationFilterReader(fileReader, filterProperties, "${", "}"); } }, // support @token@ new FilterWrapper() { + @Override public Reader getReader(Reader fileReader, Properties filterProperties) { return new InterpolationFilterReader(fileReader, filterProperties, "@", "@"); } @@ -794,11 +787,6 @@ public Reader getReader(Reader fileReader, Properties filterProperties) { } /** - * @param from - * @param to - * @param encoding - * @param wrappers - * @param filterProperties * @throws IOException TO DO: Remove this method when Maven moves to plexus-utils version 1.4 */ private static void copyFilteredFile(File from, File to, String encoding, FilterWrapper[] wrappers, @@ -809,11 +797,11 @@ private static void copyFilteredFile(File from, File to, String encoding, Filter Writer fileWriter = null; try { // fix for MWAR-36, ensures that the parent dir are created first - to.getParentFile().mkdirs(); + Files.createDirectories(to.toPath().getParent()); if (encoding == null || encoding.length() < 1) { - fileReader = new BufferedReader(new FileReader(from)); - fileWriter = new FileWriter(to); + fileReader = Files.newBufferedReader(from.toPath(), StandardCharsets.UTF_8); + fileWriter = Files.newBufferedWriter(to.toPath(), StandardCharsets.UTF_8); } else { FileInputStream instream = new FileInputStream(from); @@ -837,74 +825,6 @@ private static void copyFilteredFile(File from, File to, String encoding, Filter } } - /** - * Copy file from source to destination only if source timestamp is later than the destination timestamp. - * The directories up to destination will be created if they don't already exist. - * destination will be overwritten if it already exists. - * - * @param source An existing non-directory File to copy bytes from. - * @param destination A non-directory File to write bytes to (possibly - * overwriting). - * @throws IOException if source does not exist, destination cannot be - * written to, or an IO error occurs during copying. - * @throws java.io.FileNotFoundException if destination is a directory - *

- * TO DO: Remove this method when Maven moves to plexus-utils version 1.4 - */ - private static void copyFileIfModified(File source, File destination) - throws IOException { - // TO DO: Remove this method and use the method in WarFileUtils when Maven 2 changes - // to plexus-utils 1.2. - if (destination.lastModified() < source.lastModified()) { - FileUtils.copyFile(source, destination); - } - } - - /** - * Copies a entire directory structure but only source files with timestamp later than the destinations'. - *

- * Note: - *

    - *
  • It will include empty directories. - *
  • The sourceDirectory must exists. - *
- * - * @param sourceDirectory - * @param destinationDirectory - * @throws IOException TO DO: Remove this method when Maven moves to plexus-utils version 1.4 - */ - private static void copyDirectoryStructureIfModified(File sourceDirectory, File destinationDirectory) - throws IOException { - if (!sourceDirectory.exists()) { - throw new IOException("Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")."); - } - - String sourcePath = sourceDirectory.getAbsolutePath(); - - for (File file : sourceDirectory.listFiles()) { - String dest = file.getAbsolutePath(); - - dest = dest.substring(sourcePath.length() + 1); - - File destination = new File(destinationDirectory, dest); - - if (file.isFile()) { - destination = destination.getParentFile(); - - copyFileToDirectoryIfModified(file, destination); - } else if (file.isDirectory()) { - if (!destination.exists() && !destination.mkdirs()) { - throw new IOException( - "Could not create destination directory '" + destination.getAbsolutePath() + "'."); - } - - copyDirectoryStructureIfModified(file, destination); - } else { - throw new IOException("Unknown file type: " + file.getAbsolutePath()); - } - } - } - /** * If the project is on Git, figure out Git SHA1. * @@ -921,17 +841,17 @@ public String getGitHeadSha1() { } try { - Process p = new ProcessBuilder("git", "rev-parse", "HEAD").redirectErrorStream(true).start(); + Process p = new ProcessBuilder("git", "-C", git.getAbsolutePath(), "rev-parse", "HEAD").redirectErrorStream(true).start(); p.getOutputStream().close(); String v = IOUtils.toString(p.getInputStream()).trim(); if (p.waitFor()!=0) return null; // git rev-parse failed to run - return v.trim().substring(0,8); - } catch (IOException e) { - LOGGER.log(Level.FINE, "Failed to run git rev-parse HEAD",e); - return null; - } catch (InterruptedException e) { + if (v.length()<8) + return null; // git repository present, but without commits + + return v.substring(0,8); + } catch (IOException | InterruptedException e) { LOGGER.log(Level.FINE, "Failed to run git rev-parse HEAD",e); return null; } @@ -950,6 +870,7 @@ private interface FilterWrapper { * False, if the answer is known to be "No". Otherwise null, if there are some extensions * we don't know we can dynamic load. Otherwise, if everything is known to be dynamic loadable, return true. */ + @SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "TODO needs triage") protected Boolean isSupportDynamicLoading() throws IOException { URLClassLoader cl = new URLClassLoader(new URL[]{ new File(project.getBuild().getOutputDirectory()).toURI().toURL() diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsManifestMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsManifestMojo.java index a62e112acf..79f0d201f9 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsManifestMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsManifestMojo.java @@ -15,6 +15,7 @@ */ package org.jenkinsci.maven.plugins.hpi; +import org.apache.maven.archiver.ManifestConfiguration; import org.apache.maven.archiver.MavenArchiveConfiguration; import org.apache.maven.archiver.MavenArchiver; import org.apache.maven.artifact.Artifact; @@ -26,18 +27,19 @@ import org.apache.maven.plugins.annotations.Parameter; import org.codehaus.plexus.archiver.jar.Manifest; import org.codehaus.plexus.archiver.jar.ManifestException; -import org.codehaus.plexus.util.IOUtil; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.text.SimpleDateFormat; -import java.util.Collection; import java.util.Date; import java.util.List; import java.util.logging.Level; @@ -59,6 +61,7 @@ public abstract class AbstractJenkinsManifestMojo extends AbstractHpiMojo { /** * Optional - the oldest version of this plugin which the current version is * configuration-compatible with. + * @see Mark a new plugin version as incompatible with older versions */ @Parameter(property = "hpi.compatibleSinceVersion") private String compatibleSinceVersion; @@ -71,8 +74,11 @@ public abstract class AbstractJenkinsManifestMojo extends AbstractHpiMojo { /** * Specify the minimum version of Java that this plugin requires. + * + * @deprecated removed without replacement */ - @Parameter(required = true) + @Deprecated + @Parameter protected String minimumJavaVersion; /** @@ -80,42 +86,43 @@ public abstract class AbstractJenkinsManifestMojo extends AbstractHpiMojo { */ protected void generateManifest(MavenArchiveConfiguration archive, File manifestFile) throws MojoExecutionException { // create directory if it doesn't exist yet - if (!manifestFile.getParentFile().exists()) - manifestFile.getParentFile().mkdirs(); + if (!Files.isDirectory(manifestFile.toPath().getParent())) { + try { + Files.createDirectories(manifestFile.toPath().getParent()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create parent directories for '" + manifestFile + "'", e); + } + } getLog().info("Generating " + manifestFile); MavenArchiver ma = new MavenArchiver(); ma.setOutputFile(manifestFile); - PrintWriter printWriter = null; - try { - Manifest mf = ma.getManifest(project, archive.getManifest()); - Manifest.Section mainSection = mf.getMainSection(); + try (PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(manifestFile), StandardCharsets.UTF_8))) { + ManifestConfiguration config = archive.getManifest(); + config.setAddDefaultSpecificationEntries(true); + config.setAddDefaultImplementationEntries(true); + Manifest mf = ma.getManifest(project, config); + Manifest.ExistingSection mainSection = mf.getMainSection(); setAttributes(mainSection); - printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(manifestFile), "UTF-8")); mf.write(printWriter); - } catch (ManifestException e) { - throw new MojoExecutionException("Error preparing the manifest: " + e.getMessage(), e); - } catch (DependencyResolutionRequiredException e) { + } catch (ManifestException | IOException | DependencyResolutionRequiredException e) { throw new MojoExecutionException("Error preparing the manifest: " + e.getMessage(), e); - } catch (IOException e) { - throw new MojoExecutionException("Error preparing the manifest: " + e.getMessage(), e); - } finally { - IOUtil.close(printWriter); } } - protected void setAttributes(Manifest.Section mainSection) throws MojoExecutionException, ManifestException, IOException { + protected void setAttributes(Manifest.ExistingSection mainSection) throws MojoExecutionException, ManifestException, IOException { File pluginImpl = new File(project.getBuild().getOutputDirectory(), "META-INF/services/hudson.Plugin"); if(pluginImpl.exists()) { - BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(pluginImpl),"UTF-8")); + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(pluginImpl), StandardCharsets.UTF_8)); String pluginClassName = in.readLine(); in.close(); mainSection.addAttributeAndCheck(new Manifest.Attribute("Plugin-Class",pluginClassName)); } + mainSection.addAttributeAndCheck(new Manifest.Attribute("Group-Id",project.getGroupId())); mainSection.addAttributeAndCheck(new Manifest.Attribute("Short-Name",project.getArtifactId())); mainSection.addAttributeAndCheck(new Manifest.Attribute("Long-Name",pluginName)); @@ -126,20 +133,11 @@ protected void setAttributes(Manifest.Section mainSection) throws MojoExecutionE if (compatibleSinceVersion!=null) mainSection.addAttributeAndCheck(new Manifest.Attribute("Compatible-Since-Version", compatibleSinceVersion)); - if (this.minimumJavaVersion == null) { - throw new MojoExecutionException("minimumJavaVersion attribute must be set starting from version 2.8"); - } - try { - int res = Integer.parseInt(this.minimumJavaVersion); - LOGGER.log(Level.INFO, "Minimum Java version for the plugin: {0}", this.minimumJavaVersion); - } catch(NumberFormatException ex) { - if (this.minimumJavaVersion.equals("1.6") || this.minimumJavaVersion.equals("1.7") || this.minimumJavaVersion.equals("1.8")) { - // okay - } else { - throw new MojoExecutionException("Unsupported Java version string: `" + this.minimumJavaVersion + "`. If you use Java 9 or above, see https://openjdk.java.net/jeps/223"); - } + if (this.minimumJavaVersion != null && !this.minimumJavaVersion.isEmpty()) { + getLog().warn("Ignoring deprecated minimumJavaVersion parameter." + + " This property should be removed from your plugin's POM." + + " In the future this warning will be changed to an error and will break the build."); } - mainSection.addAttributeAndCheck(new Manifest.Attribute("Minimum-Java-Version", this.minimumJavaVersion)); if (sandboxStatus!=null) mainSection.addAttributeAndCheck(new Manifest.Attribute("Sandbox-Status", sandboxStatus)); @@ -238,7 +236,7 @@ private String findDependencyPlugins() throws IOException, MojoExecutionExceptio // check any "provided" scope plugin dependencies that are probably not what the user intended. // see http://jenkins-ci.361315.n4.nabble.com/Classloading-problem-when-referencing-classes-from-another-plugin-during-the-initialization-phase-of-td394967.html - for (Artifact a : (Collection)project.getDependencyArtifacts()) + for (Artifact a : project.getDependencyArtifacts()) if ("provided".equals(a.getScope()) && wrap(a).isPlugin()) throw new MojoExecutionException(a.getId()+" is marked as 'provided' scope dependency, but it should be the 'compile' scope."); @@ -252,8 +250,7 @@ private String findDependencyPlugins() throws IOException, MojoExecutionExceptio private String getDevelopersForManifest() throws IOException { StringBuilder buf = new StringBuilder(); - for (Object o : project.getDevelopers()) { - Developer d = (Developer) o; + for (Developer d : project.getDevelopers()) { if (buf.length() > 0) { buf.append(','); } @@ -268,15 +265,12 @@ private String getDevelopersForManifest() throws IOException { } protected Manifest loadManifest(File f) throws IOException, ManifestException { - InputStreamReader r = new InputStreamReader(new FileInputStream(f), "UTF-8"); - try { - return new Manifest(r); - } finally { - IOUtil.close(r); + try (InputStream is = Files.newInputStream(f.toPath())) { + return new Manifest(is); } } - private void addLicenseAttributesForManifest(Manifest.Section target) throws ManifestException { + private void addLicenseAttributesForManifest(Manifest.ExistingSection target) throws ManifestException { final List licenses = project.getLicenses(); int licenseCounter = 1; for (License lic : licenses) { @@ -298,14 +292,14 @@ private String getScmUrl() { return null; } - private void addAttributeIfNotNull(Manifest.Section target, String attributeName, String propertyValue) + private void addAttributeIfNotNull(Manifest.ExistingSection target, String attributeName, String propertyValue) throws ManifestException { if (propertyValue != null) { target.addAttributeAndCheck(new Manifest.Attribute(attributeName, propertyValue)); } } - private void addPropertyAttributeIfNotNull(Manifest.Section target, String attributeName, String propertyName) + private void addPropertyAttributeIfNotNull(Manifest.ExistingSection target, String attributeName, String propertyName) throws ManifestException { String propertyValue = project.getProperties().getProperty(propertyName); if (propertyValue != null) { diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsMojo.java index ea8cbd15fc..bfd62f83dd 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJenkinsMojo.java @@ -1,22 +1,27 @@ package org.jenkinsci.maven.plugins.hpi; import hudson.util.VersionNumber; -import org.apache.commons.lang.StringUtils; +import io.jenkins.lib.versionnumber.JavaSpecificationVersion; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; -import org.apache.maven.artifact.metadata.ArtifactMetadataSource; -import org.apache.maven.artifact.repository.ArtifactRepository; -import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; -import org.apache.maven.project.MavenProjectBuilder; import org.apache.maven.project.MavenProjectHelper; - -import java.util.List; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; /** * Mojos that need to figure out the Jenkins version it's working with. @@ -28,9 +33,12 @@ public abstract class AbstractJenkinsMojo extends AbstractMojo { /** * The maven project. */ - @Component + @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; + @Parameter(defaultValue = "${session}", required = true, readonly = true) + protected MavenSession session; + /** * Optional string that represents "groupId:artifactId" of Jenkins core jar. * If left unspecified, the default groupId/artifactId pair for Jenkins is looked for. @@ -48,20 +56,6 @@ public abstract class AbstractJenkinsMojo extends AbstractMojo { @Parameter private String jenkinsCoreVersionOverride; - - /** - * List of Remote Repositories used by the resolver - */ - @Parameter(defaultValue = "${project.remoteArtifactRepositories}",readonly = true, required = true) - protected List remoteRepos; - - @Component - @Parameter(defaultValue = "${localRepository}", readonly = true, required = true) - protected ArtifactRepository localRepository; - - @Component - protected ArtifactMetadataSource artifactMetadataSource; - @Component protected ArtifactFactory artifactFactory; @@ -69,14 +63,14 @@ public abstract class AbstractJenkinsMojo extends AbstractMojo { protected ArtifactResolver artifactResolver; @Component - protected MavenProjectBuilder projectBuilder; + protected ProjectBuilder projectBuilder; @Component protected MavenProjectHelper projectHelper; protected String findJenkinsVersion() throws MojoExecutionException { - for(Dependency a : (List)project.getDependencies()) { + for(Dependency a : project.getDependencies()) { boolean match; if (jenkinsCoreId!=null) match = (a.getGroupId()+':'+a.getArtifactId()).equals(jenkinsCoreId); @@ -85,7 +79,7 @@ protected String findJenkinsVersion() throws MojoExecutionException { && (a.getArtifactId().equals("jenkins-core") || a.getArtifactId().equals("hudson-core")); if (match) { - if (StringUtils.isNotBlank(jenkinsCoreVersionOverride)) { + if (jenkinsCoreVersionOverride != null && !jenkinsCoreVersionOverride.trim().isEmpty()) { VersionNumber v1 = new VersionNumber(a.getVersion()); VersionNumber v2 = new VersionNumber(jenkinsCoreVersionOverride); if (v1.compareTo(v2) == -1) { @@ -98,13 +92,60 @@ protected String findJenkinsVersion() throws MojoExecutionException { return a.getVersion(); } } - if (StringUtils.isNotBlank(jenkinsCoreVersionOverride)) { + if (jenkinsCoreVersionOverride != null && !jenkinsCoreVersionOverride.trim().isEmpty()) { return jenkinsCoreVersionOverride; } throw new MojoExecutionException("Failed to determine Jenkins version this plugin depends on."); } + protected JavaSpecificationVersion getMinimumJavaVersion() throws MojoExecutionException { + Artifact core = resolveJenkinsCore(); + File jar = wrap(core).getFile(); + try (JarFile jarFile = new JarFile(jar)) { + ZipEntry entry = jarFile.getEntry("jenkins/model/Jenkins.class"); + if (entry == null) { + throw new MojoExecutionException("Failed to find Jenkins.class in " + jar); + } + try (InputStream is = jarFile.getInputStream(entry); DataInputStream dis = new DataInputStream(is)) { + int magic = dis.readInt(); + if (magic != 0xcafebabe) { + throw new MojoExecutionException("Jenkins.class is not a valid class file in " + jar); + } + dis.readUnsignedShort(); // discard minor version + return JavaSpecificationVersion.fromClassVersion(dis.readUnsignedShort()); + } + } catch (IOException e) { + throw new MojoExecutionException("Failed to read minimum Java version from " + jar, e); + } + } + + private Artifact resolveJenkinsCore() throws MojoExecutionException { + DefaultArtifactCoordinate artifactCoordinate = new DefaultArtifactCoordinate(); + if (jenkinsCoreId != null) { + String[] parts = jenkinsCoreId.split(":"); + artifactCoordinate.setGroupId(parts[0]); + artifactCoordinate.setArtifactId(parts[1]); + } else { + artifactCoordinate.setGroupId("org.jenkins-ci.main"); + artifactCoordinate.setArtifactId("jenkins-core"); + } + artifactCoordinate.setVersion(findJenkinsVersion()); + + try { + return artifactResolver + .resolveArtifact(session.getProjectBuildingRequest(), artifactCoordinate) + .getArtifact(); + } catch (ArtifactResolverException e) { + throw new MojoExecutionException("Couldn't download artifact: ", e); + } + } + protected MavenArtifact wrap(Artifact a) { - return new MavenArtifact(a,artifactResolver,artifactFactory,projectBuilder,remoteRepos,localRepository); + return new MavenArtifact( + a, + artifactResolver, + artifactFactory, + projectBuilder, + session); } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJettyMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJettyMojo.java index 962a9fe04a..6ea31a2bcf 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJettyMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/AbstractJettyMojo.java @@ -26,6 +26,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; @@ -38,7 +39,6 @@ import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.FileUtils; @@ -51,9 +51,7 @@ import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.NCSARequestLog; -import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.RequestLog; -import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ShutdownMonitor; import org.eclipse.jetty.server.handler.ContextHandler; @@ -233,7 +231,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo /** * The maven project. */ - @Component + @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; @@ -241,7 +239,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo * The artifacts for the project. */ @Parameter(defaultValue = "${project.artifacts}") - protected Set projectArtifacts; + protected Set projectArtifacts; @Parameter(defaultValue = "${mojoExecution}", readonly = true) @@ -252,7 +250,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo * The artifacts for the plugin itself. */ @Parameter(defaultValue = "${plugin.artifacts}", readonly = true) - protected List pluginArtifacts; + protected List pluginArtifacts; @@ -323,6 +321,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo /** * @see org.apache.maven.plugin.Mojo#execute() */ + @Override public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("Configuring Jetty for project: " + this.project.getName()); @@ -348,9 +347,6 @@ public void execute() throws MojoExecutionException, MojoFailureException - /** - * @throws MojoExecutionException - */ public void configurePluginClasspath() throws MojoExecutionException { //if we are configured to include the provided dependencies on the plugin's classpath @@ -361,12 +357,10 @@ public void configurePluginClasspath() throws MojoExecutionException { try { - List provided = new ArrayList(); - URL[] urls = null; + List provided = new ArrayList<>(); + URL[] urls; - for ( Iterator iter = projectArtifacts.iterator(); iter.hasNext(); ) - { - Artifact artifact = iter.next(); + for (Artifact artifact : projectArtifacts) { if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope()) && !isPluginArtifact(artifact)) { provided.add(artifact.getFile().toURI().toURL()); @@ -393,10 +387,6 @@ public void configurePluginClasspath() throws MojoExecutionException - /** - * @param artifact - * @return - */ public boolean isPluginArtifact(Artifact artifact) { if (pluginArtifacts == null || pluginArtifacts.isEmpty()) @@ -417,14 +407,11 @@ public boolean isPluginArtifact(Artifact artifact) - /** - * @throws Exception - */ public void finishConfigurationBeforeStart() throws Exception { - HandlerCollection contexts = (HandlerCollection)server.getChildHandlerByClass(ContextHandlerCollection.class); + HandlerCollection contexts = server.getChildHandlerByClass(ContextHandlerCollection.class); if (contexts==null) - contexts = (HandlerCollection)server.getChildHandlerByClass(HandlerCollection.class); + contexts = server.getChildHandlerByClass(HandlerCollection.class); for (int i=0; (this.contextHandlers != null) && (i < this.contextHandlers.length); i++) { @@ -435,9 +422,6 @@ public void finishConfigurationBeforeStart() throws Exception - /** - * @throws Exception - */ public void applyJettyXml() throws Exception { if (getJettyXmlFiles() == null) @@ -448,9 +432,6 @@ public void applyJettyXml() throws Exception - /** - * @throws MojoExecutionException - */ public void startJetty () throws MojoExecutionException { try @@ -579,8 +560,6 @@ public void configureMonitor() /** * Subclasses should invoke this to setup basic info * on the webapp - * - * @throws MojoExecutionException */ public void configureWebApplication () throws Exception { @@ -612,8 +591,9 @@ public void configureWebApplication () throws Exception { File target = new File(project.getBuild().getDirectory()); File tmp = new File(target, "jetty"); - if (!tmp.exists()) - tmp.mkdirs(); + if (!Files.isDirectory(tmp.toPath())) { + Files.createDirectories(tmp.toPath()); + } webApp.setTempDirectory(tmp); } @@ -687,10 +667,7 @@ protected void printSystemProperties () { if (systemProperties != null) { - Iterator itor = systemProperties.getSystemProperties().iterator(); - while (itor.hasNext()) - { - SystemProperty prop = (SystemProperty)itor.next(); + for (SystemProperty prop : systemProperties.getSystemProperties()) { getLog().debug("Property "+prop.getName()+"="+prop.getValue()+" was "+ (prop.isSet() ? "set" : "skipped")); } } @@ -703,7 +680,6 @@ protected void printSystemProperties () /** * Try and find a jetty-web.xml file, using some * historical naming conventions if necessary. - * @param webInfDir * @return the jetty web xml file */ public File findJettyWebXmlFile (File webInfDir) @@ -728,10 +704,6 @@ public File findJettyWebXmlFile (File webInfDir) - /** - * @param file - * @throws Exception - */ public void setSystemPropertiesFile(File file) throws Exception { this.systemPropertiesFile = file; @@ -760,9 +732,6 @@ public void setSystemPropertiesFile(File file) throws Exception - /** - * @param systemProperties - */ public void setSystemProperties(SystemProperties systemProperties) { if (this.systemProperties == null) @@ -782,9 +751,6 @@ public void setSystemProperties(SystemProperties systemProperties) - /** - * @return - */ public List getJettyXmlFiles() { if ( this.jettyXml == null ) @@ -792,7 +758,7 @@ public List getJettyXmlFiles() return null; } - List jettyXmlFiles = new ArrayList(); + List jettyXmlFiles = new ArrayList<>(); if ( this.jettyXml.indexOf(',') == -1 ) { @@ -813,10 +779,6 @@ public List getJettyXmlFiles() - /** - * @param goal - * @return - */ public boolean isExcluded (String goal) { if (excludedGoals == null || goal == null) diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/Artifacts.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/Artifacts.java index c51ddddbdc..1a34bb8f72 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/Artifacts.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/Artifacts.java @@ -1,14 +1,13 @@ package org.jenkinsci.maven.plugins.hpi; -import com.google.common.base.Predicate; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.MavenProject; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; import java.util.List; +import java.util.function.Predicate; /** * Collection filter operations on a set of {@link Artifact}s. @@ -37,90 +36,52 @@ public static Artifacts ofDirectDependencies(MavenProject p) { } public Artifacts retainAll(Predicate filter) { - for (Iterator itr = iterator(); itr.hasNext(); ) { - if (!filter.apply(itr.next())) - itr.remove(); - } + removeIf(artifact -> !filter.test(artifact)); return this; } public Artifacts removeAll(Predicate filter) { - for (Iterator itr = iterator(); itr.hasNext(); ) { - if (filter.apply(itr.next())) - itr.remove(); - } + removeIf(filter); return this; } public Artifacts scopeIs(String... scopes) { final List s = Arrays.asList(scopes); - return retainAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getScope()); - } - }); + return retainAll(a -> s.contains(a.getScope())); } public Artifacts scopeIsNot(String... scopes) { final List s = Arrays.asList(scopes); - return removeAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getScope()); - } - }); + return removeAll(a -> s.contains(a.getScope())); } public Artifacts typeIs(String... type) { final List s = Arrays.asList(type); - return retainAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getType()); - } - }); + return retainAll(a -> s.contains(a.getType())); } public Artifacts typeIsNot(String... type) { final List s = Arrays.asList(type); - return removeAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getType()); - } - }); + return removeAll(a -> s.contains(a.getType())); } public Artifacts groupIdIs(String... groupId) { final List s = Arrays.asList(groupId); - return retainAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getType()); - } - }); + return retainAll(a -> s.contains(a.getType())); } public Artifacts groupIdIsNot(String... groupId) { final List s = Arrays.asList(groupId); - return removeAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getType()); - } - }); + return removeAll(a -> s.contains(a.getType())); } public Artifacts artifactIdIs(String... artifactId) { final List s = Arrays.asList(artifactId); - return retainAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getType()); - } - }); + return retainAll(a -> s.contains(a.getType())); } public Artifacts artifactIdIsNot(String... artifactId) { final List s = Arrays.asList(artifactId); - return removeAll(new Predicate() { - public boolean apply(Artifact a) { - return s.contains(a.getType()); - } - }); + return removeAll(a -> s.contains(a.getType())); } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/AssembleDependenciesMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/AssembleDependenciesMojo.java index 2110c2f3a9..58f0797c79 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/AssembleDependenciesMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/AssembleDependenciesMojo.java @@ -58,7 +58,7 @@ public class AssembleDependenciesMojo extends AbstractDependencyGraphTraversingM private List parsedScopes; - private final Map hpis = new HashMap(); + private final Map hpis = new HashMap<>(); @Override protected boolean accept(DependencyNode g) { @@ -85,11 +85,12 @@ protected boolean accept(DependencyNode g) { } + @Override public void execute() throws MojoExecutionException, MojoFailureException { try { hpis.clear(); - parsedScopes = new ArrayList(); + parsedScopes = new ArrayList<>(); parsedScopes.add(null); // this is needed to traverse the root node for (String s : scopes.split(",")) { parsedScopes.add(s.trim()); diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/BundlePluginsMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/BundlePluginsMojo.java deleted file mode 100644 index 309626089d..0000000000 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/BundlePluginsMojo.java +++ /dev/null @@ -1,296 +0,0 @@ -package org.jenkinsci.maven.plugins.hpi; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.resolver.ArtifactCollector; -import org.apache.maven.artifact.resolver.ArtifactNotFoundException; -import org.apache.maven.artifact.resolver.ArtifactResolutionException; -import org.apache.maven.artifact.resolver.ArtifactResolutionResult; -import org.apache.maven.artifact.resolver.ArtifactResolver; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; -import org.apache.maven.model.Dependency; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; -import org.apache.maven.project.ProjectBuildingException; -import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter; -import org.apache.maven.shared.artifact.filter.collection.TypeFilter; -import org.codehaus.plexus.util.FileUtils; - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Take the current project, list up all the transitive dependencies, then copy them - * into a specified directory. - * - *

- * Used to assemble {@code jenkins.war} by bundling all the necessary plugins. - * - * @author Stephen Connolly - */ -@Mojo(name = "bundle-plugins", - defaultPhase = LifecyclePhase.PREPARE_PACKAGE, - requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, - requiresProject = true, - threadSafe = true -) -public class BundlePluginsMojo extends AbstractJenkinsMojo { - /** - * Where to copy plugins into. - */ - @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}/WEB-INF/plugins/") - private File outputDirectory; - - /** - * Where to copy optional plugins into. - * @see optional-plugin-helper-module - */ - @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}/WEB-INF/optional-plugins/") - private File optionalOutputDirectory; - - /** - * By default the build will fail if one of the bundled plugins has an optional dependency on a newer version - * of another bundled plugin. - */ - @Parameter(property = "hpi.ignoreOptionalDependencyConflicts") - private boolean ignoreOptionalDependencyConflicts; - - @Component - protected ArtifactResolver resolver; - - @Component - protected ArtifactCollector artifactCollector; - - - public void execute() throws MojoExecutionException, MojoFailureException { - Set badDependencies = new LinkedHashSet(); - for (Artifact a: (Set)project.getDependencyArtifacts()) { - try { - if (StringUtils.isBlank(a.getType()) || StringUtils.equals("jar", a.getType()) && wrap(a).isPlugin()) { - final String gav = String.format("%s:%s:%s", a.getGroupId(), a.getArtifactId(), a.getVersion()); - getLog().error(String.format("Dependency on plugin %s does not include tag", gav)); - badDependencies.add(gav); - } - } catch (IOException e) { - throw new MojoExecutionException("Failed to check plugin dependencies", e); - } - } - if (!badDependencies.isEmpty()) { - throw new MojoFailureException( - "The following plugin dependencies are missing the tag required by the bundle-plugins " - + "goal:\n " + StringUtils.join(badDependencies, "\n ")); - } - TypeFilter typeFilter = new TypeFilter("hpi,jpi", null); - // the HPI packaging type is brain-dead... since nobody lists plugin dependencies with hpi - // we loose all transitive information, so we need to throw away all the good stuff maven would give us - // further we need to set provided in order to keep this off the classpath as then - // the war plugin would suck them in anyways - Set artifacts = typeFilter.filter(project.getDependencyArtifacts()); - artifacts = typeFilter.filter(artifacts); - Set nonOptionalArtifacts = new OptionalFilter(false).filter(artifacts); - try { - // This would be unnecessary and trivial if the hpi packaging was defined correctly - ArtifactResolutionResult r = resolver.resolveTransitively(artifacts, project.getArtifact(), remoteRepos, localRepository, artifactMetadataSource); - ArtifactResolutionResult noR = resolver.resolveTransitively(nonOptionalArtifacts, project.getArtifact(), remoteRepos, localRepository, artifactMetadataSource); - - List hpis = new ArrayList(); - - - // resolveTransitively resolves items in the 'all' set individually, - // so there will be duplicates and multiple versions - // we need to select the latest - Map selected = new HashMap(); - - // first we want to take all the dependencies as a complete set - - for (Artifact o : (Set)r.getArtifacts()) { - MavenArtifact a = wrap(o); - if (a.isPlugin()) { - MavenArtifact cur = selected.get(a.getArtifactId()); - if (cur != null) { - if (cur.getVersionNumber().compareTo(a.getVersionNumber()) < 0) { - cur = a; - } - } else { - cur = a; - } - selected.put(a.getArtifactId(), cur); - } - } - - // now just take the non-optional ones (these may have a higher minimum versions when we exclude the optional ones) - // (due to the ordering effect of dependencies when resolving conflicts in case you were wondering) - - Set nonOptional = new HashSet(); - for (Artifact o: (Set)noR.getArtifacts()) { - MavenArtifact a = wrap(o); - if (a.isPlugin()) { - nonOptional.add(o.getArtifactId()); - MavenArtifact cur = selected.get(a.getArtifactId()); - if (cur != null) { - if (cur.getVersionNumber().compareTo(a.getVersionNumber()) < 0) { - cur = a; - } - } else { - cur = a; - } - selected.put(a.getArtifactId(), cur); - } - } - - // now we just need to check if the optional dependencies have issues - // in theory we do not need to fail the build if a transitive dependency has issues - // we could just up-version it, but that would put us back to the start in terms of dependency resolution - // so much simpler to force the user to explicitly resolve the conflict by adding an explicit - // dependency - - Set optionalDependencyIssue = new LinkedHashSet(); - for (MavenArtifact a: selected.values()) { - try { - final MavenProject pom = a.resolvePom(); - for (Dependency d : (List)pom.getDependencies()) { - if (!d.isOptional()) continue; - if (!selected.containsKey(d.getArtifactId())) continue; - MavenArtifact matching = selected.get(d.getArtifactId()); - if (!StringUtils.equals(d.getGroupId(), matching.getGroupId())) continue; - if (matching.getVersionNumber().compareTo(new DefaultArtifactVersion(d.getVersion())) < 0) { - final String message = String.format( - "%s: optional dependency of %s version %s conflicts with the bundled version %s", - a.getArtifactId(), d.getArtifactId(), d.getVersion(), matching.getVersion()); - getLog().error(message); - optionalDependencyIssue.add(message); - } - } - } catch (ProjectBuildingException e) { - getLog().warn(String.format("Could not resolve pom of %s:%s:%s to check optional dependencies", - a.getGroupId(), a.getArtifactId(), a.getVersion()), e); - } - } - if (!optionalDependencyIssue.isEmpty()) { - if (ignoreOptionalDependencyConflicts) { - getLog().warn("Ignoring optional dependency conflicts"); - } else { - throw new MojoFailureException( - "Optional dependencies are incompatible with bundled dependencies:\n " + StringUtils - .join(optionalDependencyIssue, "\n ")); - } - } - - outputDirectory.mkdirs(); - if (!nonOptional.containsAll(selected.keySet())) { - optionalOutputDirectory.mkdirs(); - } - - int artifactIdLength = "Artifact ID".length(); // how many chars does it take to print artifactId? - int groupIdLength = "Group ID".length(); // how many chars does it take to print groupId? - int versionLength = "Version".length(); - for (MavenArtifact a : selected.values()) { - MavenArtifact hpi = a.getHpi(); - getLog().debug("Copying " + hpi.getFile()); - - - FileUtils.copyFile(hpi.getFile(), - new File(nonOptional.contains(hpi.getArtifactId()) ? outputDirectory : optionalOutputDirectory, - hpi.getArtifactId() + ".hpi") - ); - hpis.add(hpi); - artifactIdLength = Math.max(artifactIdLength, hpi.getArtifactId().length()); - groupIdLength = Math.max(groupIdLength, hpi.getGroupId().length()); - versionLength = Math.max(versionLength, hpi.getVersion().length()); - } - - Collections.sort(hpis, new Comparator() { - public int compare(MavenArtifact o1, MavenArtifact o2) { - return map(o1).compareTo(map(o2)); - } - - private String map(MavenArtifact a) { - return a.getArtifactId(); - } - }); - - File list = new File(project.getBuild().getOutputDirectory(), "bundled-plugins.txt"); - File manifest = new File(project.getBuild().getDirectory(), "plugin-manifest.txt"); - list.getParentFile().mkdirs(); - PrintWriter w = new PrintWriter(list); - PrintWriter m = new PrintWriter(manifest); - try { - final String format = "%" + (-artifactIdLength) + "s %" + (-versionLength) + "s %-8s %"+(-groupIdLength)+"s"; - final String format2 = "%"+(-groupIdLength)+"s %" + (-artifactIdLength) + "s %" + (-versionLength) + "s %-8s%n"; - getLog().info(String.format(format, - "Artifact ID", - "Version", - "Optional", - "Group ID")); - m.printf(format2, "Group Id", "Artifact Id", "Version", "Optional"); - m.printf(format2, StringUtils.repeat("=",groupIdLength), StringUtils.repeat("=",artifactIdLength), StringUtils.repeat("=",versionLength), StringUtils.repeat("=",8)); - getLog().info(String.format(format, - StringUtils.repeat("=", artifactIdLength), - StringUtils.repeat("=", versionLength), - StringUtils.repeat("=", 8), - StringUtils.repeat("=", groupIdLength))); - for (MavenArtifact hpi : hpis) { - getLog().info(String.format(format, - hpi.getArtifactId(), - hpi.getVersion(), - nonOptional.contains(hpi.getArtifactId()) ? "" : "optional", - hpi.getGroupId())); - m.printf(format2, - hpi.getGroupId(), - hpi.getArtifactId(), - hpi.getVersion(), - nonOptional.contains(hpi.getArtifactId()) ? "no" : "yes" - ); - w.println(hpi.getId()); - } - } finally { - IOUtils.closeQuietly(w); - IOUtils.closeQuietly(m); - } - - projectHelper.attachArtifact(project, "txt", "bundled-plugins", list); - projectHelper.attachArtifact(project, "txt", "plugin-manifest", manifest); - } catch (ArtifactResolutionException e) { - throw new MojoExecutionException("Failed to resolve plugin dependencies", e); - } catch (ArtifactNotFoundException e) { - throw new MojoExecutionException("Failed to resolve plugin dependencies", e); - } catch (IOException e) { - throw new MojoExecutionException("Failed to resolve plugin dependencies", e); - } - } - - private static class OptionalFilter extends AbstractArtifactsFilter { - private final boolean optional; - - private OptionalFilter(boolean optional) { - this.optional = optional; - } - - public Set filter(Set artifacts) { - Set result = new LinkedHashSet(); - for (Artifact a: (Set)artifacts) { - if (optional == a.isOptional()) { - result.add(a); - } - } - return result; - } - } -} diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/CollectionUtils.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/CollectionUtils.java deleted file mode 100644 index 282779e376..0000000000 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/CollectionUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jenkinsci.maven.plugins.hpi; - -import java.util.Enumeration; -import java.util.NoSuchElementException; - -/** - * Taken from jdk7 Collections - */ -class CollectionUtils { - - public static Enumeration emptyEnumeration() { - return (Enumeration) EmptyEnumeration.EMPTY_ENUMERATION; - } - - private static class EmptyEnumeration implements Enumeration { - static final EmptyEnumeration EMPTY_ENUMERATION = new EmptyEnumeration(); - - public boolean hasMoreElements() { return false; } - public E nextElement() { throw new NoSuchElementException(); } - } -} diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/ConsoleScanner.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/ConsoleScanner.java index e2a6ad84f7..7565c7f903 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/ConsoleScanner.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/ConsoleScanner.java @@ -37,9 +37,6 @@ public class ConsoleScanner extends Thread - /** - * @param mojo - */ public ConsoleScanner(AbstractJettyMojo mojo) { this.mojo = mojo; @@ -53,6 +50,7 @@ public ConsoleScanner(AbstractJettyMojo mojo) /** * @see java.lang.Thread#run() */ + @Override public void run() { try @@ -90,9 +88,6 @@ private void getSomeSleep() - /** - * @throws IOException - */ private void checkSystemInput() throws IOException { while (System.in.available() > 0) { diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/CreateMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/CreateMojo.java deleted file mode 100644 index 2fca1de54a..0000000000 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/CreateMojo.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2001-2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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.jenkinsci.maven.plugins.hpi; - -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.Mojo; - -@Mojo(name="create", requiresProject = false) -public class CreateMojo extends AbstractMojo { - - @Override - public void execute() throws MojoExecutionException { - throw new MojoExecutionException("Unimplemented!\nhpi:create is obsolete. Instead use:\n====\nmvn archetype:generate -Dfilter=io.jenkins.archetypes:\n====\n"); - } - -} diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/HpiMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/HpiMojo.java index 2cba97706a..bc8e8fc97e 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/HpiMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/HpiMojo.java @@ -18,13 +18,13 @@ import java.io.File; import java.io.IOException; -import java.util.List; -import org.apache.commons.lang.StringUtils; +import java.util.Objects; import org.apache.maven.archiver.MavenArchiveConfiguration; import org.apache.maven.archiver.MavenArchiver; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; @@ -89,31 +89,20 @@ protected File getOutputFile(String extension) { * * @throws MojoExecutionException if an error occurred while building the webapp */ - public void execute() throws MojoExecutionException { + @Override + public void execute() throws MojoExecutionException, MojoFailureException { try { performPackaging(); - } catch (DependencyResolutionRequiredException e) { - throw new MojoExecutionException("Error assembling hpi: " + e.getMessage(), e); - } catch (ManifestException e) { - throw new MojoExecutionException("Error assembling hpi", e); - } catch (IOException e) { - throw new MojoExecutionException("Error assembling hpi", e); - } catch (ArchiverException e) { + } catch (IOException | ArchiverException | ManifestException | DependencyResolutionRequiredException e) { throw new MojoExecutionException("Error assembling hpi: " + e.getMessage(), e); } } /** * Generates the webapp according to the {@code mode} attribute. - * - * @throws IOException - * @throws ArchiverException - * @throws ManifestException - * @throws DependencyResolutionRequiredException - * */ private void performPackaging() - throws IOException, ArchiverException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException { + throws IOException, ArchiverException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException, MojoFailureException { // generate a manifest File manifestFile = new File(getWebappDirectory(), "META-INF/MANIFEST.MF"); @@ -121,16 +110,16 @@ private void performPackaging() Manifest manifest = loadManifest(manifestFile); getLog().info("Checking for attached .jar artifact " - + (StringUtils.isBlank(jarClassifier) ? "..." : "with classifier " + jarClassifier + "...")); + + (jarClassifier == null || jarClassifier.trim().isEmpty() ? "..." : "with classifier " + jarClassifier + "...")); File jarFile = null; - for (Artifact artifact: (List)project.getAttachedArtifacts()) { - if (StringUtils.equals(project.getGroupId(), artifact.getGroupId()) - && StringUtils.equals(project.getArtifactId(), artifact.getArtifactId()) + for (Artifact artifact: project.getAttachedArtifacts()) { + if (Objects.equals(project.getGroupId(), artifact.getGroupId()) + && Objects.equals(project.getArtifactId(), artifact.getArtifactId()) && project.getArtifact().getVersionRange().equals(artifact.getVersionRange()) - && StringUtils.equals("jar", artifact.getType()) - && (StringUtils.isBlank(jarClassifier) + && Objects.equals("jar", artifact.getType()) + && (jarClassifier == null || jarClassifier.trim().isEmpty() ? !artifact.hasClassifier() - : StringUtils.equals(jarClassifier, artifact.getClassifier())) + : Objects.equals(jarClassifier, artifact.getClassifier())) && artifact.getFile() != null && artifact.getFile().isFile()) { jarFile = artifact.getFile(); getLog().info("Found attached .jar artifact: " + jarFile.getAbsolutePath()); @@ -145,8 +134,16 @@ private void performPackaging() archiver.setArchiver(jarArchiver); archiver.setOutputFile(jarFile); jarArchiver.addConfiguredManifest(manifest); + File indexJelly = new File(getClassesDirectory(), "index.jelly"); + if (!indexJelly.isFile()) { + throw new MojoFailureException("Missing " + indexJelly + ". Delete any from pom.xml and create src/main/resources/index.jelly:\n" + + "\n" + + "
\n" + + " The description here…\n" + + "
"); + } jarArchiver.addDirectory(getClassesDirectory()); - archiver.createArchive(project, archive); + archiver.createArchive(session, project, archive); } // HACK Alert... due to how this plugin hacks the maven dependency model (by using a dependency on the // jar file and then rewriting them for hpi projects) we need to add the jar as an attached artifact @@ -172,7 +169,7 @@ private void performPackaging() hpiArchiver.addDirectory(getWebappDirectory(), getIncludes(), getExcludes()); // create archive - archiver.createArchive(project, archive); + archiver.createArchive(session, project, archive); project.getArtifact().setFile(hpiFile); } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/HplMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/HplMojo.java index 7c909c53dc..0a21703c15 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/HplMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/HplMojo.java @@ -1,6 +1,5 @@ package org.jenkinsci.maven.plugins.hpi; -import org.apache.commons.lang.StringUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; import org.apache.maven.model.Resource; @@ -11,14 +10,13 @@ import org.apache.maven.plugins.annotations.ResolutionScope; import org.codehaus.plexus.archiver.jar.Manifest; import org.codehaus.plexus.archiver.jar.Manifest.Attribute; -import org.codehaus.plexus.archiver.jar.Manifest.Section; import org.codehaus.plexus.archiver.jar.ManifestException; -import org.codehaus.plexus.util.IOUtil; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -56,6 +54,7 @@ public void setJenkinsHome(File jenkinsHome) { this.jenkinsHome = jenkinsHome; } + @Override public void execute() throws MojoExecutionException, MojoFailureException { if(!project.getPackaging().equals("hpi")) { getLog().info("Skipping "+project.getName()+" because it's not hpi"); @@ -70,18 +69,17 @@ public void execute() throws MojoExecutionException, MojoFailureException { File hplFile = computeHplFile(); getLog().info("Generating "+hplFile); - PrintWriter printWriter = null; - try { + try (PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(hplFile.toPath(), StandardCharsets.UTF_8))) { Manifest mf = new Manifest(); - Section mainSection = mf.getMainSection(); + Manifest.ExistingSection mainSection = mf.getMainSection(); setAttributes(mainSection); // compute Libraries entry - List paths = new ArrayList(); + List paths = new ArrayList<>(); // we want resources to be picked up before target/classes, // so that the original (not in the copy) will be picked up first. - for (Resource r : (List) project.getBuild().getResources()) { + for (Resource r : project.getBuild().getResources()) { File dir = new File(r.getDirectory()); if (!dir.isAbsolute()) dir = new File(project.getBasedir(),r.getDirectory()); @@ -94,24 +92,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { buildLibraries(paths); - mainSection.addAttributeAndCheck(new Attribute("Libraries", StringUtils.join(paths, ","))); + mainSection.addAttributeAndCheck(new Attribute("Libraries", String.join(",", paths))); // compute Resource-Path entry - if (webappDirectory != null && webappDirectory.isDirectory()) { - mainSection.addAttributeAndCheck(new Attribute("Resource-Path",webappDirectory.getAbsolutePath())); - } else { - getLog().info("webappDirectory does not exist, will use warSourceDirectory"); - mainSection.addAttributeAndCheck(new Attribute("Resource-Path",warSourceDirectory.getAbsolutePath())); - } + mainSection.addAttributeAndCheck(new Attribute("Resource-Path", warSourceDirectory.getAbsolutePath())); - printWriter = new PrintWriter(new FileWriter(hplFile)); mf.write(printWriter); - } catch (ManifestException e) { - throw new MojoExecutionException("Error preparing the manifest: " + e.getMessage(), e); - } catch (IOException e) { + } catch (ManifestException | IOException e) { throw new MojoExecutionException("Error preparing the manifest: " + e.getMessage(), e); - } finally { - IOUtil.close(printWriter); } } @@ -127,7 +115,7 @@ private void buildLibraries(List paths) throws IOException { Set artifacts = getProjectArtfacts(); // List up IDs of Jenkins plugin dependencies - Set jenkinsPlugins = new HashSet(); + Set jenkinsPlugins = new HashSet<>(); for (MavenArtifact artifact : artifacts) { if (artifact.isPluginBestEffort(getLog())) jenkinsPlugins.add(artifact.getId()); @@ -137,6 +125,10 @@ private void buildLibraries(List paths) throws IOException { for (MavenArtifact artifact : artifacts) { if(jenkinsPlugins.contains(artifact.getId())) continue; // plugin dependencies + if (artifact.getDependencyTrail().size() < 2) { + throw new IllegalStateException( + "invalid dependency trail: " + artifact.getDependencyTrail()); + } if(artifact.getDependencyTrail().size() >= 1 && jenkinsPlugins.contains(artifact.getDependencyTrail().get(1))) continue; // no need to have transitive dependencies through plugins diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/InitializeMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/InitializeMojo.java new file mode 100644 index 0000000000..bd452ef18d --- /dev/null +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/InitializeMojo.java @@ -0,0 +1,73 @@ +package org.jenkinsci.maven.plugins.hpi; + +import hudson.util.VersionNumber; +import io.jenkins.lib.versionnumber.JavaSpecificationVersion; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; + +/** + * Configure Maven for the desired version of Java. + * + * @author Basil Crow + */ +@Mojo(name = "initialize", defaultPhase = LifecyclePhase.INITIALIZE) +public class InitializeMojo extends AbstractJenkinsMojo { + + @Override + public void execute() throws MojoExecutionException { + setCompilerProperties(); + } + + private void setCompilerProperties() throws MojoExecutionException { + if (!project.getProperties().containsKey("maven.compiler.source") + && !project.getProperties().containsKey("maven.compiler.release")) { + // On an older plugin parent POM that predates the setting of these values as Maven properties. + return; + } + + JavaSpecificationVersion javaVersion = getMinimumJavaVersion(); + if (JavaSpecificationVersion.forCurrentJVM().isOlderThan(new VersionNumber("9"))) { + // Should always be set already, but just in case... + setProperty("maven.compiler.source", javaVersion.toString()); + setProperty("maven.compiler.target", javaVersion.toString()); + setProperty("maven.compiler.testSource", javaVersion.toString()); + setProperty("maven.compiler.testTarget", javaVersion.toString()); + // Should never be set already, but just in case... + unsetProperty("maven.compiler.release"); + unsetProperty("maven.compiler.testRelease"); + } else { + /* + * When compiling with a Java 9+ compiler, we always rely on "release" in favor of "source" and "target", + * even when compiling to Java 8 bytecode. + */ + setProperty("maven.compiler.release", Integer.toString(javaVersion.toReleaseVersion())); + setProperty("maven.compiler.testRelease", Integer.toString(javaVersion.toReleaseVersion())); + + /* + * While it does not hurt to have these set to the Java specification version, it is also not needed when + * "release" is in use. + */ + unsetProperty("maven.compiler.source"); + unsetProperty("maven.compiler.target"); + unsetProperty("maven.compiler.testSource"); + unsetProperty("maven.compiler.testTarget"); + } + } + + private void setProperty(String key, String value) { + String currentValue = project.getProperties().getProperty(key); + if (currentValue == null || !currentValue.equals(value)) { + getLog().info("Setting " + key + " to " + value); + project.getProperties().setProperty(key, value); + } + } + + private void unsetProperty(String key) { + String currentValue = project.getProperties().getProperty(key); + if (currentValue != null && !currentValue.isEmpty()) { + getLog().info("Unsetting " + key); + project.getProperties().remove(key); + } + } +} diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/JarMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/JarMojo.java index 3ce09fe7c0..db4a178d8e 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/JarMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/JarMojo.java @@ -17,12 +17,7 @@ */ import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; import org.apache.maven.archiver.MavenArchiveConfiguration; import org.apache.maven.archiver.MavenArchiver; import org.apache.maven.artifact.DependencyResolutionRequiredException; @@ -36,9 +31,7 @@ import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.jar.Manifest; -import org.codehaus.plexus.archiver.jar.Manifest.Section; import org.codehaus.plexus.archiver.jar.ManifestException; -import org.codehaus.plexus.util.IOUtil; /** * Build a jar separate from the hpi goal. If you do not use this goal then the {@link HpiMojo} will generate a @@ -87,28 +80,17 @@ protected File getOutputFile(String extension) { * * @throws MojoExecutionException if an error occurred while building the webapp */ + @Override public void execute() throws MojoExecutionException { try { performPackaging(); - } catch (DependencyResolutionRequiredException e) { - throw new MojoExecutionException("Error assembling jar: " + e.getMessage(), e); - } catch (ManifestException e) { - throw new MojoExecutionException("Error assembling jar", e); - } catch (IOException e) { - throw new MojoExecutionException("Error assembling jar", e); - } catch (ArchiverException e) { + } catch (IOException | ArchiverException | ManifestException | DependencyResolutionRequiredException e) { throw new MojoExecutionException("Error assembling jar: " + e.getMessage(), e); } } /** * Generates the webapp according to the {@code mode} attribute. - * - * @throws IOException - * @throws ArchiverException - * @throws ManifestException - * @throws DependencyResolutionRequiredException - * */ private void performPackaging() throws IOException, ArchiverException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException { @@ -125,7 +107,7 @@ private void performPackaging() archiver.setOutputFile(jarFile); jarArchiver.addConfiguredManifest(manifest); jarArchiver.addDirectory(getClassesDirectory()); - archiver.createArchive(project,archive); + archiver.createArchive(session, project, archive); projectHelper.attachArtifact(project, "jar", jarClassifier, jarFile); } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/JettyAndServletApiOnlyClassLoader.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/JettyAndServletApiOnlyClassLoader.java index 046cbd5515..4f01cd8fa2 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/JettyAndServletApiOnlyClassLoader.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/JettyAndServletApiOnlyClassLoader.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.net.URL; +import java.util.Collections; import java.util.Enumeration; /** @@ -26,7 +27,7 @@ public Enumeration getResources(String name) throws IOException { if (name.equals("jndi.properties")) { return jettyClassLoader.getResources(name); } - return CollectionUtils.emptyEnumeration(); + return Collections.emptyEnumeration(); } @Override diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/ListPluginDependenciesMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/ListPluginDependenciesMojo.java index 3f86a650a8..04bb63c5d6 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/ListPluginDependenciesMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/ListPluginDependenciesMojo.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; /** * List up all plugin dependencies declared in the project. @@ -30,9 +31,10 @@ public class ListPluginDependenciesMojo extends AbstractHpiMojo { // TODO(oleg_nenashev): Add support for transitive plugin dependencies. // Might require reusing/refactoring the plugin dependency tree resolution code in plugin installation mojos. + @Override public void execute() throws MojoExecutionException, MojoFailureException { try { - Writer w = outputFile==null ? new NullWriter() : new OutputStreamWriter(new FileOutputStream(outputFile),"UTF-8"); + Writer w = outputFile==null ? new NullWriter() : new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8); for (MavenArtifact a : getDirectDependencyArtfacts()) { if(!a.isPlugin()) diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/MavenArtifact.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/MavenArtifact.java index e5b4c0cfbb..215ee4dab4 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/MavenArtifact.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/MavenArtifact.java @@ -3,23 +3,25 @@ import hudson.util.VersionNumber; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; -import org.apache.maven.artifact.repository.ArtifactRepository; -import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; -import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.DefaultProjectBuildingRequest; import org.apache.maven.project.MavenProject; -import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuilder; import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.Objects; import java.util.jar.JarFile; -import org.apache.commons.lang.StringUtils; - -import static org.apache.maven.artifact.Artifact.*; /** * {@link Artifact} is a bare data structure without any behavior and therefore @@ -31,24 +33,30 @@ */ public class MavenArtifact implements Comparable { public final ArtifactFactory artifactFactory; - public final MavenProjectBuilder builder; - public final List remoteRepositories; - public final ArtifactRepository localRepository; + public final ProjectBuilder builder; public final Artifact artifact; public final ArtifactResolver resolver; + public final MavenSession session; - public MavenArtifact(Artifact artifact, ArtifactResolver resolver, ArtifactFactory artifactFactory, MavenProjectBuilder builder, List remoteRepositories, ArtifactRepository localRepository) { + public MavenArtifact( + Artifact artifact, + ArtifactResolver resolver, + ArtifactFactory artifactFactory, + ProjectBuilder builder, + MavenSession session) { this.artifact = artifact; this.resolver = resolver; this.artifactFactory = artifactFactory; this.builder = builder; - this.remoteRepositories = remoteRepositories; - remoteRepositories.size(); // null check - this.localRepository = localRepository; + this.session = Objects.requireNonNull(session); } public MavenProject resolvePom() throws ProjectBuildingException { - return builder.buildFromRepository(artifact,remoteRepositories,localRepository); + ProjectBuildingRequest buildingRequest = + new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); + buildingRequest.setProcessPlugins(false); // improve performance + buildingRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + return builder.build(artifact, buildingRequest).getProject(); } /** @@ -86,8 +94,19 @@ public String getId() { * @return converted filename of the artifact */ public String getDefaultFinalName() { - return artifact.getArtifactId() + "-" + artifact.getVersion() + "." + - artifact.getArtifactHandler().getExtension(); + StringBuilder path = new StringBuilder(); + path.append(artifact.getArtifactId()); + path.append('-'); + path.append(artifact.getVersion()); + if (artifact.getClassifier() != null && !artifact.getClassifier().isEmpty()) { + path.append('-'); + path.append(artifact.getClassifier()); + } + if (!artifact.getArtifactHandler().getExtension().isEmpty()) { + path.append('.'); + path.append(artifact.getArtifactHandler().getExtension()); + } + return path.toString(); } public boolean isOptional() { @@ -104,8 +123,8 @@ public String getType() { public File getFile() { if (artifact.getFile()==null) try { - resolver.resolve(artifact, remoteRepositories, localRepository); - } catch (AbstractArtifactResolutionException e) { + return resolver.resolveArtifact(session.getProjectBuildingRequest(), artifact).getArtifact().getFile(); + } catch (ArtifactResolverException e) { throw new RuntimeException("Failed to resolve "+getId(),e); } return artifact.getFile(); @@ -116,8 +135,13 @@ public File getFile() { */ public MavenArtifact getHpi() throws IOException { Artifact a = artifactFactory - .createArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), SCOPE_COMPILE, getResolvedType()); - return new MavenArtifact(a,resolver,artifactFactory,builder,remoteRepositories,localRepository); + .createArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), Artifact.SCOPE_COMPILE, getResolvedType()); + return new MavenArtifact( + a, + resolver, + artifactFactory, + builder, + session); } public List getDependencyTrail() { @@ -159,6 +183,13 @@ public String getClassifier() { return artifact.getClassifier(); } + /** + * Get the artifact handler. + */ + public ArtifactHandler getArtifactHandler() { + return artifact.getArtifactHandler(); + } + /** For a plugin artifact, unlike {@link #getArtifactId} this parses the plugin manifest. */ public String getActualArtifactId() throws IOException { File file = getFile(); @@ -221,14 +252,14 @@ private String getResolvedType() throws IOException { return type; } // also ignore core-assets, tests, etc. - if (!StringUtils.isEmpty(artifact.getClassifier())) { + if (artifact.getClassifier() != null && !artifact.getClassifier().isEmpty()) { return type; } // when a plugin depends on another plugin, it doesn't specify the type as hpi or jpi, so we need to resolve its POM to see it return resolvePom().getPackaging(); } catch (ProjectBuildingException e) { - throw new IOException("Failed to open artifact " + artifact.toString() + " at " + artifact.getFile() + ": " + e, e); + throw new IOException("Failed to open artifact " + artifact + " at " + artifact.getFile() + ": " + e, e); } } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/PluginWorkspaceMapImpl.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/PluginWorkspaceMapImpl.java index 9605ed739a..4fa919d3a2 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/PluginWorkspaceMapImpl.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/PluginWorkspaceMapImpl.java @@ -1,5 +1,7 @@ package org.jenkinsci.maven.plugins.hpi; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import org.codehaus.plexus.component.annotations.Component; import java.io.File; @@ -32,17 +34,15 @@ public PluginWorkspaceMapImpl() { private Properties loadMap() throws IOException { Properties p = new Properties(); if (mapFile.isFile()) { - InputStream is = new FileInputStream(mapFile); - try { + try (InputStream is = new FileInputStream(mapFile)) { p.load(is); - } finally { - is.close(); } } return p; } @Override + @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "TODO needs triage") public /*@CheckForNull*/ File read(String id) throws IOException { for (Map.Entry entry : loadMap().entrySet()) { if (entry.getValue().equals(id)) { @@ -60,11 +60,8 @@ private Properties loadMap() throws IOException { public void write(String id, File f) throws IOException { Properties p = loadMap(); p.setProperty(f.getAbsolutePath(), id); - OutputStream os = new FileOutputStream(mapFile); - try { + try (OutputStream os = new FileOutputStream(mapFile)) { p.store(os, " List of development files for Jenkins plugins that have been built."); - } finally { - os.close(); } } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/PropertyUtils.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/PropertyUtils.java deleted file mode 100644 index 59f74ce80b..0000000000 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/PropertyUtils.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.jenkinsci.maven.plugins.hpi; - -/* - * Copyright 2001-2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -import org.codehaus.plexus.util.IOUtil; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Properties; - -/** - * @author Kenney Westerhof - * @version $Id: PropertyUtils.java 10807 2008-07-14 18:56:05Z btosabre $ - */ -// TODO this is duplicated from the resources plugin-migrate to plexus-utils -public final class PropertyUtils { - private PropertyUtils() { - // prevent instantiation - } - - /** - * Reads a property file, resolving all internal variables. - * - * @param propfile The property file to load - * @param fail whether to throw an exception when the file cannot be loaded or to return null - * @param useSystemProps whether to incorporate System.getProperties settings into the returned Properties object. - * @return the loaded and fully resolved Properties object - */ - public static Properties loadPropertyFile(File propfile, boolean fail, boolean useSystemProps) - throws IOException { - Properties props = new Properties(); - - if (useSystemProps) { - props = new Properties(System.getProperties()); - } - - if (propfile.exists()) { - FileInputStream inStream = new FileInputStream(propfile); - try { - props.load(inStream); - } - finally { - IOUtil.close(inStream); - } - } else if (fail) { - throw new FileNotFoundException(propfile.toString()); - } - - for (Enumeration n = props.propertyNames(); n.hasMoreElements();) { - String k = (String) n.nextElement(); - props.setProperty(k, PropertyUtils.getPropertyValue(k, props)); - } - - return props; - } - - - /** - * Retrieves a property value, replacing values like ${token} - * using the Properties to look them up. - *

- * It will leave unresolved properties alone, trying for System - * properties, and implements reparsing (in the case that - * the value of a property contains a key), and will - * not loop endlessly on a pair like - * test = ${test}. - */ - private static String getPropertyValue(String k, Properties p) { - // This can also be done using InterpolationFilterReader, - // but it requires reparsing the file over and over until - // it doesn't change. - - String v = p.getProperty(k); - String ret = ""; - int idx, idx2; - - while ((idx = v.indexOf("${")) >= 0) { - // append prefix to result - ret += v.substring(0, idx); - - // strip prefix from original - v = v.substring(idx + 2); - - // if no matching } then bail - if ((idx2 = v.indexOf('}')) < 0) { - break; - } - - // strip out the key and resolve it - // resolve the key/value for the ${statement} - String nk = v.substring(0, idx2); - v = v.substring(idx2 + 1); - String nv = p.getProperty(nk); - - // try global environment.. - if (nv == null) { - nv = System.getProperty(nk); - } - - // if the key cannot be resolved, - // leave it alone ( and don't parse again ) - // else prefix the original string with the - // resolved property ( so it can be parsed further ) - // taking recursion into account. - if (nv == null || nv.equals(k)) { - ret += "${" + nk + "}"; - } else { - v = nv + v; - } - } - return ret + v; - } -} diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/RecordCoreLocationMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/RecordCoreLocationMojo.java index 3d5b7c1db4..ccaa26c0b0 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/RecordCoreLocationMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/RecordCoreLocationMojo.java @@ -23,6 +23,7 @@ import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; /** @@ -31,7 +32,7 @@ @Mojo(name="record-core-location", defaultPhase=LifecyclePhase.PACKAGE) public class RecordCoreLocationMojo extends AbstractMojo { - @Component + @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; @Component diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/ReservedName.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/ReservedName.java index 9634039930..eef2737885 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/ReservedName.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/ReservedName.java @@ -8,7 +8,7 @@ * @author Kohsuke Kawaguchi */ class ReservedName { - static Set NAMES = new HashSet(Arrays.asList( + static Set NAMES = new HashSet<>(Arrays.asList( "abstract", "assert", "boolean", diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/RunMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/RunMojo.java index 11e9c238cc..d82291e39f 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/RunMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/RunMojo.java @@ -17,18 +17,13 @@ import hudson.util.VersionNumber; import io.jenkins.lib.support_log_formatter.SupportLogFormatter; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; -import org.apache.maven.artifact.metadata.ArtifactMetadataSource; -import org.apache.maven.artifact.repository.ArtifactRepository; -import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; -import org.apache.maven.artifact.resolver.ArtifactNotFoundException; -import org.apache.maven.artifact.resolver.ArtifactResolutionException; -import org.apache.maven.artifact.resolver.ArtifactResolutionResult; -import org.apache.maven.artifact.resolver.ArtifactResolver; -import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.LifecycleDependencyResolver; import org.apache.maven.model.Resource; +import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; @@ -37,8 +32,17 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.DefaultDependencyResolutionRequest; +import org.apache.maven.project.DependencyResolutionException; +import org.apache.maven.project.DependencyResolutionRequest; +import org.apache.maven.project.DependencyResolutionResult; import org.apache.maven.project.MavenProject; -import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectDependenciesResolver; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; +import org.eclipse.aether.graph.DependencyFilter; +import org.eclipse.aether.util.filter.ScopeDependencyFilter; import org.eclipse.jetty.maven.plugin.JettyWebAppContext; import org.eclipse.jetty.maven.plugin.MavenServerConnector; import org.eclipse.jetty.security.HashLoginService; @@ -50,16 +54,21 @@ import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; +import org.twdata.maven.mojoexecutor.MojoExecutor; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -84,7 +93,7 @@ *

* *

- * To specify the HTTP port, use -Djetty.port=PORT + * To specify the HTTP port, use {@code -Djetty.port=PORT} *

* * @author Kohsuke Kawaguchi @@ -93,6 +102,9 @@ @Execute(phase = LifecyclePhase.COMPILE) public class RunMojo extends AbstractJettyMojo { + @Parameter(defaultValue = "${session}", required = true, readonly = true) + protected MavenSession session; + /** * The location of the war file. * @@ -125,12 +137,6 @@ public class RunMojo extends AbstractJettyMojo { @Parameter(defaultValue = "test") protected String dependencyResolution; - /** - * The directory where the webapp is built. - */ - @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}") - private File webappDirectory; - /** * Single directory for extra files to include in the WAR. */ @@ -143,14 +149,11 @@ public class RunMojo extends AbstractJettyMojo { @Component protected ArtifactFactory artifactFactory; - @Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true) - protected List remoteRepos; - - @Parameter(defaultValue = "${localRepository}") - protected ArtifactRepository localRepository; + @Component + private ProjectDependenciesResolver dependenciesResolver; @Component - protected ArtifactMetadataSource artifactMetadataSource; + private BuildPluginManager pluginManager; /** * Specifies the HTTP port number. @@ -178,13 +181,13 @@ public class RunMojo extends AbstractJettyMojo { /** * If true, the context will be restarted after a line feed on - * the input console. Disabled by default. + * the input console. Enabled by default. */ @Parameter(property = "jetty.consoleForceReload", defaultValue = "true") protected boolean consoleForceReload; @Component - protected MavenProjectBuilder projectBuilder; + protected ProjectBuilder projectBuilder; /** * Optional string that represents "groupId:artifactId" of Jenkins core jar. @@ -242,8 +245,11 @@ public class RunMojo extends AbstractJettyMojo { /** * Specify the minimum version of Java that this plugin requires. + * + * @deprecated removed without replacement */ - @Parameter(required = true) + @Deprecated + @Parameter private String minimumJavaVersion; /** @@ -252,12 +258,14 @@ public class RunMojo extends AbstractJettyMojo { * * @deprecated Use <webApp><contextPath> instead. */ + @Deprecated @Parameter(readonly = true, required = true, defaultValue = "/${project.artifactId}") protected String contextPath; @Component protected PluginWorkspaceMap pluginWorkspaceMap; + @Override public void execute() throws MojoExecutionException, MojoFailureException { getProject().setArtifacts(resolveDependencies(dependencyResolution)); @@ -300,8 +308,6 @@ public void execute() throws MojoExecutionException, MojoFailureException { setSystemPropertyIfEmpty("org.eclipse.jetty.Request.maxFormContentSize","-1"); // general-purpose system property so that we can tell from Jenkins if we are running in the hpi:run mode. setSystemPropertyIfEmpty("hudson.hpi.run","true"); - // this adds 3 secs to the shutdown time. Skip it. - setSystemPropertyIfEmpty("hudson.DNSMultiCast.disabled","true"); // expose the current top-directory of the plugin setSystemPropertyIfEmpty("jenkins.moduleRoot", basedir.getAbsolutePath()); @@ -321,8 +327,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { if (webAppFile == null) { Artifact jenkinsWarArtifact = getJenkinsWarArtifact(); try { - artifactResolver.resolve(jenkinsWarArtifact, remoteRepos, localRepository); - } catch (AbstractArtifactResolutionException x) { + jenkinsWarArtifact = artifactResolver.resolveArtifact(session.getProjectBuildingRequest(), jenkinsWarArtifact).getArtifact(); + } catch (ArtifactResolverException x) { throw new MojoExecutionException("Could not resolve " + jenkinsWarArtifact + ": " + x, x); } webAppFile = jenkinsWarArtifact.getFile(); @@ -341,7 +347,11 @@ public void execute() throws MojoExecutionException, MojoFailureException { // set JENKINS_HOME setSystemPropertyIfEmpty("JENKINS_HOME",jenkinsHome.getAbsolutePath()); File pluginsDir = new File(jenkinsHome, "plugins"); - pluginsDir.mkdirs(); + try { + Files.createDirectories(pluginsDir.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + pluginsDir + "'", e); + } // enable view auto refreshing via stapler setSystemPropertyIfEmpty("stapler.jelly.noCache","true"); @@ -365,7 +375,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { // find corresponding .hpi file Artifact hpi = artifactFactory.createArtifact(a.getGroupId(),a.getArtifactId(),a.getVersion(),null,"hpi"); - artifactResolver.resolve(hpi,getProject().getRemoteArtifactRepositories(), localRepository); + hpi = artifactResolver.resolveArtifact(session.getProjectBuildingRequest(), hpi).getArtifact(); // check recursive dependency. this is a rare case that happens when we split out some things from the core // into a plugin @@ -386,11 +396,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { copyPlugin(hpi.getFile(), pluginsDir, actualArtifactId); } } - } catch (IOException e) { - throw new MojoExecutionException("Unable to copy dependency plugin",e); - } catch (ArtifactNotFoundException e) { - throw new MojoExecutionException("Unable to copy dependency plugin",e); - } catch (ArtifactResolutionException e) { + } catch (IOException | ArtifactResolverException e) { throw new MojoExecutionException("Unable to copy dependency plugin",e); } @@ -405,7 +411,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { h.setLevel(Level.ALL); } } - loggerReferences = new LinkedList(); + loggerReferences = new LinkedList<>(); for (Map.Entry logger : loggers.entrySet()) { Logger l = Logger.getLogger(logger.getKey()); loggerReferences.add(l); @@ -430,9 +436,9 @@ private void setSystemPropertyIfEmpty(String name, String value) { private void copyPlugin(File src, File pluginsDir, String shortName) throws IOException { File dst = new File(pluginsDir, shortName + ".jpi"); File hpi = new File(pluginsDir, shortName + ".hpi"); - if (hpi.isFile()) { + if (Files.isRegularFile(hpi.toPath())) { getLog().warn("Moving historical " + hpi + " to *.jpi"); - hpi.renameTo(dst); + Files.move(hpi.toPath(), dst.toPath(), StandardCopyOption.REPLACE_EXISTING); } VersionNumber dstV = versionOfPlugin(dst); if (versionOfPlugin(src).compareTo(dstV) < 0) { @@ -445,7 +451,7 @@ private void copyPlugin(File src, File pluginsDir, String shortName) throws IOEx // pin the dependency plugin, so that even if a different version of the same plugin is bundled to Jenkins, // we still use the plugin as specified by the POM of the plugin. FileUtils.writeStringToFile(new File(dst + ".pinned"), "pinned"); - new File(pluginsDir, shortName + ".jpl").delete(); // in case we used to have a snapshot dependency + Files.deleteIfExists(new File(pluginsDir, shortName + ".jpl").toPath()); // in case we used to have a snapshot dependency } private VersionNumber versionOfPlugin(File p) throws IOException { if (!p.isFile()) { @@ -481,40 +487,24 @@ private void copyHpl(File src, File pluginsDir, String shortName) throws IOExcep /** * Create a dot-hpl file. - * - *

- * All I want to do here is to invoke the hpl target. - * there must be a better way to do this! - * (jglick: perhaps https://github.com/TimMoore/mojo-executor would work?) - * - *

- * Besides, if the user wants to change the plugin name, etc, - * this forces them to do it in two places. */ - private void generateHpl() throws MojoExecutionException, MojoFailureException { - HplMojo hpl = new HplMojo(); - hpl.project = getProject(); - hpl.setJenkinsHome(jenkinsHome); - hpl.setLog(getLog()); - hpl.pluginName = getProject().getName(); - hpl.webappDirectory = webappDirectory; - hpl.warSourceDirectory = warSourceDirectory; - hpl.scopeFilter = new ScopeArtifactFilter("runtime"); - hpl.projectBuilder = this.projectBuilder; - hpl.localRepository = this.localRepository; - hpl.jenkinsCoreId = this.jenkinsCoreId; - hpl.pluginFirstClassLoader = this.pluginFirstClassLoader; - hpl.maskClasses = this.maskClasses; - hpl.remoteRepos = this.remoteRepos; - hpl.minimumJavaVersion = this.minimumJavaVersion; - /* As needed: - hpl.artifactFactory = this.artifactFactory; - hpl.artifactResolver = this.artifactResolver; - hpl.artifactMetadataSource = this.artifactMetadataSource; - */ - hpl.execute(); + private void generateHpl() throws MojoExecutionException { + MojoExecutor.executeMojo( + MojoExecutor.plugin( + MojoExecutor.groupId("org.jenkins-ci.tools"), + MojoExecutor.artifactId("maven-hpi-plugin")), + MojoExecutor.goal("hpl"), + MojoExecutor.configuration( + MojoExecutor.element(MojoExecutor.name("jenkinsHome"), jenkinsHome.toString()), + MojoExecutor.element(MojoExecutor.name("pluginName"), project.getName()), + MojoExecutor.element(MojoExecutor.name("warSourceDirectory"), warSourceDirectory.toString()), + MojoExecutor.element(MojoExecutor.name("jenkinsCoreId"), jenkinsCoreId), + MojoExecutor.element(MojoExecutor.name("pluginFirstClassLoader"), Boolean.toString(pluginFirstClassLoader)), + MojoExecutor.element(MojoExecutor.name("maskClasses"), maskClasses)), + MojoExecutor.executionEnvironment(project, session, pluginManager)); } + @Override public void configureWebApplication() throws Exception { // Jetty tries to do this in WebAppContext.resolveWebApp but it failed to delete the directory. File t = webApp.getTempDirectory(); @@ -526,7 +516,7 @@ public void configureWebApplication() throws Exception { super.configureWebApplication(); getWebAppConfig().setWar(webAppFile.getCanonicalPath()); - for (Artifact a : (Set) project.getArtifacts()) { + for (Artifact a : project.getArtifacts()) { if (a.getGroupId().equals("org.jenkins-ci.main") && a.getArtifactId().equals("jenkins-core")) { File coreBasedir = pluginWorkspaceMap.read(a.getId()); if (coreBasedir != null) { @@ -607,6 +597,7 @@ private String loadVersion(InputStream is) throws IOException { return props.getProperty(VERSION_PROP); } + @Override public void configureScanner() throws MojoExecutionException { // use a bigger buffer as Stapler traces can get pretty large on deeply nested URL // this can only be done after server.start() is called, which happens in AbstractJettyMojo.startJetty() @@ -622,8 +613,9 @@ public void configureScanner() throws MojoExecutionException { setUpScanList(); - scannerListeners = new ArrayList(); + scannerListeners = new ArrayList<>(); scannerListeners.add(new Scanner.BulkListener() { + @Override public void filesChanged(List changes) { try { restartWebApp(changes.contains(getProject().getFile().getCanonicalPath())); @@ -667,11 +659,11 @@ public void restartWebApp(boolean reconfigureScanner) throws Exception { getLog().debug("Restarting webapp ..."); webApp.start(); - getLog().info("Restart completed at " + new Date().toString()); + getLog().info("Restart completed at " + new Date()); } private void setUpScanList() { - scanList = new ArrayList(); + scanList = new ArrayList<>(); scanList.add(getProject().getFile()); scanList.add(webAppFile); scanList.add(new File(getProject().getBuild().getOutputDirectory())); @@ -686,9 +678,11 @@ protected void startConsoleScanner() throws Exception { } } + @Override public void checkPomConfiguration() throws MojoExecutionException { } + @Override public void finishConfigurationBeforeStart() throws Exception { super.finishConfigurationBeforeStart(); WebAppContext wac = getWebAppConfig(); @@ -708,9 +702,9 @@ public void finishConfigurationBeforeStart() throws Exception { if (getProject().getPackaging().equals("jenkins-module")) { // classes compiled from jenkins module should behave as if it's a part of the core // load resources from source folders directly - for (Resource r : (List)getProject().getResources()) - super.addURL(new File(r.getDirectory()).toURL()); - super.addURL(new File(getProject().getBuild().getOutputDirectory()).toURL()); + for (Resource r : getProject().getResources()) + super.addURL(new File(r.getDirectory()).toURI().toURL()); + super.addURL(new File(getProject().getBuild().getOutputDirectory()).toURI().toURL()); // add all the jar dependencies of the module // "provided" includes all core and others, so drop them @@ -758,12 +752,12 @@ private boolean isPostJava8() { @Override public void startJetty() throws MojoExecutionException { - if (httpConnector == null && (defaultPort != 0 || StringUtils.isNotEmpty(defaultHost))) { + if (httpConnector == null && (defaultPort != 0 || (defaultHost != null && !defaultHost.isEmpty()))) { httpConnector = new MavenServerConnector(); if (defaultPort != 0) { httpConnector.setPort(defaultPort); } - if (StringUtils.isNotEmpty(defaultHost)) { + if (defaultHost != null && !defaultHost.isEmpty()) { httpConnector.setHost(defaultHost); } String browserHost; @@ -779,38 +773,63 @@ public void startJetty() throws MojoExecutionException { } /** - * Performs the equivalent of "@requireDependencyResolution" mojo attribute, + * Performs the equivalent of "@requiresDependencyResolution" mojo attribute, * so that we can choose the scope at runtime. - * @param scope + * @see LifecycleDependencyResolver#getDependencies(MavenProject, Collection, Collection, + * MavenSession, boolean, Set) */ protected Set resolveDependencies(String scope) throws MojoExecutionException { try { - ArtifactResolutionResult result = artifactResolver.resolveTransitively( - getProject().getDependencyArtifacts(), - getProject().getArtifact(), - getProject().getManagedVersionMap(), - localRepository, - getProject().getRemoteArtifactRepositories(), - artifactMetadataSource, - new ScopeArtifactFilter(scope)); - return result.getArtifacts(); - } catch (ArtifactNotFoundException e) { - throw new MojoExecutionException("Unable to copy dependency plugin",e); - } catch (ArtifactResolutionException e) { + DependencyResolutionRequest request = + new DefaultDependencyResolutionRequest( + getProject(), session.getRepositorySession()); + request.setResolutionFilter(getDependencyFilter(scope)); + DependencyResolutionResult result = dependenciesResolver.resolve(request); + + Set artifacts = new LinkedHashSet<>(); + if (result.getDependencyGraph() != null + && !result.getDependencyGraph().getChildren().isEmpty()) { + RepositoryUtils.toArtifacts( + artifacts, + result.getDependencyGraph().getChildren(), + Collections.singletonList(getProject().getArtifact().getId()), + request.getResolutionFilter()); + } + return artifacts; + } catch (DependencyResolutionException e) { throw new MojoExecutionException("Unable to copy dependency plugin",e); } } + private static DependencyFilter getDependencyFilter(String scope) { + switch (scope) { + case Artifact.SCOPE_COMPILE: + return new ScopeDependencyFilter(Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST); + case Artifact.SCOPE_RUNTIME: + return new ScopeDependencyFilter( + Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_TEST); + case Artifact.SCOPE_TEST: + return null; + default: + throw new IllegalArgumentException("unexpected scope: " + scope); + } + } + public Set getProjectArtifacts() { - Set r = new HashSet(); - for (Artifact a : (Collection)getProject().getArtifacts()) { + Set r = new HashSet<>(); + for (Artifact a : getProject().getArtifacts()) { r.add(wrap(a)); } return r; } protected MavenArtifact wrap(Artifact a) { - return new MavenArtifact(a,artifactResolver,artifactFactory,projectBuilder,getProject().getRemoteArtifactRepositories(),localRepository); + return new MavenArtifact( + a, + artifactResolver, + artifactFactory, + projectBuilder, + session); } protected Artifact getJenkinsWarArtifact() throws MojoExecutionException { diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/TagLibInterfaceGeneratorMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/TagLibInterfaceGeneratorMojo.java index 2ad1bd82e5..1f69ae7d27 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/TagLibInterfaceGeneratorMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/TagLibInterfaceGeneratorMojo.java @@ -13,12 +13,10 @@ import com.sun.codemodel.writer.FilterCodeWriter; import groovy.lang.Closure; import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang.StringUtils; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @@ -32,13 +30,15 @@ import org.kohsuke.stapler.jelly.groovy.TagLibraryUri; import org.kohsuke.stapler.jelly.groovy.TypedTagLibrary; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileFilter; -import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.List; import java.util.Map; @@ -52,7 +52,7 @@ public class TagLibInterfaceGeneratorMojo extends AbstractMojo { /** * The maven project. */ - @Component + @Parameter(defaultValue = "${project}", readonly = true) protected MavenProject project; /** @@ -69,6 +69,7 @@ public class TagLibInterfaceGeneratorMojo extends AbstractMojo { private SAXReader saxReader = new SAXReader(); + @Override public void execute() throws MojoExecutionException, MojoFailureException { try { JCodeModel codeModel = new JCodeModel(); @@ -76,7 +77,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { walk(new File(res.getDirectory()),codeModel.rootPackage(),""); } - outputDirectory.mkdirs(); + Files.createDirectories(outputDirectory.toPath()); CodeWriter w = new FilterCodeWriter(encoding != null ? new FileCodeWriter(outputDirectory, encoding) : new FileCodeWriter(outputDirectory)) { // Cf. ProgressCodeWriter: @Override public Writer openSource(JPackage pkg, String fileName) throws IOException { @@ -105,31 +106,24 @@ private void report(JPackage pkg, String fileName) { } private void walk(File dir,JPackage pkg,String dirName) throws JClassAlreadyExistsException, IOException { - File[] children = dir.listFiles(new FileFilter() { - public boolean accept(File f) { - return f.isDirectory(); - } - }); + File[] children = dir.listFiles(File::isDirectory); if (children!=null) { for (File child : children) walk(child,pkg.subPackage(h2j(child.getName())),dirName+'/'+child.getName()); } if (isTagLibDir(dir)) { - JDefinedClass c = pkg.parent()._interface(StringUtils.capitalize(h2j(dir.getName())) + "TagLib"); + String taglib = h2j(dir.getName()); + JDefinedClass c = pkg.parent()._interface(taglib.substring(0, 1).toUpperCase() + taglib.substring(1) + "TagLib"); c._implements(TypedTagLibrary.class); c.annotate(TagLibraryUri.class).param("value",dirName); JBinaryFile _gdsl = new JBinaryFile(c.name()+".gdsl"); - PrintWriter gdsl = new PrintWriter(_gdsl.getDataStore()); + PrintWriter gdsl = new PrintWriter(new BufferedWriter(new OutputStreamWriter(_gdsl.getDataStore(), StandardCharsets.UTF_8))); gdsl.printf("package %s;\n",pkg.parent().name()); gdsl.printf("contributor(context(ctype:'%s')) {\n",c.fullName()); - File[] tags = dir.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.endsWith(".jelly"); - } - }); + File[] tags = dir.listFiles((unused, name) -> name.endsWith(".jelly")); long timestamp = -1; @@ -181,7 +175,7 @@ public boolean accept(File dir, String name) { gdsl.printf(" ], dummy:void, c:Closure]\n"); } } catch (DocumentException e) { - throw (IOException)new IOException("Failed to parse "+tag).initCause(e); + throw new IOException("Failed to parse " + tag, e); } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestDependencyMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestDependencyMojo.java index bf17ac5cdc..43f21f1403 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestDependencyMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestDependencyMojo.java @@ -1,16 +1,70 @@ package org.jenkinsci.maven.plugins.hpi; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; +import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.ComparableVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.LifecycleDependencyResolver; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.DefaultDependencyResolutionRequest; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.DependencyResolutionException; +import org.apache.maven.project.DependencyResolutionRequest; +import org.apache.maven.project.DependencyResolutionResult; import org.apache.maven.project.MavenProject; - -import java.io.*; -import java.util.Set; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectDependenciesResolver; +import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder; +import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilderException; +import org.apache.maven.shared.dependency.graph.DependencyNode; +import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor; +import org.twdata.maven.mojoexecutor.MojoExecutor; /** * Places test-dependency plugins into somewhere the test harness can pick up. @@ -18,18 +72,253 @@ *

* See {@code TestPluginManager.loadBundledPlugins()} where the test harness uses it. * - * @author Kohsuke Kawaguchi + *

Additionally, it may adjust the classpath for {@code surefire:test} to run tests against + * different versions of various dependencies than what was configured in the POM. */ @Mojo(name="resolve-test-dependencies", requiresDependencyResolution = ResolutionScope.TEST) +@SuppressFBWarnings(value = "REDOS", justification = "trusted code") public class TestDependencyMojo extends AbstractHpiMojo { - public void execute() throws MojoExecutionException, MojoFailureException { - File testDir = new File(project.getBuild().getTestOutputDirectory(),"test-dependencies"); - testDir.mkdirs(); + private static final Pattern CORE_REGEX = Pattern.compile("WEB-INF/lib/jenkins-core-([0-9.]+(?:-[0-9a-f.]+)*(?:-(?i)([a-z]+)(-)?([0-9a-f.]+)?)?(?:-(?i)([a-z]+)(-)?([0-9a-f_.]+)?)?(?:-SNAPSHOT)?)[.]jar"); + private static final Pattern PLUGIN_REGEX = Pattern.compile("WEB-INF/plugins/([^/.]+)[.][hj]pi"); + private static final Pattern OVERRIDE_REGEX = Pattern.compile("([^:]+:[^:]+):([^:]+)"); + + @Component private BuildPluginManager pluginManager; + + @Component private DependencyCollectorBuilder dependencyCollectorBuilder; + + @Component private ProjectDependenciesResolver dependenciesResolver; + + /** + * List of dependency version overrides in the form {@code groupId:artifactId:version} to apply + * during testing. Must correspond to dependencies already present in the project model or their + * transitive dependencies. + */ + @Parameter(property = "overrideVersions") + private List overrideVersions; + + /** + * Path to a Jenkins WAR file with bundled plugins to apply during testing. + *

Dependencies already present in the project model or their transitive dependencies will be updated to the versions in the WAR. + * Dependencies not already present in the project model will be added to the project model only if {@link #overrideWarAdditions} is set. + *

May be combined with {@code overrideVersions} so long as the results do not conflict. + *

The version of the WAR must be identical to {@code jenkins.version}. + */ + @Parameter(property = "overrideWar") + private File overrideWar; + + /** + * Indicates that all plugins bundled in {@link #overrideWar} should be added to the project model even if not originally mentioned. + * Would normally complement setting the system property {@code jth.jenkins-war.path} to that WAR. + */ + @Parameter(property = "overrideWarAdditions") + private boolean overrideWarAdditions; + + /** + * Whether to update all transitive dependencies to the upper bounds. Effectively causes the + * same behavior as the {@code requireUpperBoundDeps} Enforcer rule would, if the specified + * dependencies were to be written to the POM. Intended for use in conjunction with {@link + * #overrideVersions} or {@link #overrideWar}. + */ + @Parameter(property = "useUpperBounds") + private boolean useUpperBounds; + + /** + * List of exclusions to upper bound updates in the form {@code groupId:artifactId}. + * Must not be provided when {@link #useUpperBounds} is false. + */ + @Parameter(property = "upperBoundsExcludes") + private List upperBoundsExcludes; + + @Override + public void execute() throws MojoExecutionException { + Map overrides = overrideVersions != null ? parseOverrides(overrideVersions) : Collections.emptyMap(); + if (!overrides.isEmpty()) { + getLog().info(String.format("Applying %d overrides.", overrides.size())); + } + if (overrides.containsKey(String.format("%s:%s", project.getGroupId(), project.getArtifactId()))) { + throw new MojoExecutionException("Cannot override self"); + } + + Map bundledPlugins = overrideWar != null ? scanWar(overrideWar, session, project) : Collections.emptyMap(); + if (!bundledPlugins.isEmpty()) { + getLog().info(String.format("Scanned contents of %s with %d bundled plugins", overrideWar, bundledPlugins.size())); + } + + // Deal with conflicts in user-provided input. + Set intersection = new HashSet<>(bundledPlugins.keySet()); + intersection.retainAll(overrides.keySet()); + for (String override : intersection) { + if (bundledPlugins.get(override).equals(overrides.get(override))) { + /* + * Not really a conflict since the versions are the same. Remove it from one of the + * two lists to simplify the implementation later. We pick the former since the + * semantics for the latter are looser. + */ + overrides.remove(override); + } else { + throw new MojoExecutionException(String.format( + "Failed to override %s: conflict between %s in overrideVersions and %s in overrideWar", + override, overrides.get(override), bundledPlugins.get(override))); + } + } + + // The effective artifacts to be used when building the plugin index and test classpath. + Set effectiveArtifacts; + + // Track changes to the classpath when the user has overridden dependency versions. + Map additions = new HashMap<>(); + Map deletions = new HashMap<>(); + Map updates = new HashMap<>(); + + if (overrides.isEmpty() && overrideWar == null) { + effectiveArtifacts = getProjectArtfacts(); + } else { + // Under no circumstances should this code ever be executed when performing a release. + for (String goal : session.getGoals()) { + if (goal.contains("deploy")) { + throw new MojoExecutionException("Cannot override dependencies when doing a release"); + } + } + + // Create a shadow project for dependency analysis. + MavenProject shadow = project.clone(); + + // Stash the original resolution for use later. + Map originalResolution = new HashMap<>(); + for (Artifact artifact : shadow.getArtifacts()) { + originalResolution.put(toKey(artifact), artifact.getVersion()); + } + + // First pass: apply the overrides specified by the user. + applyOverrides(overrides, bundledPlugins, false, overrideWarAdditions, shadow, getLog()); + + if (useUpperBounds) { + boolean converged = false; + int i = 0; + Map upperBounds = null; + + while (!converged) { + if (i++ > 10) { + throw new MojoExecutionException("Failed to iterate to convergence during upper bounds analysis: " + upperBounds); + } + + /* + * Do upper bounds analysis. Upper bounds analysis consumes the model directly and + * not the resolution of that model, so it is fine to invoke it at this point with + * the model having been updated and the resolution having been cleared. + */ + DependencyNode node; + try { + ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); + buildingRequest.setProject(shadow); + ArtifactFilter filter = null; // Evaluate all scopes + node = dependencyCollectorBuilder.collectDependencyGraph(buildingRequest, filter); + } catch (DependencyCollectorBuilderException e) { + throw new MojoExecutionException("Failed to analyze dependency tree for useUpperBounds", e); + } + RequireUpperBoundDepsVisitor visitor = new RequireUpperBoundDepsVisitor(); + node.accept(visitor); + String self = String.format("%s:%s", shadow.getGroupId(), shadow.getArtifactId()); + upperBounds = visitor.upperBounds(upperBoundsExcludes, self); + + if (upperBounds.isEmpty()) { + converged = true; + } else { + // Second pass: apply the results of the upper bounds analysis. + + /* + * applyOverrides depends on resolution, so resolve again between the first pass + * and the second. + */ + Set resolved = resolveDependencies(shadow); + shadow.setArtifacts(resolved); + + applyOverrides(upperBounds, Collections.emptyMap(), true, overrideWarAdditions, shadow, getLog()); + } + } + } else if (!upperBoundsExcludes.isEmpty()) { + throw new MojoExecutionException("Cannot provide upper bounds excludes when not using upper bounds"); + } + + /* + * At this point, the model has been updated as the user has requested. We now redo + * resolution and compare the new resolution to the original in order to account for + * updates to transitive dependencies that are not present in the model. Anything that + * was updated in the new resolution needs to be updated in the test classpath. Anything + * that was removed in the new resolution needs to be removed from the test classpath. + * Anything that was added in the new resolution needs to be added to the test + * classpath. + */ + Set resolved = resolveDependencies(shadow); + Map newResolution = new HashMap<>(); + Set self = new HashSet<>(); + for (Artifact artifact : resolved) { + if (artifact.getGroupId().equals(project.getGroupId()) && artifact.getArtifactId().equals(project.getArtifactId())) { + self.add(artifact); + } else { + newResolution.put(toKey(artifact), artifact.getVersion()); + } + } + resolved.removeAll(self); + effectiveArtifacts = wrap(new Artifacts(resolved)); + for (Map.Entry entry : newResolution.entrySet()) { + if (originalResolution.containsKey(entry.getKey())) { + // Present in both old and new resolution: check for update. + String originalVersion = originalResolution.get(entry.getKey()); + String newVersion = entry.getValue(); + /* + * We check that the new version is not equal to the original version rather + * than newer than the original version for the following reason. Suppose we + * depend on A:1.0 which depends on B:1.2. Now suppose a problem is discovered + * in B:1.2 that results in A:1.1 rolling back to B:1.1. We only ever directly + * depended on A:1.0, but now we override A:1.0 to A:1.1. B:1.2 was in our + * transitive tree before, but now for correctness we must change B from 1.2 to + * 1.1. + */ + if (!newVersion.equals(originalVersion)) { + updates.put(entry.getKey(), newVersion); + } + } else { + // Present in new resolution but not old: addition. + additions.put(entry.getKey(), entry.getValue()); + } + } + for (Map.Entry entry : originalResolution.entrySet()) { + if (!newResolution.containsKey(entry.getKey())) { + // Present in old resolution but not new: deletion. + deletions.put(entry.getKey(), entry.getValue()); + } + } + getLog().info("After resolving, additions: " + additions); + getLog().info("After resolving, deletions: " + deletions); + getLog().info("After resolving, updates: " + updates); + if (getLog().isDebugEnabled()) { + getLog().debug("New dependency tree:"); + MavenSession shadowSession = session.clone(); + shadowSession.setCurrentProject(shadow); + shadow.setArtifacts(resolved); + MojoExecutor.executeMojo( + MojoExecutor.plugin( + MojoExecutor.groupId("org.apache.maven.plugins"), + MojoExecutor.artifactId("maven-dependency-plugin")), + MojoExecutor.goal("tree"), + MojoExecutor.configuration( + MojoExecutor.element( + MojoExecutor.name("scope"), Artifact.SCOPE_TEST)), + MojoExecutor.executionEnvironment(shadow, shadowSession, pluginManager)); + } + } + + File testDir = new File(project.getBuild().getTestOutputDirectory(), "test-dependencies"); try { - Writer w = new OutputStreamWriter(new FileOutputStream(new File(testDir,"index")),"UTF-8"); + Files.createDirectories(testDir.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + testDir + "'", e); + } - for (MavenArtifact a : getProjectArtfacts()) { + try (FileOutputStream fos = new FileOutputStream(new File(testDir, "index")); Writer w = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) { + for (MavenArtifact a : effectiveArtifacts) { if (!a.isPluginBestEffort(getLog())) continue; @@ -44,10 +333,528 @@ public void execute() throws MojoExecutionException, MojoFailureException { FileUtils.copyFile(a.getHpi().getFile(),dst); w.write(artifactId + "\n"); } - - w.close(); } catch (IOException e) { throw new MojoExecutionException("Failed to copy dependency plugins",e); } + + if (!additions.isEmpty() || !deletions.isEmpty() || !updates.isEmpty()) { + List additionalClasspathElements = new LinkedList<>(); + NavigableMap includes = new TreeMap<>(); + includes.putAll(additions); + includes.putAll(updates); + for (Map.Entry entry : includes.entrySet()) { + String key = entry.getKey(); + String[] groupArt = key.split(":"); + String groupId = groupArt[0]; + String artifactId = groupArt[1]; + String version = entry.getValue(); + /* + * We cannot use MavenProject.getArtifactMap since we may have multiple dependencies + * of different classifiers. + */ + boolean found = false; + // Yeah, this is O(n²)... deal with it! + for (MavenArtifact a : effectiveArtifacts) { + if (!a.getGroupId().equals(groupId) || !a.getArtifactId().equals(artifactId)) { + continue; + } + if (!a.getVersion().equals(version)) { + throw new AssertionError("should never happen"); + } + found = true; + if (a.getArtifactHandler().isAddedToClasspath()) { + /* + * Everything is added to the test classpath, so there is no need to check + * scope. + */ + additionalClasspathElements.add(a.getFile().getAbsolutePath()); + } + } + if (!found) { + throw new MojoExecutionException("could not find dependency " + key); + } + } + + NavigableSet classpathDependencyExcludes = new TreeSet<>(); + classpathDependencyExcludes.addAll(deletions.keySet()); + classpathDependencyExcludes.addAll(updates.keySet()); + + Properties properties = project.getProperties(); + if (getLog().isDebugEnabled()) { + getLog().debug(String.format("Replacing POM-defined classpath elements %s with %s", classpathDependencyExcludes, additionalClasspathElements)); + } + // cf. http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html + appendEntries("maven.test.additionalClasspath", additionalClasspathElements, properties); + appendEntries("maven.test.dependency.excludes", classpathDependencyExcludes, properties); + } + } + + private static void appendEntries(String property, Collection additions, Properties properties) { + String existing = properties.getProperty(property); + List newEntries = new ArrayList<>(); + if (existing != null && !existing.isEmpty()) { + for (String entry : existing.split(",")) { + entry = entry.trim(); + if (!entry.isEmpty()) { + newEntries.add(entry); + } + } + } + newEntries.addAll(additions); + properties.setProperty(property, String.join(",", newEntries)); + } + + /** + * Scan a WAR file, accumulating plugin information. + * + * @param war The WAR to scan. + * @return The bundled plugins in the WAR. + */ + private static Map scanWar(File war, MavenSession session, MavenProject project) throws MojoExecutionException { + Map overrides = new HashMap<>(); + try (JarFile jf = new JarFile(war)) { + Enumeration entries = jf.entries(); + String coreVersion = null; + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + Matcher m = CORE_REGEX.matcher(name); + if (m.matches()) { + if (coreVersion != null) { + throw new MojoExecutionException("More than 1 jenkins-core JAR in " + war); + } + coreVersion = m.group(1); + } + m = PLUGIN_REGEX.matcher(name); + if (m.matches()) { + try (InputStream is = jf.getInputStream(entry); JarInputStream jis = new JarInputStream(is)) { + Manifest manifest = jis.getManifest(); + String groupId = manifest.getMainAttributes().getValue("Group-Id"); + if (groupId == null) { + throw new IllegalArgumentException("Failed to determine group ID for " + name); + } + String artifactId = manifest.getMainAttributes().getValue("Short-Name"); + if (artifactId == null) { + throw new IllegalArgumentException("Failed to determine artifact ID for " + name); + } + String version = manifest.getMainAttributes().getValue("Plugin-Version"); + if (version == null) { + throw new IllegalArgumentException("Failed to determine version for " + name); + } + String key = String.format("%s:%s", groupId, artifactId); + String self = String.format("%s:%s", project.getGroupId(), project.getArtifactId()); + if (!key.equals(self)) { + overrides.put(key, version); + } + } + } + } + + /* + * It is tempting to try and avoid the requirement for jenkins.version here and simply + * override the core dependencies in the tree; however, this fails to take into account + * that core's provided dependencies are already being managed at their original + * versions. + */ + if (coreVersion == null) { + throw new MojoExecutionException("no jenkins-core.jar in " + war); + } + String jenkinsVersion = session.getSystemProperties().getProperty("jenkins.version"); + if (jenkinsVersion == null) { + jenkinsVersion = project.getProperties().getProperty("jenkins.version"); + } + if (jenkinsVersion == null) { + throw new MojoExecutionException("jenkins.version must be set when using overrideWar"); + } else if (!jenkinsVersion.equals(coreVersion)) { + throw new MojoExecutionException("jenkins.version must match the version specified by overrideWar: " + coreVersion); + } + } catch (IOException e) { + throw new MojoExecutionException("Failed to scan " + war, e); + } + return overrides; + } + + private static Map parseOverrides(List overrideVersions) throws MojoExecutionException { + Map overrides = new HashMap<>(); + for (String override : overrideVersions) { + Matcher m = OVERRIDE_REGEX.matcher(override); + if (!m.matches()) { + throw new MojoExecutionException("illegal override: " + override); + } + overrides.put(m.group(1), m.group(2)); + } + return overrides; + } + + /** + * Apply the overrides specified by the user or upper bounds analysis to the model (i.e., + * dependency management or dependencies) in the shadow project. This clears the existing + * resolution that was done because of the {@code @requiresDependencyResolution} Mojo attribute, + * as it is now invalid. It is possible to perform such a pass manually on a plugin and compare + * the results with this algorithm to verify that the logic in this method is correct. + */ + private static void applyOverrides( + Map overrides, + Map bundledPlugins, + boolean upperBounds, + boolean overrideWarAdditions, + MavenProject project, + Log log) + throws MojoExecutionException { + Set appliedOverrides = new HashSet<>(); + Set appliedBundledPlugins = new HashSet<>(); + + // Update existing dependency entries in the model. + for (Dependency dependency : project.getDependencies()) { + String key = toKey(dependency); + if (updateDependency(dependency, overrides, "direct dependency", log)) { + appliedOverrides.add(key); + } + if (updateDependency(dependency, bundledPlugins, "direct dependency", log)) { + appliedBundledPlugins.add(key); + } + } + + // Update existing dependency management entries in the model. + if (project.getDependencyManagement() != null) { + for (Dependency dependency : project.getDependencyManagement().getDependencies()) { + String key = toKey(dependency); + if (updateDependency(dependency, overrides, "dependency management entry", log)) { + appliedOverrides.add(key); + } + if (updateDependency(dependency, bundledPlugins, "dependency management entry", log)) { + appliedBundledPlugins.add(key); + } + } + } + + /* + * If an override was requested for a transitive dependency that is not in the model, add a + * dependency management entry to the model. + */ + Set unappliedOverrides = new HashSet<>(overrides.keySet()); + unappliedOverrides.removeAll(appliedOverrides); + Set overrideAdditions = new HashSet<>(); + for (Artifact artifact : project.getArtifacts()) { + String key = toKey(artifact); + if (unappliedOverrides.contains(key)) { + String version = overrides.get(key); + Dependency dependency = new Dependency(); + dependency.setGroupId(artifact.getGroupId()); + dependency.setArtifactId(artifact.getArtifactId()); + dependency.setVersion(version); + dependency.setScope(artifact.getScope()); + dependency.setType(artifact.getType()); + dependency.setClassifier(artifact.getClassifier()); + if (dependency.getGroupId().equals(project.getGroupId()) && dependency.getArtifactId().equals(project.getArtifactId())) { + throw new MojoExecutionException("Cannot add self to dependency management section"); + } + DependencyManagement dm = project.getDependencyManagement(); + if (dm != null) { + log.info(String.format("Adding dependency management entry %s:%s", key, dependency.getVersion())); + dm.addDependency(dependency); + } else { + throw new MojoExecutionException(String.format("Failed to add dependency management entry %s:%s because the project does not have a dependency management section", key, version)); + } + overrideAdditions.add(key); + } + } + unappliedOverrides.removeAll(overrideAdditions); + + // By now, we should have applied the entire override request. If not, fail. + if (!unappliedOverrides.isEmpty()) { + if (upperBounds) { + /* + * An upper bounds override that could not be found in the transitive tree is most likely a + * provided transitive dependency of a test-scoped dependency. We could ignore these, but + * we add them to the dependency management section just to be safe. + */ + for (String key : unappliedOverrides) { + String[] groupArt = key.split(":"); + Dependency dependency = new Dependency(); + dependency.setGroupId(groupArt[0]); + dependency.setArtifactId(groupArt[1]); + dependency.setVersion(overrides.get(key)); + if (dependency.getGroupId().equals(project.getGroupId()) && dependency.getArtifactId().equals(project.getArtifactId())) { + throw new MojoExecutionException("Cannot add self to dependency management section"); + } + DependencyManagement dm = project.getDependencyManagement(); + if (dm == null) { + dm = new DependencyManagement(); + project.getModel().setDependencyManagement(dm); + } + log.info(String.format("Adding dependency management entry %s:%s", key, dependency.getVersion())); + dm.addDependency(dependency); + overrideAdditions.add(key); + } + } else { + throw new MojoExecutionException("Failed to apply the following overrides: " + unappliedOverrides); + } + } + + if (overrideWarAdditions) { + /* + * If a bundled plugin was added that is neither in the model nor the transitive dependency + * chain, add a test-scoped direct dependency to the model. This is necessary in order for + * us to be able to correctly populate target/test-dependencies/ later on. + */ + Set unappliedBundledPlugins = new HashSet<>(bundledPlugins.keySet()); + unappliedBundledPlugins.removeAll(appliedBundledPlugins); + for (String key : unappliedBundledPlugins) { + String[] groupArt = key.split(":"); + String groupId = groupArt[0]; + String artifactId = groupArt[1]; + String version = bundledPlugins.get(key); + Dependency dependency = new Dependency(); + dependency.setGroupId(groupId); + dependency.setArtifactId(artifactId); + dependency.setVersion(version); + dependency.setScope(Artifact.SCOPE_TEST); + if (dependency.getGroupId().equals(project.getGroupId()) && dependency.getArtifactId().equals(project.getArtifactId())) { + throw new MojoExecutionException("Cannot add self as test-scoped dependency"); + } + log.info(String.format("Adding test-scoped direct dependency %s:%s", key, version)); + project.getDependencies().add(dependency); + } + } + + log.debug("adjusted dependencies: " + project.getDependencies()); + if (project.getDependencyManagement() != null) { + log.debug("adjusted dependency management: " + project.getDependencyManagement().getDependencies()); + } + + /* + * With our changes to the model, the existing resolution is now invalid, so clear it lest + * anything accidentally use the invalid values. We will perform resolution again after all + * passes are complete. + */ + project.setDependencyArtifacts(null); + project.setArtifacts(null); + } + + private static boolean updateDependency(Dependency dependency, Map overrides, String type, Log log) { + String key = toKey(dependency); + String overrideVersion = overrides.get(key); + if (overrideVersion != null) { + String classifier = dependency.getClassifier(); + log.info(String.format("Updating %s %s%s from %s to %s", type, key, classifier != null ? ":" + classifier : "", dependency.getVersion(), overrideVersion)); + dependency.setVersion(overrideVersion); + return true; + } + return false; + } + + /** + * Performs the equivalent of the "@requiresDependencyResolution" mojo attribute. + * + * @see LifecycleDependencyResolver#getDependencies(MavenProject, Collection, Collection, + * MavenSession, boolean, Set) + */ + private Set resolveDependencies(MavenProject project) throws MojoExecutionException { + try { + DependencyResolutionRequest request = new DefaultDependencyResolutionRequest(project, session.getRepositorySession()); + DependencyResolutionResult result = dependenciesResolver.resolve(request); + + Set artifacts = new LinkedHashSet<>(); + if (result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty()) { + RepositoryUtils.toArtifacts( + artifacts, + result.getDependencyGraph().getChildren(), + Collections.singletonList(project.getArtifact().getId()), + request.getResolutionFilter()); + } + return artifacts; + } catch (DependencyResolutionException e) { + throw new MojoExecutionException("Unable to resolve dependencies", e); + } + } + + // Adapted from RequireUpperBoundDeps @ 731ea7a693a0986f2054b6a73a86a31373df59ec. + private class RequireUpperBoundDepsVisitor implements DependencyNodeVisitor { + + private Map> keyToPairsMap = new LinkedHashMap<>(); + + public boolean visit(DependencyNode node) { + DependencyNodeHopCountPair pair = new DependencyNodeHopCountPair(node); + String key = pair.constructKey(); + List pairs = keyToPairsMap.get(key); + if (pairs == null) { + pairs = new ArrayList<>(); + keyToPairsMap.put(key, pairs); + } + pairs.add(pair); + Collections.sort(pairs); + return true; + } + + public boolean endVisit(DependencyNode node) { + return true; + } + + // added for TestDependencyMojo in place of getConflicts/containsConflicts + public Map upperBounds(List upperBoundsExcludes, String self) { + Map r = new HashMap<>(); + for (List pairs : keyToPairsMap.values()) { + DependencyNodeHopCountPair resolvedPair = pairs.get(0); + + // search for artifact with lowest hopCount + for (DependencyNodeHopCountPair hopPair : pairs.subList(1, pairs.size())) { + if (hopPair.getHopCount() < resolvedPair.getHopCount()) { + resolvedPair = hopPair; + } + } + + ArtifactVersion resolvedVersion = resolvedPair.extractArtifactVersion(false); + + for (DependencyNodeHopCountPair pair : pairs) { + ArtifactVersion version = pair.extractArtifactVersion(true); + if (resolvedVersion.compareTo(version) < 0) { + Artifact artifact = resolvedPair.node.getArtifact(); + String key = toKey(artifact); + if (!key.equals(self) && (!r.containsKey(key) || new ComparableVersion(version.toString()).compareTo(new ComparableVersion(r.get(key))) > 1)) { + if (upperBoundsExcludes.contains(key)) { + getLog().info( "Ignoring requireUpperBoundDeps in " + key); + } else { + getLog().info(buildErrorMessage(pairs.stream().map(DependencyNodeHopCountPair::getNode).collect(Collectors.toList())).trim()); + getLog().info(String.format("for %s, upper bounds forces an upgrade from %s to %s", key, resolvedVersion, version)); + r.put(key, version.toString()); + } + } + } + } + } + return r; + } + } + + private static class DependencyNodeHopCountPair implements Comparable { + + private DependencyNode node; + + private int hopCount; + + private DependencyNodeHopCountPair(DependencyNode node) { + this.node = node; + countHops(); + } + + private void countHops() { + hopCount = 0; + DependencyNode parent = node.getParent(); + while (parent != null) { + hopCount++; + parent = parent.getParent(); + } + } + + private String constructKey() { + Artifact artifact = node.getArtifact(); + return toKey(artifact); + } + + public DependencyNode getNode() { + return node; + } + + private ArtifactVersion extractArtifactVersion(boolean usePremanagedVersion) { + if (usePremanagedVersion && node.getPremanagedVersion() != null) { + return new DefaultArtifactVersion(node.getPremanagedVersion()); + } + + Artifact artifact = node.getArtifact(); + String version = artifact.getBaseVersion(); + if (version != null) { + return new DefaultArtifactVersion(version); + } + try { + return artifact.getSelectedVersion(); + } catch (OverConstrainedVersionException e) { + throw new RuntimeException("Version ranges problem with " + node.getArtifact(), e); + } + } + + public int getHopCount() { + return hopCount; + } + + @SuppressFBWarnings( + value = "EQ_COMPARETO_USE_OBJECT_EQUALS", + justification = "Silly check; it is perfectly reasonable to implement Comparable by writing a compareTo without an equals.") + public int compareTo(DependencyNodeHopCountPair other) { + return Integer.compare(hopCount, other.getHopCount()); + } + } + + private static String buildErrorMessage(List conflict) { + StringBuilder errorMessage = new StringBuilder(); + errorMessage.append( + "Require upper bound dependencies error for " + + getFullArtifactName(conflict.get(0), false) + + " paths to dependency are:" + + System.lineSeparator()); + if (conflict.size() > 0) { + errorMessage.append(buildTreeString(conflict.get(0))); + } + for (DependencyNode node : conflict.subList(1, conflict.size())) { + errorMessage.append("and" + System.lineSeparator()); + errorMessage.append(buildTreeString(node)); + } + return errorMessage.toString(); + } + + private static StringBuilder buildTreeString(DependencyNode node) { + List loc = new ArrayList<>(); + DependencyNode currentNode = node; + while (currentNode != null) { + StringBuilder line = new StringBuilder(getFullArtifactName(currentNode, false)); + + if (currentNode.getPremanagedVersion() != null) { + line.append(" (managed) <-- "); + line.append(getFullArtifactName(currentNode, true)); + } + + loc.add(line.toString()); + currentNode = currentNode.getParent(); + } + Collections.reverse(loc); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < loc.size(); i++) { + for (int j = 0; j < i; j++) { + builder.append(" "); + } + builder.append("+-").append(loc.get(i)); + builder.append(System.lineSeparator()); + } + return builder; + } + + private static String getFullArtifactName(DependencyNode node, boolean usePremanaged) { + Artifact artifact = node.getArtifact(); + + String version = node.getPremanagedVersion(); + if (!usePremanaged || version == null) { + version = artifact.getBaseVersion(); + } + String result = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + version; + + String classifier = artifact.getClassifier(); + if (classifier != null && !classifier.isEmpty()) { + result += ":" + classifier; + } + + String scope = artifact.getScope(); + if (scope != null) { + result += " [" + scope + ']'; + } + + return result; + } + + private static String toKey(Artifact artifact) { + return artifact.getGroupId() + ":" + artifact.getArtifactId(); + } + + private static String toKey(Dependency dependency) { + return dependency.getGroupId() + ":" + dependency.getArtifactId(); } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestHplMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestHplMojo.java index 2c552f626a..0d937c7ed4 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestHplMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestHplMojo.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; /** * Generate .hpl file in the test class directory so that test harness can locate the plugin. @@ -25,7 +26,11 @@ public class TestHplMojo extends HplMojo { @Override protected File computeHplFile() throws MojoExecutionException { File testDir = new File(project.getBuild().getTestOutputDirectory()); - testDir.mkdirs(); + try { + Files.createDirectories(testDir.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create directories for '" + testDir + "'", e); + } File theHpl = new File(testDir,"the.hpl"); if (project.getArtifact().isSnapshot()) { try { diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestInsertionMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestInsertionMojo.java index ddd2ea20f1..ab88182659 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestInsertionMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestInsertionMojo.java @@ -2,11 +2,13 @@ import hudson.util.VersionNumber; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.util.List; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.attribute.FileTime; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; @@ -59,11 +61,12 @@ private static String quote(String s) { return '"'+s.replace("\\", "\\\\")+'"'; } + @Override public void execute() throws MojoExecutionException, MojoFailureException { if (!project.getPackaging().equals("hpi")) { Artifact jenkinsTestHarness = null; if (jenkinsTestHarnessId != null) { - for (Artifact b : (List) project.getTestArtifacts()) { + for (Artifact b : project.getTestArtifacts()) { if (jenkinsTestHarnessId.equals(b.getGroupId() +":"+b.getArtifactId())) { jenkinsTestHarness = b; break; @@ -73,7 +76,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { if (jenkinsTestHarness != null) { try { ArtifactVersion version = jenkinsTestHarness.getSelectedVersion(); - if (version == null || version.compareTo(new DefaultArtifactVersion("2.14")) == -1) { + if (version == null || version.compareTo(new DefaultArtifactVersion("2.14")) < 0) { getLog().info( "Skipping " + project.getName() + " because it's not hpi and the " + jenkinsTestHarnessId @@ -104,11 +107,15 @@ public void execute() throws MojoExecutionException, MojoFailureException { return; } + File f = new File(project.getBasedir(), "target/generated-test-sources/injected"); try { - File f = new File(project.getBasedir(), "target/generated-test-sources/injected"); - f.mkdirs(); - File javaFile = new File(f, injectedTestName + ".java"); - PrintWriter w = new PrintWriter(new OutputStreamWriter(new FileOutputStream(javaFile))); + Files.createDirectories(f.toPath()); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create injected test directory", e); + } + + File javaFile = new File(f, injectedTestName + ".java"); + try (PrintWriter w = new PrintWriter(new OutputStreamWriter(new FileOutputStream(javaFile), StandardCharsets.UTF_8));) { w.println("import java.util.*;"); w.println("/**"); w.println(" * Entry point to auto-generated tests (generated by maven-hpi-plugin)."); @@ -128,15 +135,19 @@ public void execute() throws MojoExecutionException, MojoFailureException { w.println(" return org.jvnet.hudson.test.PluginAutomaticTestBuilder.build(parameters);"); w.println(" }"); w.println("}"); - w.close(); + } catch (IOException e) { + throw new MojoExecutionException("Failed to create injected tests", e); + } - project.addTestCompileSourceRoot(f.getAbsolutePath()); + project.addTestCompileSourceRoot(f.getAbsolutePath()); + try { // always set the same time stamp on this file, so that Maven will not re-compile this // every time we run this mojo. - javaFile.setLastModified(0); - } catch (FileNotFoundException e) { - e.printStackTrace(); + Files.setLastModifiedTime(javaFile.toPath(), FileTime.fromMillis(0L)); + } catch (IOException e) { + // Ignore, as this is an optimization for performance rather than correctness. + getLog().warn("Failed to clear last modified time on " + javaFile, e); } } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/TestRuntimeMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestRuntimeMojo.java new file mode 100644 index 0000000000..e7d8ec798e --- /dev/null +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/TestRuntimeMojo.java @@ -0,0 +1,158 @@ +package org.jenkinsci.maven.plugins.hpi; + +import com.google.common.io.ByteStreams; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import io.jenkins.lib.versionnumber.JavaSpecificationVersion; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; + +/** + * Configure Surefire for the desired version of Java. + * + * @author Basil Crow + */ +@Mojo(name = "test-runtime", requiresDependencyResolution = ResolutionScope.TEST) +public class TestRuntimeMojo extends AbstractJenkinsMojo { + + /** + * Set to {@code true} when we are compiling the tests but not running them. + */ + @Parameter(property = "skipTests", defaultValue = "false") + private boolean skipTests; + + /** + * Set to {@code true} when we are neither compiling nor running the tests. + */ + @Parameter(property = "maven.test.skip", defaultValue = "false") + private boolean skip; + + @Override + public void execute() throws MojoExecutionException { + if (skipTests || skip) { + getLog().info("Tests are skipped."); + return; + } + setAddOpensProperty(); + setInsaneHookProperty(); + } + + private void setAddOpensProperty() throws MojoExecutionException { + if (JavaSpecificationVersion.forCurrentJVM().isOlderThan(new JavaSpecificationVersion("9"))) { + // nothing to do prior to JEP 261 + return; + } + + String manifestEntry = getManifestEntry(wrap(resolveJenkinsWar())); + if (manifestEntry == null) { + // core older than 2.339, ignore + return; + } + + String argLine = buildArgLine(manifestEntry); + getLog().info("Setting jenkins.addOpens to " + argLine); + project.getProperties().setProperty("jenkins.addOpens", argLine); + } + + @NonNull + private Artifact resolveJenkinsWar() throws MojoExecutionException { + DefaultArtifactCoordinate artifactCoordinate = new DefaultArtifactCoordinate(); + artifactCoordinate.setGroupId("org.jenkins-ci.main"); + artifactCoordinate.setArtifactId("jenkins-war"); + artifactCoordinate.setVersion(findJenkinsVersion()); + artifactCoordinate.setExtension("war"); + + try { + return artifactResolver + .resolveArtifact(session.getProjectBuildingRequest(), artifactCoordinate) + .getArtifact(); + } catch (ArtifactResolverException e) { + throw new MojoExecutionException("Couldn't download artifact: ", e); + } + } + + @CheckForNull + private static String getManifestEntry(MavenArtifact artifact) throws MojoExecutionException { + File war = artifact.getFile(); + try (JarFile jarFile = new JarFile(war)) { + Manifest manifest = jarFile.getManifest(); + if (manifest == null) { + throw new MojoExecutionException("No manifest found in " + war); + } + return manifest.getMainAttributes().getValue("Add-Opens"); + } catch (IOException e) { + throw new MojoExecutionException("Failed to read MANIFEST.MF from " + war, e); + } + } + + @NonNull + private static String buildArgLine(String manifestEntry) { + List arguments = new ArrayList<>(); + for (String module : manifestEntry.split("\\s+")) { + if (!module.isEmpty()) { + arguments.add("--add-opens"); + arguments.add(module + "=ALL-UNNAMED"); + } + } + return String.join(" ", arguments); + } + + private void setInsaneHookProperty() throws MojoExecutionException { + Artifact insane = project.getArtifactMap().get("org.netbeans.modules:org-netbeans-insane"); + if (insane == null || Integer.parseInt(insane.getVersion().substring("RELEASE".length())) < 130) { + // older versions of insane do not need a hook + return; + } + + Artifact jth = project.getArtifactMap().get("org.jenkins-ci.main:jenkins-test-harness"); + if (jth == null) { + return; + } + + Path insaneHook = getInsaneHook(wrap(jth)); + + String argLine; + if (JavaSpecificationVersion.forCurrentJVM().isNewerThanOrEqualTo(new JavaSpecificationVersion("9"))) { + argLine = String.format("--patch-module=java.base=%s --add-exports=java.base/org.netbeans.insane.hook=ALL-UNNAMED", insaneHook); + } else { + argLine = String.format("-Xbootclasspath/p:%s", insaneHook); + } + getLog().info("Setting jenkins.insaneHook to " + argLine); + project.getProperties().setProperty("jenkins.insaneHook", argLine); + } + + @NonNull + private static Path getInsaneHook(MavenArtifact artifact) throws MojoExecutionException { + File jar = artifact.getFile(); + try (JarFile jarFile = new JarFile(jar)) { + ZipEntry entry = jarFile.getEntry("netbeans/harness/modules/ext/org-netbeans-insane-hook.jar"); + if (entry == null) { + throw new MojoExecutionException("Failed to find org-netbeans-insane-hook.jar in " + jar); + } + Path tempFile = Files.createTempFile("org-netbeans-insane-hook", ".jar"); + tempFile.toFile().deleteOnExit(); + try (InputStream is = jarFile.getInputStream(entry); OutputStream os = Files.newOutputStream(tempFile)) { + ByteStreams.copy(is, os); + } + return tempFile.toAbsolutePath(); + } catch (IOException e) { + throw new MojoExecutionException("Failed to read org-netbeans-insane-hook.jar from " + jar, e); + } + } +} diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateHpiMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateHpiMojo.java index 351c426adc..3043c5210a 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateHpiMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateHpiMojo.java @@ -2,6 +2,7 @@ import com.google.common.collect.Sets; import hudson.util.VersionNumber; +import java.io.File; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -17,41 +18,52 @@ */ @Mojo(name = "validate-hpi", defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyResolution = ResolutionScope.TEST) public class ValidateHpiMojo extends AbstractHpiMojo { + @Override public void execute() throws MojoExecutionException, MojoFailureException { - try { VersionNumber coreVersion = new VersionNumber(findJenkinsVersion()); MavenArtifact maxCoreVersionArtifact = null; VersionNumber maxCoreVersion = new VersionNumber("0"); for (MavenArtifact artifact : Sets.union(getProjectArtfacts(), getDirectDependencyArtfacts())) { - if (artifact.isPluginBestEffort(getLog())) { - VersionNumber dependencyCoreVersion = getDependencyCoreVersion(artifact); - if (dependencyCoreVersion.compareTo(maxCoreVersion) > 0) { - maxCoreVersionArtifact = artifact; - maxCoreVersion = dependencyCoreVersion; + try { + if (artifact.isPluginBestEffort(getLog())) { + VersionNumber dependencyCoreVersion = getDependencyCoreVersion(artifact); + if (dependencyCoreVersion.compareTo(maxCoreVersion) > 0) { + maxCoreVersionArtifact = artifact; + maxCoreVersion = dependencyCoreVersion; + } } + } catch (IOException e) { + throw new MojoExecutionException("Unable to retrieve manifest, artifactId: " + artifact.getArtifactId(), e); } } if (coreVersion.compareTo(maxCoreVersion) < 0) { throw new MojoExecutionException("Dependency " + maxCoreVersionArtifact + " requires Jenkins " + maxCoreVersion + " or higher."); } - } catch (IOException e) { - throw new MojoExecutionException("Unable to retrieve manifest", e); - } } private VersionNumber getDependencyCoreVersion(MavenArtifact artifact) throws IOException, MojoExecutionException { - Attributes mainAttributes = new JarFile(artifact.getFile()).getManifest().getMainAttributes(); - Attributes.Name jName = new Attributes.Name("Jenkins-Version"); - if (mainAttributes.containsKey(jName)) { - return new VersionNumber(mainAttributes.getValue(jName)); - } else { - Attributes.Name hName = new Attributes.Name("Hudson-Version"); - if (mainAttributes.containsKey(hName)) { - return new VersionNumber(mainAttributes.getValue(hName)); + File file = artifact.getFile(); + if (file.isFile()) { + Attributes mainAttributes; + try (JarFile jarFile = new JarFile(file)) { + mainAttributes = jarFile.getManifest().getMainAttributes(); + } + Attributes.Name jName = new Attributes.Name("Jenkins-Version"); + if (mainAttributes.containsKey(jName)) { + return new VersionNumber(mainAttributes.getValue(jName)); } else { - throw new MojoExecutionException("Could not find Jenkins Version in manifest for " + artifact); + Attributes.Name hName = new Attributes.Name("Hudson-Version"); + if (mainAttributes.containsKey(hName)) { + return new VersionNumber(mainAttributes.getValue(hName)); + } else { + throw new MojoExecutionException("Could not find Jenkins Version in manifest for " + artifact); + } } + } else { + getLog().warn("Skipping jenkins-core validation for " + artifact + " since we rely on sources and don't have a manifest. Use 'package' goal to get validation"); + // Assume the version is the same + return new VersionNumber(findJenkinsVersion()); } } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateMojo.java index 9ff1d03cc8..ee540c5e1d 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/ValidateMojo.java @@ -1,10 +1,11 @@ package org.jenkinsci.maven.plugins.hpi; import hudson.util.VersionNumber; +import io.jenkins.lib.versionnumber.JavaSpecificationVersion; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.project.MavenProject; /** * Make sure that we are running in the right environment. @@ -13,23 +14,27 @@ */ @Mojo(name = "validate", defaultPhase = LifecyclePhase.VALIDATE) public class ValidateMojo extends AbstractJenkinsMojo { - public void execute() throws MojoExecutionException, MojoFailureException { - if (!isMustangOrAbove()) - throw new MojoExecutionException("JDK6 or later is necessary to build a Jenkins plugin"); - if (new VersionNumber(findJenkinsVersion()).compareTo(new VersionNumber("1.419.99"))<=0) - throw new MojoExecutionException("This version of maven-hpi-plugin requires Jenkins 1.420 or later"); - } + @Override + public void execute() throws MojoExecutionException { + JavaSpecificationVersion javaVersion = getMinimumJavaVersion(); + if (JavaSpecificationVersion.forCurrentJVM().isOlderThan(javaVersion)) { + throw new MojoExecutionException("Java " + javaVersion + " or later is necessary to build this plugin."); + } + + if (new VersionNumber(findJenkinsVersion()).compareTo(new VersionNumber("2.204")) < 0) { + throw new MojoExecutionException("This version of maven-hpi-plugin requires Jenkins 2.204 or later"); + } - /** - * Are we running on JDK6 or above? - */ - private static boolean isMustangOrAbove() { - try { - Class.forName("javax.annotation.processing.Processor"); - return true; - } catch(ClassNotFoundException e) { - return false; + MavenProject parent = project.getParent(); + if (parent != null + && parent.getGroupId().equals("org.jenkins-ci.plugins") + && parent.getArtifactId().equals("plugin") + && !parent.getProperties().containsKey("java.level") + && project.getProperties().containsKey("java.level")) { + getLog().warn("Ignoring deprecated java.level property." + + " This property should be removed from your plugin's POM." + + " In the future this warning will be changed to an error and will break the build."); } } } diff --git a/src/main/java/org/jenkinsci/maven/plugins/hpi/WarMojo.java b/src/main/java/org/jenkinsci/maven/plugins/hpi/WarMojo.java index 02fa899ade..2f2f5c1c34 100644 --- a/src/main/java/org/jenkinsci/maven/plugins/hpi/WarMojo.java +++ b/src/main/java/org/jenkinsci/maven/plugins/hpi/WarMojo.java @@ -1,13 +1,15 @@ package org.jenkinsci.maven.plugins.hpi; import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Zip; import org.apache.tools.ant.types.ZipFileSet; @@ -18,15 +20,12 @@ import java.util.Optional; import java.util.Set; -import static org.apache.maven.plugins.annotations.LifecyclePhase.*; -import static org.apache.maven.plugins.annotations.ResolutionScope.*; - /** * Builds a custom Jenkins war that includes all the additional plugins referenced in this POM. * * @author Kohsuke Kawaguchi */ -@Mojo(name="custom-war", defaultPhase = PACKAGE, requiresDependencyResolution = RUNTIME) +@Mojo(name="custom-war", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME) public class WarMojo extends RunMojo { /** * Optional string that represents "groupId:artifactId" of Jenkins war. @@ -52,6 +51,7 @@ public class WarMojo extends RunMojo { * * @throws MojoExecutionException if an error occurred while building the webapp */ + @Override public void execute() throws MojoExecutionException { try { if (outputFile==null) @@ -84,7 +84,7 @@ public void execute() throws MojoExecutionException { // find corresponding .hpi file Artifact hpi = artifactFactory.createArtifact(a.getGroupId(),a.getArtifactId(),a.getVersion(),null,"hpi"); - artifactResolver.resolve(hpi,getProject().getRemoteArtifactRepositories(), localRepository); + hpi = artifactResolver.resolveArtifact(session.getProjectBuildingRequest(), hpi).getArtifact(); if (hpi.getFile().isDirectory()) throw new UnsupportedOperationException(hpi.getFile()+" is a directory and not packaged yet. this isn't supported"); @@ -99,9 +99,7 @@ public void execute() throws MojoExecutionException { getLog().info("Generated "+outputFile); projectHelper.attachArtifact(getProject(), "war", outputFile); - } catch (IOException e) { - throw new MojoExecutionException("Failed to package war",e); - } catch (AbstractArtifactResolutionException e) { + } catch (IOException | ArtifactResolverException e) { throw new MojoExecutionException("Failed to package war",e); } } diff --git a/src/main/resources/META-INF/plexus/components.xml b/src/main/resources/META-INF/plexus/components.xml index 79237e44ee..e00dfef3bb 100644 --- a/src/main/resources/META-INF/plexus/components.xml +++ b/src/main/resources/META-INF/plexus/components.xml @@ -12,13 +12,14 @@ default org.jenkins-ci.tools:maven-hpi-plugin:validate,org.jenkins-ci.tools:maven-hpi-plugin:validate-hpi + org.jenkins-ci.tools:maven-hpi-plugin:initialize org.apache.maven.plugins:maven-resources-plugin:resources org.apache.maven.plugins:maven-compiler-plugin:compile org.kohsuke:access-modifier-checker:enforce org.apache.maven.plugins:maven-resources-plugin:testResources org.jenkins-ci.tools:maven-hpi-plugin:insert-test org.apache.maven.plugins:maven-compiler-plugin:testCompile,org.jenkins-ci.tools:maven-hpi-plugin:test-hpl,org.jenkins-ci.tools:maven-hpi-plugin:resolve-test-dependencies - org.apache.maven.plugins:maven-surefire-plugin:test + org.jenkins-ci.tools:maven-hpi-plugin:test-runtime,org.apache.maven.plugins:maven-surefire-plugin:test org.jenkins-ci.tools:maven-hpi-plugin:hpi org.apache.maven.plugins:maven-install-plugin:install org.apache.maven.plugins:maven-deploy-plugin:deploy @@ -40,7 +41,7 @@ - + org.apache.maven.lifecycle.mapping.LifecycleMapping jm diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt index badb5d8260..37603c5ecb 100644 --- a/src/site/apt/index.apt +++ b/src/site/apt/index.apt @@ -5,19 +5,17 @@ Maven Jenkins Plugin - This maven2 plugin provides various goals for developing Jenkins plugins. + This Maven 3 plugin provides various goals for developing Jenkins plugins. * Common Goals Overview - * {{{./create-mojo.html}hpi:create}} Creates a skeleton of a new plugin. + * {{{./hpi-mojo.html}hpi:hpi}} Builds the <<<.hpi>>> file - * {{{./hpi-mojo.html}hpi:hpi}} Builds the .hpi file - - * {{{./hpl-mojo.html}hpi:hpl}} Generates the .hpl file + * {{{./hpl-mojo.html}hpi:hpl}} Generates the <<<.hpl>>> file * {{{./run-mojo.html}hpi:run}} Runs Jenkins with the current plugin project - For a list of all goals see the full {{{./plugin-info.html}plugin documentation}} + For a list of all goals see the full {{{./plugin-info.html}plugin documentation}}. - For more information about how to develop Jenkins plugins, refer to the {{{http://wiki.jenkins-ci.org/display/JENKINS/Extend+Jenkins}Wiki}} + For more information about how to develop Jenkins plugins, refer to the {{{https://www.jenkins.io/doc/developer/}docs site}}. diff --git a/src/site/site.xml b/src/site/site.xml index 0b54684262..e00db1ff07 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -17,7 +17,7 @@ org.apache.maven.skins maven-fluido-skin - 1.3.0 + 1.10.0 diff --git a/src/spotbugs/spotbugs-excludes.xml b/src/spotbugs/spotbugs-excludes.xml new file mode 100644 index 0000000000..ab99099058 --- /dev/null +++ b/src/spotbugs/spotbugs-excludes.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +