diff --git a/scripts/Jenkinsfiles/bokchoy b/scripts/Jenkinsfiles/bokchoy new file mode 100644 index 0000000000000000000000000000000000000000..77c95ea117ac406b87c32c28326fcf139fe22220 --- /dev/null +++ b/scripts/Jenkinsfiles/bokchoy @@ -0,0 +1,134 @@ +def runBokchoyTests() { + // Determine git refspec, branch, and clone type + git_shallow_clone = true + git_extensions = [] + if (env.ghprbActualCommit) { + git_branch = "${ghprbActualCommit}" + git_refspec = "+refs/pull/${ghprbPullId}/*:refs/remotes/origin/pr/${ghprbPullId}/*" + } else { + git_branch = 'master' + git_refspec = "+refs/heads/master:refs/remotes/origin/master" + } + sshagent(credentials: ['jenkins-worker'], ignoreMissing: true) { + checkout changelog: false, poll: false, scm: [$class: 'GitSCM', branches: [[name: git_branch]], + doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CloneOption', honorRefspec: true, + noTags: true, shallow: true]], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'jenkins-worker', + refspec: git_refspec, url: 'git@github.com:edx/edx-platform.git']]] + console_output = sh(returnStdout: true, script: 'bash scripts/all-tests.sh').trim() + dir('stdout') { + writeFile file: "${TEST_SUITE}-${SHARD}-stdout.log", text: console_output + } + } +} + +def bokchoyTestCleanup() { + archiveArtifacts allowEmptyArchive: true, artifacts: 'test_root/log/**/*.log,stdout/*.log' + junit '**/reports/bok_choy/**/xunit.xml' +} + +pipeline { + agent { label "jenkins-worker" } + options { + timestamps() + timeout(60) + } + stages { + stage('Mark build as pending on Github') { + when { + // Only run github-build-status for master builds + expression { env.ghprbActualCommit == null } + } + steps { + script { + commit_sha = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() + build job: 'github-build-status', + parameters: [ + string(name: 'GIT_SHA', value: commit_sha), + string(name: 'GITHUB_ORG', value: 'edx'), string(name: 'GITHUB_REPO', value: 'edx-platform'), + string(name: 'TARGET_URL', value: "${BUILD_URL}"), + string(name: 'DESCRIPTION', value: 'Pending'), string(name: 'CONTEXT', value: 'jenkins/bokchoy-pipeline-master'), + string(name: 'CREATE_DEPLOYMENT', value: 'false'), string(name: 'BUILD_STATUS', value: 'pending') + ], + propagate: false, wait: false + } + } + } + stage("Run tests") { + steps { + script { + def parallel_stages = [:] + for (int i = 1; i <= 22; i++) { + int index = i + parallel_stages["${index}"] = { + node('jenkins-worker') { + withEnv(["SHARD=${index}","TEST_SUITE=bok-choy"]) { + try { + stage("Bokchoy shard: ${index}") { + runBokchoyTests() + } + } finally { + bokchoyTestCleanup() + } + } + } + } + } + parallel parallel_stages + } + } + } + } + post { + always { + script{ + if (env.ghprbPullId != null) { + // For PR jobs, run the edx-platform-test-notifier for PR reporting + build job: 'edx-platform-test-notifier', parameters: [string(name: 'PR_NUMBER', value: "${ghprbPullId}")], wait: false + } else { + // For master jobs run github-build-status and report to slack when necessary + if (currentBuild.currentResult == "SUCCESS") { + create_deployment = "true" + build_status = "success" + build_description = "Build Passed" + } + else { + create_deployment = "false" + build_status = "failure" + build_description = "Build Failed" + } + + commit_sha = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() + build job: 'github-build-status', + parameters: [ + string(name: 'GIT_SHA', value: commit_sha), + string(name: 'GITHUB_ORG', value: 'edx'), string(name: 'GITHUB_REPO', value: 'edx-platform'), + string(name: 'TARGET_URL', value: "${BUILD_URL}"), + string(name: 'DESCRIPTION', value: build_description), string(name: 'CONTEXT', value: 'jenkins/bokchoy-pipeline-master'), + string(name: 'CREATE_DEPLOYMENT', value: create_deployment), string(name: 'BUILD_STATUS', value: build_status) + ], + propagate: false, wait: false + + if (currentBuild.currentResult != "SUCCESS"){ + slackSend "`${JOB_NAME}` #${BUILD_NUMBER}: ${currentBuild.currentResult} after ${currentBuild.durationString.replace(' and counting', '')}\n${BUILD_URL}" + + email_body = "See: <${BUILD_URL}>\n\nChanges:\n" + change_sets = currentBuild.changeSets + for (int j = 0; j < change_sets.size(); j++) { + change_set_items = change_sets[j].items + for (int k = 0; k < change_set_items.length; k++) { + item = change_set_items[k] + email_body = email_body + "\n Commit: ${item.commitId} by ${item.author}: ${item.msg}" + } + } + emailext body: email_body, + subject: "Build failed in Jenkins: ${JOB_NAME} #${BUILD_NUMBER}", to: 'testeng@edx.org' + } else if (currentBuild.currentResult == "SUCCESS" && currentBuild.previousBuild.currentResult != "SUCCESS") { + slackSend "`${JOB_NAME}` #${BUILD_NUMBER}: Back to normal after ${currentBuild.durationString.replace(' and counting', '')}\n${BUILD_URL}" + emailext body: "See <${BUILD_URL}>", + subject: "Jenkins Build is back to normal: ${JOB_NAME} #${BUILD_NUMBER}", to: 'testeng@edx.org' + } + } + } + } + } +} diff --git a/scripts/Jenkinsfiles/quality b/scripts/Jenkinsfiles/quality new file mode 100644 index 0000000000000000000000000000000000000000..f920858139bd94e1e1900275b2d7a85ceb6fd6e9 --- /dev/null +++ b/scripts/Jenkinsfiles/quality @@ -0,0 +1,243 @@ +def runQualityTests() { + sshagent(credentials: ['jenkins-worker'], ignoreMissing: true) { + // Determine git refspec, branch, and clone type + git_shallow_clone = true + git_extensions = [] + if (env.ghprbActualCommit) { + git_branch = "${ghprbActualCommit}" + refspec = "+refs/pull/${ghprbPullId}/*:refs/remotes/origin/pr/${ghprbPullId}/*" + if (SHARD == "4") { + git_shallow_clone = false + git_extensions.add([$class: 'WipeWorkspace']) + refspec = refspec + " +refs/heads/master:refs/remotes/origin/master" + } + } else { + git_branch = 'master' + refspec = "+refs/heads/master:refs/remotes/origin/master" + } + git_extensions.add([$class: 'CloneOption', honorRefspec: true, noTags: true, shallow: git_shallow_clone ]) + + checkout changelog: false, poll: false, scm: [$class: 'GitSCM', branches: [[name: git_branch]], + doGenerateSubmoduleConfigurations: false, extensions: git_extensions, submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'jenkins-worker', + refspec: refspec, + url: 'git@github.com:edx/edx-platform.git']]] + + sh "bash scripts/all-tests.sh" + stash includes: '**/reports/**/*', name: "${TEST_SUITE}-${SHARD}-reports" + } +} + +def qualityTestCleanup() { + archiveArtifacts allowEmptyArchive: true, artifacts: '**/reports/**/*,test_root/log/**/*.log,*.log' + junit 'reports/quality_junitxml/*.xml' +} + +pipeline { + agent { label "jenkins-worker" } + options { + timestamps() + timeout(60) + } + stages { + stage('Mark build as pending on Github') { + when { + // Only run github-build-status for master builds + expression { env.ghprbActualCommit == null } + } + steps { + script { + commit_sha = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() + build job: 'github-build-status', + parameters: [ + string(name: 'GIT_SHA', value: commit_sha), + string(name: 'GITHUB_ORG', value: 'edx'), string(name: 'GITHUB_REPO', value: 'edx-platform'), + string(name: 'TARGET_URL', value: "${BUILD_URL}"), + string(name: 'DESCRIPTION', value: 'Pending'), string(name: 'CONTEXT', value: 'jenkins/quality-pipeline-master'), + string(name: 'CREATE_DEPLOYMENT', value: 'false'), string(name: 'BUILD_STATUS', value: 'pending') + ], + propagate: false, wait: false + } + } + } + stage('Run Tests') { + parallel { + stage("commonlib pylint") { + agent { label "jenkins-worker" } + environment { + TEST_SUITE = "quality" + SHARD = 1 + } + steps { + script { + runQualityTests() + } + } + post { + always { + script { + qualityTestCleanup() + } + } + } + } + stage("lms pylint") { + agent { label "jenkins-worker" } + environment { + TEST_SUITE = "quality" + SHARD = 2 + } + steps { + script { + runQualityTests() + } + } + post { + always { + script { + qualityTestCleanup() + } + } + } + } + stage("cms/openedx/pavelib pylint") { + agent { label "jenkins-worker" } + environment { + TEST_SUITE = "quality" + SHARD = 3 + } + steps { + script { + runQualityTests() + } + } + post { + always { + script { + qualityTestCleanup() + } + } + } + } + stage("Other quality checks") { + agent { label "jenkins-worker" } + environment { + TEST_SUITE = "quality" + SHARD = 4 + } + steps { + script { + runQualityTests() + } + } + post { + always { + script { + qualityTestCleanup() + } + } + } + } + } + } + stage('Diff quality') { + when { + // Only run diff quality on PR builds + expression { env.ghprbTargetBranch != null } + } + environment { + TARGET_BRANCH = "origin/${ghprbTargetBranch}" + } + steps { + sshagent(credentials: ['jenkins-worker'], ignoreMissing: true) { + checkout changelog: false, poll: false, scm: [$class: 'GitSCM', branches: [[name: "${ghprbActualCommit}"]], + doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CloneOption', + honorRefspec: true, noTags: true, shallow: false], [$class: 'WipeWorkspace']], submoduleCfg: [], + userRemoteConfigs: [[credentialsId: 'jenkins-worker', + refspec: "+refs/heads/${ghprbTargetBranch}:refs/remotes/origin/${ghprbTargetBranch} +refs/pull/${ghprbPullId}/*:refs/remotes/origin/pr/${ghprbPullId}/*", + url: 'git@github.com:edx/edx-platform.git']]] + unstash 'quality-1-reports' + unstash 'quality-2-reports' + unstash 'quality-3-reports' + unstash 'quality-4-reports' + sh "./scripts/jenkins-quality-diff.sh" + } + } + post { + always { + qualityTestCleanup() + publishHTML([allowMissing: true, alwaysLinkToLastBuild: false, keepAll: true, reportDir: 'reports/diff_quality', + reportFiles: 'diff_quality_pylint.html, diff_quality_eslint.html', reportName: 'Diff Quality Report', reportTitles: '']) + } + } + } + } + post { + always { + script{ + try { + unstash 'quality-1-reports' + unstash 'quality-2-reports' + unstash 'quality-3-reports' + unstash 'quality-4-reports' + // Check for warnings + warnings canComputeNew: false, canResolveRelativePaths: false, canRunOnFailed: true, categoriesPattern: '', + defaultEncoding: '', excludePattern: '', healthy: '', includePattern: '', messagesPattern: '', + parserConfigurations: [[parserName: 'Pep8', pattern: 'reports/pep8/pep8.report'], [parserName: 'PyLint', pattern: 'reports/**/pylint.report']], + unHealthy: '' + // Publish Quality report + publishHTML([allowMissing: true, alwaysLinkToLastBuild: false, keepAll: true, + reportDir: 'reports/metrics/', + reportFiles: 'pylint/*view*/,pep8/*view*/,python_complexity/*view*/,xsscommitlint/*view*/,xsslint/*view*/,eslint/*view*/', + reportName: 'Quality Report', reportTitles: '']) + } finally { + if (env.ghprbPullId != null) { + // For PR jobs, run the edx-platform-test-notifier for PR reporting + build job: 'edx-platform-test-notifier', parameters: [string(name: 'PR_NUMBER', value: "${ghprbPullId}")], wait: false + } else { + // For master jobs run github-build-status and report to slack when necessary + if (currentBuild.currentResult == "SUCCESS") { + create_deployment = "true" + build_status = "success" + build_description = "Build Passed" + } + else { + create_deployment = "false" + build_status = "failure" + build_description = "Build Failed" + } + + commit_sha = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() + build job: 'github-build-status', parameters: [ + string(name: 'GIT_SHA', value: commit_sha), + string(name: 'GITHUB_ORG', value: 'edx'), string(name: 'GITHUB_REPO', value: 'edx-platform'), + string(name: 'TARGET_URL', value: "${BUILD_URL}"), + string(name: 'DESCRIPTION', value: build_description), string(name: 'CONTEXT', value: 'jenkins/quality-pipeline-master'), + string(name: 'CREATE_DEPLOYMENT', value: create_deployment), string(name: 'BUILD_STATUS', value: build_status) + ], + propagate: false, wait: false + + if (currentBuild.currentResult != "SUCCESS"){ + slackSend "`${JOB_NAME}` #${BUILD_NUMBER}: ${currentBuild.currentResult} after ${currentBuild.durationString.replace(' and counting', '')}\n${BUILD_URL}" + + email_body = "See: <${BUILD_URL}>\n\nChanges:\n" + change_sets = currentBuild.changeSets + for (int j = 0; j < change_sets.size(); j++) { + change_set_items = change_sets[j].items + for (int k = 0; k < change_set_items.length; k++) { + item = change_set_items[k] + email_body = email_body + "\n Commit: ${item.commitId} by ${item.author}: ${item.msg}" + } + } + emailext body: email_body, + subject: "Build failed in Jenkins: ${JOB_NAME} #${BUILD_NUMBER}", to: 'testeng@edx.org' + } else if (currentBuild.currentResult == "SUCCESS" && currentBuild.previousBuild.currentResult != "SUCCESS") { + slackSend "`${JOB_NAME}` #${BUILD_NUMBER}: Back to normal after ${currentBuild.durationString.replace(' and counting', '')}\n${BUILD_URL}" + emailext body: "See <${BUILD_URL}>", + subject: "Jenkins Build is back to normal: ${JOB_NAME} #${BUILD_NUMBER}", to: 'testeng@edx.org' + } + } + } + } + } + } +}