From 9e597bb9e077530e065dff5893acb0a070f733c0 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 13 Jan 2021 13:30:44 +0100 Subject: [PATCH] Start Jenkinsfile migration --- Jenkinsfile | 369 +++++++++++++++------------------- scm-ui/e2e-tests/src/index.js | 2 +- 2 files changed, 166 insertions(+), 205 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9acca06822..a2c4289169 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,244 +1,205 @@ #!groovy +pipeline { -// switch back to a stable tag, after pr 22 is mreged an the next version is released -// see https://github.com/cloudogu/ces-build-lib/pull/22 -@Library('github.com/cloudogu/ces-build-lib@7a14da6') -import com.cloudogu.ces.cesbuildlib.* - -node('docker') { - - developmentBranch = 'develop' - mainBranch = 'master' - - properties([ - // Keep only the last 10 build to preserve space - buildDiscarder(logRotator(numToKeepStr: '10')), + options { + buildDiscarder(logRotator(numToKeepStr: '10')) disableConcurrentBuilds() - ]) + } - timeout(activity: true, time: 60, unit: 'MINUTES') { + agent { + docker { + image 'scmmanager/java-build:11.0.9_11.1' + label 'docker' + } + } - Git git = new Git(this) + environment { + HOME = "${env.WORKSPACE}" + SONAR_USER_HOME = "${env.WORKSPACE}/.sonar" + } - catchError { + stages { - Maven mvn = setupMavenBuild() - - stage('Checkout') { - checkout scm + stage('Set Version') { + when { + branch pattern: 'release/*', comparator: 'GLOB' } + steps { + // read version from branch, set it and commit it + sh "./gradlew setVersion -PnewVersion ${releaseVersion}" + sh "git add gradle.properties lerna.json '**.json'" + commit "Release version ${releaseVersion}" - if (isReleaseBranch()) { - stage('Set Version') { - String releaseVersion = getReleaseVersion(); - // set maven versions - mvn "versions:set -DgenerateBackupPoms=false -DnewVersion=${releaseVersion}" - // set versions for ui packages - // we need to run 'yarn install' in order to set version with ui-scripts - mvn "-pl :scm-ui buildfrontend:install@install" - mvn "-pl :scm-ui buildfrontend:run@set-version" + // fetch all remotes from origin + sh 'git config --replace-all "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*"' + sh 'git fetch --all' - // stage pom changes - sh "git status --porcelain | sed s/^...// | grep pom.xml | xargs git add" - // stage package.json changes - sh "git status --porcelain | sed s/^...// | grep package.json | xargs git add" - // stage lerna.json changes - sh "git add lerna.json" + // checkout, reset and merge + sh 'git checkout master' + sh 'git reset --hard origin/master' + sh "git merge --ff-only ${env.BRANCH_NAME}" - // commit changes - sh "git -c user.name='CES Marvin' -c user.email='cesmarvin@cloudogu.com' commit -m 'release version ${releaseVersion}'" - - // we need to fetch all branches, so we can checkout master and develop later - sh "git config 'remote.origin.fetch' '+refs/heads/*:refs/remotes/origin/*'" - sh "git fetch --all" - - // merge release branch into main branch - sh "git checkout ${mainBranch}" - sh "git reset --hard origin/${mainBranch}" - sh "git merge --ff-only ${env.BRANCH_NAME}" - - // set tag - sh "git -c user.name='CES Marvin' -c user.email='cesmarvin@cloudogu.com' tag -m 'release version ${releaseVersion}' ${releaseVersion}" - } + // set tag + tag releaseVersion } + } - stage('Build') { - mvn "clean install -DskipTests" + stage('Build') { + steps { + // build without tests + sh "./gradlew -xtest build" } + } - parallel( - unitTest: { - stage('Unit Test') { - mvn 'test -DskipFrontendBuild -DskipTypecheck -Pcoverage -pl !scm-it -Dmaven.test.failure.ignore=true' - junit allowEmptyResults: true, testResults: '**/target/surefire-reports/TEST-*.xml,**/target/jest-reports/TEST-*.xml' - } - }, - integrationTest: { - stage('Integration Test') { - mvn 'verify -Pit -DskipUnitTests -pl :scm-webapp,:scm-it -Dmaven.test.failure.ignore=true' - junit allowEmptyResults: true, testResults: '**/target/failsafe-reports/TEST-*.xml,**/target/cypress-reports/TEST-*.xml' - archiveArtifacts allowEmptyArchive: true, artifacts: 'scm-ui/e2e-tests/cypress/videos/*.mp4' - archiveArtifacts allowEmptyArchive: true, artifacts: 'scm-ui/e2e-tests/cypress/screenshots/**/*.png' - } - } - ) - - stage('SonarQube') { - def sonarQube = new SonarCloud(this, [sonarQubeEnv: 'sonarcloud.io-scm', sonarOrganization: 'scm-manager', integrationBranch: 'develop']) - sonarQube.analyzeWith(mvn) + stage('Check') { + steps { + sh "./gradlew check" + junit allowEmptyResults: true, testResults: '**/build/test-results/test/TEST-*.xml,**/build/test-results/tests/test/TEST-*.xml,**/build/jest-reports/TEST-*.xml' } + } - if (isBuildSuccessful() && (isDevelopmentBranch() || isReleaseBranch())) { - def commitHash = git.getCommitHash() + // in parallel with check? + stage('Integration Tests') { + steps { + sh "./gradlew integrationTest" + junit allowEmptyResults: true, testResults: 'scm-it/build/test-results/javaIntegrationTests/*.xml,scm-ui/build/reports/e2e/*.xml' + archiveArtifacts allowEmptyArchive: true, artifacts: 'scm-ui/e2e-tests/cypress/videos/*.mp4' + archiveArtifacts allowEmptyArchive: true, artifacts: 'scm-ui/e2e-tests/cypress/screenshots/**/*.png' + } + } - def imageVersion = mvn.getVersion() - if (imageVersion.endsWith('-SNAPSHOT')) { - imageVersion = imageVersion.replace('-SNAPSHOT', "-${commitHash.substring(0,7)}-${BUILD_NUMBER}") - } - - stage('Deployment') { - // configuration for docker deployment - mvn.useRepositoryCredentials([ - id: 'docker.io', - credentialsId: 'hub.docker.com-cesmarvin' - ]) - - mvn.useRepositoryCredentials([ - id: 'github.com/scm-manager/website', - credentialsId: 'cesmarvin-github' - ]) - - // deploy java artifacts - mvn.useDeploymentRepository([ - id: 'packages.scm-manager.org', - url: 'https://packages.scm-manager.org', - credentialsId: 'maven.scm-manager.org', - snapshotRepository: '/repository/snapshots/', - releaseRepository: '/repository/releases/', - type: 'Configurable' - ]) - mvn.deployToNexusRepository() - - // deploy frontend bits - withCredentials([string(credentialsId: 'cesmarvin_npm_token', variable: 'NPM_TOKEN')]) { - writeFile encoding: 'UTF-8', file: '.npmrc', text: "//registry.npmjs.org/:_authToken='${NPM_TOKEN}'" - writeFile encoding: 'UTF-8', file: '.yarnrc', text: ''' - registry "https://registry.npmjs.org/" - always-auth true - email cesmarvin@cloudogu.com - '''.trim() - - // we are tricking lerna by pretending that we are not a git repository - sh "mv .git .git.disabled" - try { - mvn "-pl :scm-ui buildfrontend:run@deploy" - } finally { - sh "mv .git.disabled .git" - } - } - - // deploy packages - withGPGEnvironment { - mvn "-Dgpg.scm.keyring='${GPG_KEYRING}' -Dgpg.scm.key='${GPG_KEY_ID}' -Dgpg.scm.passphrase='${GPG_KEY_PASSPHRASE}' -Ppackaging -rf :scm-packaging deploy" - } - } - - stage('Presentation Environment') { - // we don't use developmentBranch, because we only want the lastest version of develop branch on - // next-scm. We don't want a support branch or something similar on the presentation environment. - if ("develop".equals(env.BRANCH_NAME)) { - build job: 'scm-manager/next-scm.cloudogu.com', propagate: false, wait: false, parameters: [ - string(name: 'changeset', value: commitHash), - string(name: 'imageTag', value: imageVersion) - ] - } - } - - if (isReleaseBranch()) { - stage('Update Repository') { - - // merge changes into develop - sh "git checkout ${developmentBranch}" - - // TODO what if we have a conflict - // e.g.: someone has edited the changelog during the release - if (!developmentBranch.equals(mainBranch)) { - sh "git merge ${mainBranch}" - } - - - // set versions for maven packages - mvn "build-helper:parse-version versions:set -DgenerateBackupPoms=false -DnewVersion='\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.0-SNAPSHOT'" - - // set versions for ui packages - mvn "-pl :scm-ui buildfrontend:run@set-version" - - // stage pom changes - sh "git status --porcelain | sed s/^...// | grep pom.xml | xargs git add" - // stage package.json changes - sh "git status --porcelain | sed s/^...// | grep package.json | xargs git add" - // stage lerna.json changes - sh "git add lerna.json" - - // commit changes - sh "git -c user.name='CES Marvin' -c user.email='cesmarvin@cloudogu.com' commit -m 'prepare for next development iteration'" - - // push changes back to remote repository - withCredentials([usernamePassword(credentialsId: 'cesmarvin-github', usernameVariable: 'GIT_AUTH_USR', passwordVariable: 'GIT_AUTH_PSW')]) { - sh "git -c credential.helper=\"!f() { echo username='\$GIT_AUTH_USR'; echo password='\$GIT_AUTH_PSW'; }; f\" push origin ${mainBranch} --tags" - if (!developmentBranch.equals(mainBranch)) { - sh "git -c credential.helper=\"!f() { echo username='\$GIT_AUTH_USR'; echo password='\$GIT_AUTH_PSW'; }; f\" push origin develop --tags" - } - sh "git -c credential.helper=\"!f() { echo username='\$GIT_AUTH_USR'; echo password='\$GIT_AUTH_PSW'; }; f\" push origin :${env.BRANCH_NAME}" + stage('SonarQube') { + steps { + sh 'git config --replace-all "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*"' + sh 'git fetch origin master' + sh "./gradlew integrationTest" + script { + withSonarQubeEnv('sonarcloud.io-scm') { + String sonar = "sonarqube -Dsonar.organization=scm-manager -Dsonar.branch.name=${script.env.BRANCH_NAME}" + if (script.env.BRANCH_NAME != "master") { + sonar += " -Dsonar.branch.target=master" } + // disable until we know how todo sonar analysis with subprojects + // sh "./gradlew sonarqube" } } } } - mailIfStatusChanged(git.commitAuthorEmail) + stage('Deployment') { + when { + branch pattern: 'release/*', comparator: 'GLOB' + // TODO or develop + expression { return isBuildSuccess() } + } + steps { + withPublishProperies { + sh "./gradlew ${PUBLISH_PROPERTIES}" + } + } + } + + stage('Push Tag') { + when { + branch pattern: 'release/*', comparator: 'GLOB' + expression { return isBuildSuccess() } + } + steps { + // push changes back to remote repository + authGit 'cesmarvin-github', 'push origin master --tags' + authGit 'cesmarvin-github', 'push origin --tags' + } + } + + + stage('Set Next Version') { + when { + branch pattern: 'release/*', comparator: 'GLOB' + expression { return isBuildSuccess() } + } + steps { + sh returnStatus: true, script: "git branch -D develop" + sh "git checkout develop" + sh "git merge master" + + sh "./gradlew setVersionToNextSnapshot" + + sh "git add gradle.properties lerna.json '**.json'" + commit 'Prepare for next development iteration' + authGit 'cesmarvin-github', 'push origin develop' + } + } + + stage('Delete Release Branch') { + when { + branch pattern: 'release/*', comparator: 'GLOB' + expression { return isBuildSuccess() } + } + steps { + authGit 'cesmarvin-github', "push origin :${env.BRANCH_NAME}" + } + } + } -} - -String developmentBranch -String mainBranch - -Maven setupMavenBuild() { - MavenWrapperInDocker mvn = new MavenWrapperInDocker(this, "scmmanager/java-build:11.0.9_11.1") - mvn.enableDockerHost = true - - // disable logging durring the build - def logConf = "scm-webapp/src/main/resources/logback.ci.xml" - mvn.additionalArgs += " -Dlogback.configurationFile=${logConf}" - mvn.additionalArgs += " -Dscm-it.logbackConfiguration=${logConf}" - mvn.additionalArgs += " -Dsonar.coverage.exclusions=**/*.test.ts,**/*.test.tsx,**/*.stories.tsx" - - if (isDevelopmentBranch() || isReleaseBranch()) { - // Release starts javadoc, which takes very long, so do only for certain branches - mvn.additionalArgs += ' -DperformRelease' - // JDK8 is more strict, we should fix this before the next release. Right now, this is just not the focus, yet. - mvn.additionalArgs += ' -Dmaven.javadoc.failOnError=false' + + post { + failure { + mail to: "scm-team@cloudogu.com", + subject: "Jenkins Job ${JOB_NAME} - Build #${BUILD_NUMBER} - ${currentBuild.currentResult}!", + body: "Check console output at ${BUILD_URL} to view the results." + } + unstable { + mail to: "scm-team@cloudogu.com", + subject: "Jenkins Job ${JOB_NAME} - Build #${BUILD_NUMBER} - ${currentBuild.currentResult}!", + body: "Check console output at ${BUILD_URL} to view the results." + } + fixed { + mail to: "scm-team@cloudogu.com", + subject: "Jenkins Job ${JOB_NAME} - Is back to normal with Build #${BUILD_NUMBER}", + body: "Check console output at ${BUILD_URL} to view the results." + } } - return mvn -} - -boolean isReleaseBranch() { - return env.BRANCH_NAME.startsWith("release/"); } String getReleaseVersion() { return env.BRANCH_NAME.substring("release/".length()); } -boolean isDevelopmentBranch() { - return developmentBranch.equals(env.BRANCH_NAME) +void commit(String message) { + sh "git -c user.name='CES Marvin' -c user.email='cesmarvin@cloudogu.com' commit -m '${message}'" } -void withGPGEnvironment(def closure) { +void tag(String version) { + String message = "Release version ${version}" + sh "git -c user.name='CES Marvin' -c user.email='cesmarvin@cloudogu.com' tag -m '${message}' ${version}" +} + +void isBuildSuccess() { + return currentBuild.result == null || currentBuild.result == 'SUCCESS' +} + +void withPublishProperies(Closure closure) { withCredentials([ + usernamePassword(credentialsId: 'maven.scm-manager.org', passwordVariable: 'PACKAGES_PASSWORD', usernameVariable: 'PACKAGES_USERNAME'), + usernamePassword(credentialsId: 'hub.docker.com-cesmarvin', passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME'), + string(credentialsId: 'cesmarvin_npm_token', variable: 'NPM_TOKEN'), file(credentialsId: 'oss-gpg-secring', variable: 'GPG_KEYRING'), usernamePassword(credentialsId: 'oss-keyid-and-passphrase', usernameVariable: 'GPG_KEY_ID', passwordVariable: 'GPG_KEY_PASSPHRASE') ]) { - closure.call() + String properties = "-PpackagesScmManagerUsername=${PACKAGES_USERNAME} -PpackagesScmManagerPassword=${PACKAGES_PASSWORD}" + properties += " -PdockerUsername=${DOCKER_USERNAME} -PdockerPassword=${DOCKER_PASSWORD}" + properties += " -PnpmEmail=cesmarvin@cloudogu.com -PnpmToken=${NPM_TOKEN}" + properties += " -Psigning.secretKeyRingFile=${GPG_KEYRING} -Psigning.keyId=${GPG_KEY_ID} -Psigning.password=${GPG_KEY_PASSPHRASE}" + withEnv(["PUBLISH_PROPERTIES=\"${properties}\""]) { + closure.call() + } + } +} + +void authGit(String credentials, String command) { + withCredentials([ + usernamePassword(credentialsId: credentials, usernameVariable: 'AUTH_USR', passwordVariable: 'AUTH_PSW') + ]) { + sh "git -c credential.helper=\"!f() { echo username='\$AUTH_USR'; echo password='\$AUTH_PSW'; }; f\" ${command}" } } diff --git a/scm-ui/e2e-tests/src/index.js b/scm-ui/e2e-tests/src/index.js index 55c08bb312..099a8ae520 100644 --- a/scm-ui/e2e-tests/src/index.js +++ b/scm-ui/e2e-tests/src/index.js @@ -33,7 +33,7 @@ ffmpeg.setFfmpegPath(ffmpegPath); const options = { reporter: "junit", reporterOptions: { - mochaFile: path.join("..", "target", "cypress-reports", "TEST-[hash].xml") + mochaFile: path.join("..", "build", "reports", "e2e", "TEST-[hash].xml") } };