diff --git a/Jenkinsfile.main-for-coverage b/Jenkinsfile.main-for-coverage index 234f286f6bd..aadefd191fc 100644 --- a/Jenkinsfile.main-for-coverage +++ b/Jenkinsfile.main-for-coverage @@ -61,16 +61,14 @@ pipeline { // } // } - // stage('Rspec') { - // steps { - // skipIfPreviouslySuccessful("rspec") { - // build( - // job: 'test-suites/rspec', - // parameters: build_parameters - // ) - // } - // } - // } + stage('Rspec') { + steps { + build( + job: 'test-suites/rspec', + parameters: build_parameters + ) + } + } } } } diff --git a/Jenkinsfile.rspec b/Jenkinsfile.rspec index 1c84ee1f410..b8dcabb3cde 100644 --- a/Jenkinsfile.rspec +++ b/Jenkinsfile.rspec @@ -18,6 +18,16 @@ * with this program. If not, see . */ +def runCoverage() { + def flags = load 'build/new-jenkins/groovy/commit-flags.groovy' + return env.RUN_COVERAGE == '1' || flags.forceRunCoverage() ? '1' : '' +} + +def getImageTagVersion() { + def flags = load 'build/new-jenkins/groovy/commit-flags.groovy' + return env.RUN_COVERAGE == '1' ? 'master' : flags.getImageTagVersion() +} + def ci_node_total = 15; // how many nodes to run on pipeline { agent { label 'canvas-docker' } @@ -27,8 +37,7 @@ pipeline { environment { COMPOSE_FILE = 'docker-compose.new-jenkins.yml' - // 'refs/changes/63/181863/8' -> '63.181863.8' - NAME = "${env.GERRIT_REFSPEC}".minus('refs/changes/').replaceAll('/','.') + NAME = getImageTagVersion() PATCHSET_TAG = "$DOCKER_REGISTRY_FQDN/jenkins/canvas-lms:$NAME" KNAPSACK_ENABLED = 1 KNAPSACK_GENERATE_REPORT = 'false' @@ -37,6 +46,7 @@ pipeline { KNAPSACK_TEST_DIR = 'spec' RERUNS_RETRY = 1 MAX_FAIL = 50 + COVERAGE = runCoverage() } stages { stage ('Distribute Rspec Tests') { @@ -46,13 +56,14 @@ pipeline { for(int i = 0; i < ci_node_total; i++) { def index = i; nodes["rspec set ${(i).toString().padLeft(2, '0')}"] = { - withEnv(["CI_NODE_INDEX=$index", "CI_NODE_TOTAL=$ci_node_total"]) { + withEnv(["TEST_ENV_NUMBER=$index", "CI_NODE_INDEX=$index", "CI_NODE_TOTAL=$ci_node_total"]) { node('canvas-docker') { stage("Running RSpec Set ${index}") { try { - sh 'rm -rf ./tmp/spec_failures' + sh 'rm -rf ./tmp' checkout scm sh 'build/new-jenkins/docker-cleanup.sh' + sh 'mkdir -p tmp' timeout(time: 60) { sh 'build/new-jenkins/print-env-excluding-secrets.sh' sh 'build/new-jenkins/docker-compose-pull.sh' @@ -63,16 +74,17 @@ pipeline { } catch (ex) { // copy spec failures to local - sh 'mkdir -p tmp' sh 'docker cp $(docker-compose ps -q web):/usr/src/app/log/spec_failures/ ./tmp/spec_failures/' throw ex } finally { def reports = load 'build/new-jenkins/groovy/reports.groovy' - dir ('tmp') { - reports.stashSpecFailures(index) + reports.stashSpecFailures(index) + if (env.COVERAGE == '1') { + sh 'docker cp $(docker-compose ps -q web):/usr/src/app/coverage/ ./tmp/spec_coverage/' + reports.stashSpecCoverage(index) } - sh 'rm -rf ./tmp/spec_failures' + sh 'rm -rf ./tmp' sh 'build/new-jenkins/docker-cleanup.sh --allow-failure' } } @@ -84,6 +96,16 @@ pipeline { } } } + + stage('Upload Coverage') { + when { expression { env.COVERAGE == '1' } } + steps { + script { + def reports = load 'build/new-jenkins/groovy/reports.groovy' + reports.publishSpecCoverageToS3(ci_node_total) + } + } + } } post { failure { diff --git a/Jenkinsfile.selenium.chrome b/Jenkinsfile.selenium.chrome index 6dc2a23ffb5..22548458cc2 100644 --- a/Jenkinsfile.selenium.chrome +++ b/Jenkinsfile.selenium.chrome @@ -72,9 +72,7 @@ pipeline { } finally { def reports = load 'build/new-jenkins/groovy/reports.groovy' - dir ('tmp') { - reports.stashSpecFailures(index) - } + reports.stashSpecFailures(index) sh 'rm -rf ./tmp/spec_failures' sh 'build/new-jenkins/docker-cleanup.sh --allow-failure' } diff --git a/build/new-jenkins/groovy/reports.groovy b/build/new-jenkins/groovy/reports.groovy index 402750e0083..ef195945db2 100644 --- a/build/new-jenkins/groovy/reports.groovy +++ b/build/new-jenkins/groovy/reports.groovy @@ -16,18 +16,47 @@ * with this program. If not, see . */ +def stashSpecCoverage(index) { + dir("tmp") { + stash name: "spec_coverage_${index}", includes: 'spec_coverage/**/*' + } +} + +def publishSpecCoverageToS3(ci_node_total) { + sh 'rm -rf ./coverage_nodes' + dir('coverage_nodes') { + for(int index = 0; index < ci_node_total; index++) { + dir("node_${index}") { + unstash "spec_coverage_${index}" + } + } + } + + sh './build/new-jenkins/rspec-coverage-report.sh' + + archiveArtifacts(artifacts: 'coverage_nodes/**') + archiveArtifacts(artifacts: 'coverage/**') + uploadCoverage([ + uploadSource: "/coverage", + uploadDest: "canvas-lms-rspec/coverage" + ]) + sh 'rm -rf ./coverage_nodes' + sh 'rm -rf ./coverage' +} + // this method is to ensure that the stashing is done in a way that // is expected in publishSpecFailuresAsHTML def stashSpecFailures(index) { - stash name: "spec_failures_${index}", includes: 'spec_failures/**/*', allowEmpty: true + dir("tmp") { + stash name: "spec_failures_${index}", includes: 'spec_failures/**/*', allowEmpty: true + } } def publishSpecFailuresAsHTML(ci_node_total) { sh 'rm -rf ./compiled_failures' def htmlFiles; dir('compiled_failures') { - for(int i = 0; i < ci_node_total; i++) { - def index = i; + for(int index = 0; index < ci_node_total; index++) { dir ("node_${index}") { unstash "spec_failures_${index}" } diff --git a/build/new-jenkins/rspec-combine-coverage-results.py b/build/new-jenkins/rspec-combine-coverage-results.py new file mode 100644 index 00000000000..aa62904b4a7 --- /dev/null +++ b/build/new-jenkins/rspec-combine-coverage-results.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +import subprocess +import json +import os +import shutil + +def fp(message): + print(message, flush=True) + +def main(): + results = {} + for node in os.listdir("./coverage_nodes"): + if not os.path.isdir('./coverage_nodes/{}'.format(node)): + continue + with open('./coverage_nodes/{}/spec_coverage/.resultset.json'.format(node)) as json_file: + resultset = json.load(json_file) + fp(node) + for process in resultset: + key = "{}:{}".format(node, process) + results[key] = resultset[process] + for process in results: + fp(process) + with open('./coverage_nodes/.resultset.json', 'w') as results_file: + json.dump(results, results_file) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + fp("") diff --git a/build/new-jenkins/rspec-coverage-report.sh b/build/new-jenkins/rspec-coverage-report.sh new file mode 100755 index 00000000000..62fe20ee43f --- /dev/null +++ b/build/new-jenkins/rspec-coverage-report.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +set -o errexit -o errtrace -o nounset -o pipefail -o xtrace + +rm -rf coverage_reports +mkdir coverage_reports +chmod 777 coverage_reports + +python3 ./build/new-jenkins/rspec-combine-coverage-results.py + +# build the reports inside the canvas-lms image because it has the required executables +inputs=() +inputs+=("--volume $WORKSPACE/coverage_nodes:/usr/src/app/coverage_nodes") +inputs+=("--volume $WORKSPACE/coverage_reports:/usr/src/app/coverage_reports") +cat <