Skip to content
Snippets Groups Projects
Jenkinsfile 12 KiB
Newer Older
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

// It's assumed that Jenkins has been configured to implicitly load the vars/*.groovy libraries.
// Note that the version used is the one defined in Jenkins but it can be overridden as follows:
// @Library("XWiki@<branch, tag, sha1>") _
// See https://github.com/jenkinsci/workflow-cps-global-lib-plugin for details.

// Definitions of all builds
def builds = [
  'Main' : {
    build(
      name: 'Main',
      profiles: 'legacy,integration-tests,snapshot',
        '-Dxwiki.checkstyle.skip=true -Dxwiki.surefire.captureconsole.skip=true -Dxwiki.revapi.skip=true -DskipITs'
  // Can be used to manually trigger the main build with integration tests on the CI.
  'Main with Integration Tests' : {
    build(
      profiles: 'legacy,integration-tests,snapshot',
      properties:
        '-Dxwiki.checkstyle.skip=true -Dxwiki.surefire.captureconsole.skip=true -Dxwiki.revapi.skip=true'
  'Distribution' : {
    build(
      name: 'Distribution',
      profiles: 'legacy,integration-tests,snapshot',
      pom: 'xwiki-platform-distribution/pom.xml'
    )
  },
  'Flavor Test - POM' : {
    buildFunctionalTest(
      name: 'Flavor Test - POM',
      pom: 'pom.xml',
      properties: '-N'
    )
  },
  'Flavor Test - PageObjects' : {
    buildFunctionalTest(
      name: 'Flavor Test - PageObjects',
      pom: 'xwiki-platform-distribution-flavor-test-pageobjects/pom.xml'
    )
  },
  'Flavor Test - UI' : {
    buildFunctionalTest(
      name: 'Flavor Test - UI',
      pom: 'xwiki-platform-distribution-flavor-test-ui/pom.xml',
    )
  },
  'Flavor Test - Misc' : {
    buildFunctionalTest(
      name: 'Flavor Test - Misc',
      pom: 'xwiki-platform-distribution-flavor-test-misc/pom.xml'
    )
  },
  'Flavor Test - Storage': {
    buildFunctionalTest(
      name: 'Flavor Test - Storage',
      pom: 'xwiki-platform-distribution-flavor-test-storage/pom.xml'
    )
  },
  'Flavor Test - Escaping' : {
    buildFunctionalTest(
      name: 'Flavor Test - Escaping',
      pom: 'xwiki-platform-distribution-flavor-test-escaping/pom.xml'
    )
  },
  'Flavor Test - Upgrade' : {
    buildFunctionalTest(
      name: 'Flavor Test - Upgrade',
      pom: 'xwiki-platform-distribution-flavor-test-upgrade/pom.xml'
    )
  },
  'Flavor Test - Webstandards' : {
    buildFunctionalTest(
      name: 'Flavor Test - Webstandards',
      pom: 'xwiki-platform-distribution-flavor-test-webstandards/pom.xml',
      mavenOpts: '-Xmx2048m -Xms512m -XX:ThreadStackSize=2048'
  'Flavor Test - Security' : {
    buildFunctionalTest(
      name: 'Flavor Test - Security',
      pom: 'xwiki-platform-distribution-flavor-test-security/pom.xml'
  'TestRelease': {
    build(
      name: 'TestRelease',
      goals: 'clean install',
      profiles: 'release,hsqldb,jetty,legacy,integration-tests,standalone,flavor-integration-tests,distribution,docker',
      properties: '-DskipTests -DperformRelease=true -Dgpg.skip=true -Dxwiki.checkstyle.skip=true -Dxwiki.revapi.skip=true -Dxwiki.enforcer.skip=true -Dxwiki.spoon.skip=true -Ddoclint=all'
    node() {
      // Run the quality checks.
      // Notes for step 1:
      // - The build executes jacoco to generate a single jacoco exec file containing the results of the coverage
      //   for all tests from all modules. This why we need -Pcoverage and -Dxwiki.jacoco.itDestFile.
      buildInsideNode(
        name: 'Quality Step 1',
        goals: 'clean install',
        profiles: 'repository-snapshots,quality,legacy,coverage',
        properties: '-Dxwiki.jacoco.itDestFile=`pwd`/target/jacoco-it.exec'
      )
      // Notes for step 2:
      // - We generate the jacoco reports for all modules (all from the single jacoco-it.exec file)
      // - We then generate the sonar analysis and upload to Sonarcloud with the sonar:sonar goal.
      // - Sonar uses the jacoco report files and it's thus important that the sonar:sonar goal is executed after
      //   the jacoco:report one.
      buildInsideNode(
        name: 'Quality Step 2',
        goals: 'jacoco:report sonar:sonar',
        profiles: 'repository-snapshots,quality,legacy,coverage',
        properties: '-Dxwiki.jacoco.itDestFile=`pwd`/target/jacoco-it.exec'
      )
    }
// Let the user select the build to execute if manually triggered. Otherwise, build everything.
stage('Platform Builds') {
  def choices = builds.collect { k,v -> "$k" }.join('\n')
  // askUser() will check if the build was triggered manually or not. If not, it'll default to 'All'.
  def selection = askUser(choices)
  if (selection == 'All') {
    buildStandardAll(builds)
  } else {
    buildStandardSingle(builds[selection])
private void buildStandardSingle(build)
private void buildStandardAll(builds)
      // Build, skipping quality checks and integration tests (but execute unit tests) so that the result of the build
      // can be sent as fast as possible to the devs. Note that we skip integration tests by using the FailSafe plugin
      // property "DskipITs".
      // In addition, we want the generated artifacts to be deployed to our remote Maven repository so that developers
      // can benefit from them even though some quality checks have not yet passed.
      // In // we start a build with the quality profile that executes various quality checks, and we run all the
      // integration tests just after this build.
      // Note: We configure the snapshot extension repository in XWiki (-Psnapshots) in the generated
      // distributions to make it easy for developers to install snapshot extensions when they do manual tests.
      builds['Main'].call()
Vincent Massol's avatar
Vincent Massol committed
      parallel(
        'integration-tests' : {
          // Run all integration tests, with each module in its own node to parallelize the work.
          runIntegrationTests()
        },
        'distribution' : {
          // Note: We want the following behavior:
          // - if an error occurs during the previous build we don't want the subsequent builds to execute. This will
          //   happen since Jenkins will throw an exception and we don't catch it.
          // - if the previous build has failures (e.g. test execution failures), we want subsequent builds to execute
          //   since failures can be test flickers for ex, and it could still be interesting to get a distribution to
          //   test xwiki manually.
          // Build the distributions
          builds['Distribution'].call()
Vincent Massol's avatar
Vincent Massol committed
          // Building the various functional tests, after the distribution has been built successfully.
Vincent Massol's avatar
Vincent Massol committed
          // Build the Flavor Test POM, required for the pageobjects module below.
          builds['Flavor Test - POM'].call()
Vincent Massol's avatar
Vincent Massol committed
          // Build the Flavor Test PageObjects required by the functional test below that need an XWiki UI
          builds['Flavor Test - PageObjects'].call()
Vincent Massol's avatar
Vincent Massol committed
          // Now run all tests in parallel
          parallel(
            'flavor-test-ui': {
              // Run the Flavor UI tests
              builds['Flavor Test - UI'].call()
            },
            'flavor-test-misc': {
              // Run the Flavor Misc tests
              builds['Flavor Test - Misc'].call()
            },
            'flavor-test-storage': {
              // Run the Flavor Storage tests
              builds['Flavor Test - Storage'].call()
            },
            'flavor-test-escaping': {
              // Run the Flavor Escaping tests
              builds['Flavor Test - Escaping'].call()
            },
            'flavor-test-webstandards': {
              // Run the Flavor Webstandards tests
              // Note: -XX:ThreadStackSize=2048 is used to prevent a StackOverflowError error when using the HTML5 Nu
              // Validator (see https://bitbucket.org/sideshowbarker/vnu/issues/4/stackoverflowerror-error-when-running)
              builds['Flavor Test - Webstandards'].call()
            },
            'flavor-test-upgrade': {
              // Run the Flavor Upgrade tests
              builds['Flavor Test - Upgrade'].call()
            },
            'flavor-test-security': {
              // Run the Flavor Security tests
              builds['Flavor Test - Security'].call()
      // Simulate a release and verify all is fine, in preparation for the release day.
      builds['TestRelease'].call()
    'quality': {
      // Run the quality checks
      builds['Quality'].call()
private void runIntegrationTests()
{
  def itModuleList
  node() {
    // Checkout platform to find all IT modules so that we can then parallelize executions across Jenkins agents.
    checkout skipChangeLog: true, scm: scm
Vincent Massol's avatar
Vincent Massol committed
    itModuleList = itModules()
  }

  xwikiITBuild {
    modules = itModuleList
  node(map.node ?: '') {
    buildInsideNode(map)
private void buildInsideNode(map)
    // We want to get a memory dump on OOM errors.
    // Make sure the memory dump directory exists (see below)
    // Note that the user used to run the job on the agent must have the permission to create these directories
    // Verify existence of /home/hudsonagent/jenkins_root so that we only set the oomPath if it does
    def heapDumpPath = ''
    def exists = fileExists '/home/hudsonagent/jenkins_root'
    if (exists) {
        def oomPath = "/home/hudsonagent/jenkins_root/oom/maven/${env.JOB_NAME}-${currentBuild.id}"
        sh "mkdir -p \"${oomPath}\""
        heapDumpPath = "-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"${oomPath}\""
    }
      mavenOpts = map.mavenOpts ?: "-Xmx2048m -Xms512m ${heapDumpPath}"
Vincent Massol's avatar
Vincent Massol committed
      if (map.properties != null) {
      // Keep builds for 30 days since we want to be able to see all builds if there are a lot at a given time, to be
      // able to identify flickers, etc.
      // We don't need to trigger xwiki-platform monthly since it's already the case of xwiki-commons
      monthlyTrigger = false
        skipCheckout = map.skipCheckout
      }
      // Avoid duplicate changelogs in jenkins job execution UI page
      if (map.name != 'Main') {
        skipChangeLog = true
      }
      if (map.javaTool != null) {
Vincent Massol's avatar
Vincent Massol committed
        javaTool = map.javaTool
private void buildFunctionalTest(map)
Vincent Massol's avatar
Vincent Massol committed
{
  def sharedPOMPrefix =
    'xwiki-platform-distribution/xwiki-platform-distribution-flavor/xwiki-platform-distribution-flavor-test'

  build(
    name: map.name,
    profiles: 'legacy,integration-tests,jetty,hsqldb,firefox',
    mavenOpts: map.mavenOpts,
    pom: "${sharedPOMPrefix}/${map.pom}",
    properties: map.properties