You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2022/01/07 18:27:07 UTC

[jackrabbit-filevault-jenkins-lib] branch master updated: JCRVLT-580 allow to parameterize Maven version

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault-jenkins-lib.git


The following commit(s) were added to refs/heads/master by this push:
     new 0643ca9  JCRVLT-580 allow to parameterize Maven version
0643ca9 is described below

commit 0643ca96faad4819fbeab55ac44f53591f746a78
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Jan 7 19:24:53 2022 +0100

    JCRVLT-580 allow to parameterize Maven version
    
    separate IT execution
    allow to configure each stage separately
---
 .gitignore                                         |   3 +
 README.md                                          |  19 ++--
 .../vault/AsfCloudbeesJenkinsEnvironment.groovy    |  49 +++++++++
 .../apache/jackrabbit/vault/PipelineSupport.groovy |  89 +++++++++++++++
 vars/fileVaultMavenStdBuild.groovy                 | 119 ---------------------
 vars/vaultPipeline.groovy                          |  40 +++++++
 vars/vaultStageBuild.groovy                        |  81 ++++++++++++++
 vars/vaultStageDeploy.groovy                       |  45 ++++++++
 vars/vaultStageIT.groovy                           |  56 ++++++++++
 9 files changed, 376 insertions(+), 125 deletions(-)

diff --git a/.gitignore b/.gitignore
index d8fe4fa..0accafe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
 /.project
+/bin/
+/.settings
+/.classpath
\ No newline at end of file
diff --git a/README.md b/README.md
index 0567512..9152f98 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,22 @@ This library is used for all FileVault Jenkins builds and enabled on <https://ci
 
 It follows the structure outlined at <https://www.jenkins.io/doc/book/pipeline/shared-libraries/>
 
-
 It is supposed to be called in a `Jenkinsfile` like this
 
 ```
-fileVaultMavenStdBuild([11, 8, 17], 11, [ "ubuntu", "Windows"], "ubuntu", "apache_jackrabbit-filevault")
+vaultPipeline('ubuntu', 11, '3', {
+   vaultStageBuild(['ubuntu', 'Windows'], [8, 11, 17], ['3', '3.6.3'], 'apache_jackrabbit-filevault-package-maven-plugin') 
+   vaultStageDeploy()
+  }
+)
 ```
 
-The first argument is an array of JDK versions to build with, the second one the main JDK version.
-The third argument is an array of node labels to build on, the fourth one the main node label.
-The fifth argument is the SonarCloud project key.
+The `vaultPipeline` step encapsulates the main build environment parameters:
+The first argument is the main *node label* to build with, the second one the main *JDK version*, third argument the main *Maven version*
+The fourth argument is a closure containing the actual stages where each may be one of
+
+1. `vaultStageBuild`: the actual Maven build and SonarQube execution (the latter only for the main environment)
+1. `vaultStageIT`: an isolated execution of just the integration tests
+1. `vaultStageDeploy`: the stage to deploy the previously built Maven artifacts to the ASF Snapshot Repository (depends on 1.)
 
-The main parameters specify on which environment the lengthy (environment independent) steps should happen like SonarQube analysis and optional deployment.
\ No newline at end of file
+For the parametrisation of those individual stages refer the source code.
\ No newline at end of file
diff --git a/src/org/apache/jackrabbit/vault/AsfCloudbeesJenkinsEnvironment.groovy b/src/org/apache/jackrabbit/vault/AsfCloudbeesJenkinsEnvironment.groovy
new file mode 100644
index 0000000..0ca356b
--- /dev/null
+++ b/src/org/apache/jackrabbit/vault/AsfCloudbeesJenkinsEnvironment.groovy
@@ -0,0 +1,49 @@
+/**
+ * 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.
+ */
+package org.apache.jackrabbit.vault
+
+class AsfCloudbeesJenkinsEnvironment {
+    
+    // always major.minor.qualifier version parts or just one (which means latest of that major version)
+    public static String getMavenLabel(boolean isWindows, String mavenVersion) {
+        final String versionLabel
+        if (mavenVersion ==~ /\d+/) {
+            versionLabel = "${mavenVersion}_latest"
+        } else if (mavenVersion ==~ /\d+\.\d+\.\d+/) {
+            // make sure it
+            final String suffix
+            if (isWindows) {
+                suffix = "_windows"
+            } else {
+                suffix = ""
+            }
+            versionLabel = "${mavenVersion}${suffix}"
+        } else {
+            throw new IllegalArgumentException('mavenVersion must be either one integer or three integers separated by dot')
+        }
+        // valid installation names in https://cwiki.apache.org/confluence/display/INFRA/Maven+Installation+Matrix and https://github.com/apache/infrastructure-p6/blob/production/modules/jenkins_client_master/files/hudson.tasks.Maven.xml
+        return "maven_${versionLabel}"
+    }
+
+    public static String getJdkLabel(int jdkVersion) {
+        // https://cwiki.apache.org/confluence/display/INFRA/JDK+Installation+Matrix
+        def availableJDKs = [ 8: 'jdk_1.8_latest', 9: 'jdk_1.9_latest', 10: 'jdk_10_latest', 11: 'jdk_11_latest', 12: 'jdk_12_latest', 13: 'jdk_13_latest', 14: 'jdk_14_latest', 15: 'jdk_15_latest', 16: 'jdk_16_latest', 17: 'jdk_17_latest', 18: 'jdk_18_latest', 19: 'jdk_19_latest']
+        return availableJDKs[jdkVersion]
+    }
+}
diff --git a/src/org/apache/jackrabbit/vault/PipelineSupport.groovy b/src/org/apache/jackrabbit/vault/PipelineSupport.groovy
new file mode 100644
index 0000000..4a353ec
--- /dev/null
+++ b/src/org/apache/jackrabbit/vault/PipelineSupport.groovy
@@ -0,0 +1,89 @@
+/**
+ * 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.
+ */
+package org.apache.jackrabbit.vault
+
+/**
+ * Singleton class encapsulating information about main build environment and some helper methods.
+ */
+class PipelineSupport implements Serializable {
+    
+    static PipelineSupport INSTANCE;
+    
+    static PipelineSupport createInstance(String mainNodeLabel, int mainJdkVersion, String mainMavenVersion, boolean isOnMainBranch) {
+        INSTANCE = new PipelineSupport(mainNodeLabel, mainJdkVersion, mainMavenVersion, isOnMainBranch)
+        return INSTANCE
+    }
+
+    static PipelineSupport getInstance() {
+        if (INSTANCE == null) {
+            throw new IllegalStateException("createInstance has not been called before, not wrapped in vaultPipeline step?")
+        }
+        return INSTANCE;
+    }
+
+    private final String mainNodeLabel;
+    private final Integer mainJdkVersion;
+    private final String mainMavenVersion;
+    private final boolean isOnMainBranch;
+
+    PipelineSupport(String mainNodeLabel, int mainJdkVersion, String mainMavenVersion, boolean isOnMainBranch) {
+        this.mainNodeLabel = mainNodeLabel
+        this.mainJdkVersion = mainJdkVersion;
+        this.mainMavenVersion = mainMavenVersion
+        this.isOnMainBranch = isOnMainBranch
+    }
+
+    def executeMaven(pipeline, String mavenArguments, boolean enablePublishers) {
+        executeMaven(pipeline, mainJdkVersion, mainMavenVersion, mavenArguments, enablePublishers)
+    }
+
+    static def executeMaven(pipeline, Integer jdkVersion, String mavenVersion, String mavenArguments, boolean enablePublishers) {
+        pipeline.withMaven(
+            maven: AsfCloudbeesJenkinsEnvironment.getMavenLabel(!pipeline.isUnix(), mavenVersion),
+            jdk: AsfCloudbeesJenkinsEnvironment.getJdkLabel(jdkVersion),
+            mavenLocalRepo: '.repository',
+            publisherStrategy: enablePublishers?'IMPLICIT':'EXPLICIT') {
+            if (pipeline.isUnix()) {
+                pipeline.sh "mvn -B ${mavenArguments}"
+            } else {
+                pipeline.bat "mvn -B ${mavenArguments}"
+            }
+        }
+    }
+
+    def stepsForMainAndAdditional(String stepsLabel, Set<String> nodeLabels, Set<Integer> jdkVersions, Set<String> mavenVersions, Closure closure) {
+        stepsFor(stepsLabel, nodeLabels.plus(mainNodeLabel), jdkVersions.plus(mainJdkVersion), mavenVersions.plus(mainMavenVersion), closure, false)
+    }
+
+    def stepsFor(String stepsLabel, Set<String> nodeLabels, Set<Integer> jdkVersions, Set<String> mavenVersions, Closure closure, boolean excludeMain = false) {
+        def stepsMap = [:]
+        for (nodeLabel in nodeLabels) {
+            for (jdkVersion in jdkVersions) {
+                for (mavenVersion in mavenVersions) {
+                    boolean isMainBuild = (nodeLabel.equals(mainNodeLabel) && jdkVersion.equals(mainJdkVersion) && mavenVersion.equals(mainMavenVersion))
+                    if (excludeMain && isMainBuild) {
+                        continue; // skip main environment
+                    }
+                    stepsMap["${stepsLabel} (JDK ${jdkVersion}, ${nodeLabel}, Maven ${mavenVersion}${isMainBuild ? ' (Main)' : ''})"] = closure(nodeLabel, jdkVersion, mavenVersion, isMainBuild)
+                }
+            }
+        }
+        return stepsMap
+    }
+}
\ No newline at end of file
diff --git a/vars/fileVaultMavenStdBuild.groovy b/vars/fileVaultMavenStdBuild.groovy
deleted file mode 100644
index 070b123..0000000
--- a/vars/fileVaultMavenStdBuild.groovy
+++ /dev/null
@@ -1,119 +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.
- */
-
-def isOnMainBranch() {
-    return env.BRANCH_NAME == 'master'
-}
-
-def executeMaven(String jdkLabel, String mavenArguments, String publisherStrategy) {
-    withMaven(
-        maven: 'maven_3_latest',
-        jdk: jdkLabel,
-        mavenLocalRepo: '.repository',
-        publisherStrategy: publisherStrategy) {
-        if (isUnix()) {
-            sh "mvn -B ${mavenArguments}"
-        } else {
-            bat "mvn -B ${mavenArguments}"
-        }
-    }
-}
-
-def buildStage(final int jdkVersion, final String nodeLabel, final boolean isMainBuild, final String sonarProjectKey) {
-    return {
-        // https://cwiki.apache.org/confluence/display/INFRA/JDK+Installation+Matrix
-        def availableJDKs = [ 8: 'jdk_1.8_latest', 9: 'jdk_1.9_latest', 10: 'jdk_10_latest', 11: 'jdk_11_latest', 12: 'jdk_12_latest', 13: 'jdk_13_latest', 14: 'jdk_14_latest', 15: 'jdk_15_latest', 16: 'jdk_16_latest', 17: 'jdk_17_latest', 18: 'jdk_18_latest']
-        final String jdkLabel = availableJDKs[jdkVersion]
-        final String wagonPluginGav = "org.codehaus.mojo:wagon-maven-plugin:2.0.2"
-        final String sonarPluginGav = "org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.0.2155"
-        node(label: nodeLabel) {
-            stage("${isMainBuild ? 'Main ' : ''}Maven Build (JDK ${jdkVersion}, ${nodeLabel})") {
-                timeout(60) {
-                    echo "Running on node ${env.NODE_NAME}"
-                    checkout scm
-                    try {
-                        String mavenArguments
-                        if (isMainBuild) {
-                            String localRepoPath = "${env.WORKSPACE}/local-snapshots-dir"
-                            // Make sure the directory is wiped.
-                            dir(localRepoPath) {
-                                deleteDir()
-                            }
-                            mavenArguments = "-U clean site deploy -DaltDeploymentRepository=snapshot-repo::default::file:${localRepoPath} -Pjacoco-report -Dlogback.configurationFile=vault-core/src/test/resources/logback-only-errors.xml"
-                        } else {
-                            mavenArguments = '-U clean verify site'
-                        }
-                        executeMaven(jdkLabel, mavenArguments, 'IMPLICIT')
-                        if (isMainBuild && isOnMainBranch()) {
-                            // Stash the build results so we can deploy them on another node
-                            stash name: 'local-snapshots-dir', includes: 'local-snapshots-dir/**'
-                        }
-                    } finally {
-                        junit '**/target/surefire-reports/**/*.xml,**/target/failsafe-reports*/**/*.xml'
-                    }
-                }
-            }
-            if (isMainBuild) {
-                stage("SonarCloud Analysis") {
-                    timeout(60) {
-                        withCredentials([string(credentialsId: 'sonarcloud-filevault-token', variable: 'SONAR_TOKEN')]) {
-                            String mavenArguments = "${sonarPluginGav}:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=${sonarProjectKey}"
-                            executeMaven(jdkLabel, mavenArguments, 'EXPLICIT')
-                        }
-                    }
-                }
-            }
-        }
-        if (isMainBuild && isOnMainBranch()) {
-            stage("Deployment") {
-                node('nexus-deploy') {
-                    timeout(60) {
-                        // Nexus deployment needs pom.xml
-                        checkout scm
-                        // Unstash the previously stashed build results.
-                        unstash name: 'local-snapshots-dir'
-                        // https://www.mojohaus.org/wagon-maven-plugin/merge-maven-repos-mojo.html
-                        String mavenArguments = "${wagonPluginGav}:merge-maven-repos -Dwagon.target=https://repository.apache.org/content/repositories/snapshots -Dwagon.targetId=apache.snapshots.https -Dwagon.source=file:${env.WORKSPACE}/local-snapshots-dir"
-                        executeMaven(jdkLabel, mavenArguments, 'EXPLICIT')
-                    }
-                }
-            }
-        }
-    }
-}
-
-def stagesFor(List<Integer> jdkVersions, int mainJdkVersion, List<String> nodeLabels, String mainNodeLabel, String sonarProjectKey) {
-    def stageMap = [:]
-    for (nodeLabel in nodeLabels) {
-        for (jdkVersion in jdkVersions) {
-            boolean isMainBuild = (jdkVersion == mainJdkVersion && nodeLabel == mainNodeLabel)
-            stageMap["JDK ${jdkVersion}, ${nodeLabel}${isMainBuild ? ' (Main)' : ''}"] = buildStage(jdkVersion, nodeLabel, isMainBuild, sonarProjectKey)
-        }
-    }
-    return stageMap
-}
-
-// valid node labels in https://cwiki.apache.org/confluence/display/INFRA/ci-builds.apache.org
-def call(List<Integer> jdkVersions, int mainJdkVersion, List<String> nodeLabels, String mainNodeLabel, String sonarProjectKey) {
-    // adjust some job properties (https://www.jenkins.io/doc/pipeline/steps/workflow-multibranch/#properties-set-job-properties)
-    properties([
-        buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10'))
-    ])
-    parallel stagesFor(jdkVersions, mainJdkVersion, nodeLabels, mainNodeLabel, sonarProjectKey)
-}
diff --git a/vars/vaultPipeline.groovy b/vars/vaultPipeline.groovy
new file mode 100644
index 0000000..44a34a2
--- /dev/null
+++ b/vars/vaultPipeline.groovy
@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+import org.apache.jackrabbit.vault.PipelineSupport
+
+// valid node labels in https://cwiki.apache.org/confluence/display/INFRA/ci-builds.apache.org
+def call(String mainNodeLabel, int mainJdkVersion, String mainMavenVersion, Closure body) {
+    PipelineSupport pipelineSupport = PipelineSupport.createInstance(mainNodeLabel, mainJdkVersion, mainMavenVersion, env.BRANCH_NAME == 'master')
+    // adjust some job properties (https://www.jenkins.io/doc/pipeline/steps/workflow-multibranch/#properties-set-job-properties)
+    def buildProperties = []
+    if (pipelineSupport.isOnMainBranch) {
+      echo "Building main branch ${env.BRANCH_NAME}"
+      // set build retention time first
+      buildProperties.add(buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '5', daysToKeepStr: '15', numToKeepStr: '10')))
+      // ensure a build is done every month
+      buildProperties.add(pipelineTriggers([cron('@monthly')]))
+    } else {
+      echo "Building auxiliary branch ${env.BRANCH_NAME}"
+      buildProperties.add(buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '2', daysToKeepStr: '7', numToKeepStr: '3')))
+    }
+    properties(buildProperties)
+    body()
+}
+
diff --git a/vars/vaultStageBuild.groovy b/vars/vaultStageBuild.groovy
new file mode 100644
index 0000000..a0f5667
--- /dev/null
+++ b/vars/vaultStageBuild.groovy
@@ -0,0 +1,81 @@
+/**
+ * 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.
+ */
+
+import org.apache.jackrabbit.vault.PipelineSupport
+
+// valid node labels in https://cwiki.apache.org/confluence/display/INFRA/ci-builds.apache.org
+def call(List<String> additionalNodeLabels, List<Integer> additionalJdkVersions, List<String> additionalMavenVersions, String sonarProjectKey, Map options=[:]) {
+    boolean hasSeparateItExecution = options.hasSeparateItExecution ?: false
+    String mainBuildArguments = options.mainBuildArguments ?: "-U clean site deploy -Pjacoco-report -Dlogback.configurationFile=vault-core/src/test/resources/logback-only-errors.xml"
+    String additionalBuildArguments = options.additionalBuildArguments ?: "-U clean ${hasSeparateItExecution?'package':'verify'} site"
+    PipelineSupport pipelineSupport = PipelineSupport.getInstance()
+    parallel pipelineSupport.stepsForMainAndAdditional('Maven Build', additionalNodeLabels.toSet(), additionalJdkVersions.toSet(), additionalMavenVersions.toSet(), 
+        { String nodeLabel, Integer jdkVersion, String mavenVersion, boolean isMainBuild -> 
+            return {
+                final String sonarPluginGav = "org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184"
+                node(label: nodeLabel) {
+                    stage("${isMainBuild ? 'Main ' : ''}Maven Build (JDK ${jdkVersion}, Maven ${mavenVersion}, ${nodeLabel})") {
+                        timeout(60) {
+                            echo "Running on node ${env.NODE_NAME}"
+                            checkout scm
+                            try {
+                                String mavenArguments
+                                if (isMainBuild) {
+                                    String localRepoPath = "${env.WORKSPACE}/local-snapshots-dir"
+                                    // Make sure the directory is wiped.
+                                    dir(localRepoPath) {
+                                        deleteDir()
+                                    }
+                                    // main build with IT for properly calculating coverage
+                                    mavenArguments = "${mainBuildArguments} -DaltDeploymentRepository=snapshot-repo::default::file:${localRepoPath}"
+                                } else {
+                                    mavenArguments = additionalBuildArguments
+                                }
+                                PipelineSupport.executeMaven(this, jdkVersion, mavenVersion, mavenArguments, false)
+                                if (isMainBuild) {
+                                    if (hasSeparateItExecution) {
+                                        // stash the integration test classes and the build artifact for later execution
+                                        stash name: 'integration-test-classes', includes: '**/target/test-classes/**,**/target/*.jar'
+                                    }
+                                    if (pipelineSupport.isOnMainBranch) {
+                                        // Stash the build results so we can deploy them on another node
+                                        stash name: 'local-snapshots-dir', includes: 'local-snapshots-dir/**'
+                                    }
+                                }
+                            } catch (Throwable e) {
+                                 error 'Error during building ' + e.toString()
+                            }  finally {
+                                junit '**/target/surefire-reports/**/*.xml,**/target/failsafe-reports*/**/*.xml'
+                            }
+                        }
+                    }
+                    if (isMainBuild) {
+                        stage("SonarCloud Analysis") {
+                            timeout(60) {
+                                withCredentials([string(credentialsId: 'sonarcloud-filevault-token', variable: 'SONAR_TOKEN')]) {
+                                    String mavenArguments = "${sonarPluginGav}:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=${sonarProjectKey}"
+                                    pipelineSupport.executeMaven(this, mavenArguments, false)
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } )
+}
diff --git a/vars/vaultStageDeploy.groovy b/vars/vaultStageDeploy.groovy
new file mode 100644
index 0000000..06efc20
--- /dev/null
+++ b/vars/vaultStageDeploy.groovy
@@ -0,0 +1,45 @@
+/**
+ * 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.
+ */
+
+import org.apache.jackrabbit.vault.PipelineSupport
+
+
+def deployStage(final PipelineSupport pipelineSupport) {
+    final String wagonPluginGav = "org.codehaus.mojo:wagon-maven-plugin:2.0.2"
+    stage("Deployment to Maven Repository") {
+        node('nexus-deploy') {
+            timeout(60) {
+                // Nexus deployment needs pom.xml
+                checkout scm
+                // Unstash the previously stashed build results.
+                unstash name: 'local-snapshots-dir'
+                // https://www.mojohaus.org/wagon-maven-plugin/merge-maven-repos-mojo.html
+                String mavenArguments = "${wagonPluginGav}:merge-maven-repos -Dwagon.target=https://repository.apache.org/content/repositories/snapshots -Dwagon.targetId=apache.snapshots.https -Dwagon.source=file:${env.WORKSPACE}/local-snapshots-dir"
+                pipelineSupport.executeMaven(this, mavenArguments, false)
+            }
+        }
+    }
+}
+
+def call() {
+    PipelineSupport pipelineSupport = PipelineSupport.getInstance()
+    if (pipelineSupport.isOnMainBranch) {
+        deployStage(pipelineSupport)
+    }
+}
\ No newline at end of file
diff --git a/vars/vaultStageIT.groovy b/vars/vaultStageIT.groovy
new file mode 100644
index 0000000..09cc9e5
--- /dev/null
+++ b/vars/vaultStageIT.groovy
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ */
+
+import org.apache.jackrabbit.vault.PipelineSupport
+import java.io.File
+import groovy.io.FileType
+
+// valid node labels in https://cwiki.apache.org/confluence/display/INFRA/ci-builds.apache.org
+def call(List<String> additionalNodeLabels, List<Integer> additionalJdkVersions, List<String> additionalMavenVersions) {
+    PipelineSupport pipelineSupport = PipelineSupport.getInstance()
+    parallel pipelineSupport.stepsFor('Integration Tests', additionalNodeLabels.toSet(), additionalJdkVersions.toSet(), additionalMavenVersions.toSet(), 
+        {String nodeLabel, int jdkVersion, String mavenVersion, boolean isMainBuild -> 
+            return {
+                stage("Run Integration Tests  (JDK ${jdkVersion}, Maven ${mavenVersion}, ${nodeLabel})") {
+                    node(nodeLabel) {
+                        timeout(60) {
+                            // running ITs needs pom.xml
+                            checkout scm
+                            // Unstash the previously stashed build results.
+                            unstash name: 'integration-test-classes'
+                            try {
+                                // install to be tested artifact to local repository
+                                // https://www.jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#findfiles-find-files-in-the-workspace
+                                def jarFiles = findFiles(glob: '**/target/*.jar')
+                                if (jarFiles.length == 0) {
+                                    echo "Found no JAR artifact to install in local repository"
+                                } else {
+                                    pipelineSupport.executeMaven(this, "install:install-file -Dfile=${jarFiles[0].path} -DpomFile=pom.xml", false)
+                                }
+                                // execute ITs
+                                pipelineSupport.executeMaven(this, jdkVersion, mavenVersion, 'failsafe:integration-test failsafe:verify', false)
+                            } finally {
+                                junit '**/target/failsafe-reports*/**/*.xml'
+                            }
+                        }
+                    }
+                }
+            }
+        } )
+}