re-enable canvas javascript coverage report

refs DE-989
flag=none

TEST PLAN:
  confirm https://code-coverage.inseng.net/ is updated with new report

Change-Id: I596ce0df04c920b096a875004bd765979dec344b
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/285861
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Aaron Ogata <aogata@instructure.com>
QA-Review: Bobby Buten <bobby.buten@instructure.com>
Product-Review: Bobby Buten <bobby.buten@instructure.com>
This commit is contained in:
Bobby Buten 2022-02-23 16:18:06 -05:00
parent e9f0e22817
commit e91a88762d
8 changed files with 263 additions and 40 deletions

140
Jenkinsfile.coverage-js Normal file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env groovy
/*
* Copyright (C) 2022 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
library "canvas-builds-library@${env.CANVAS_BUILDS_REFSPEC}"
loadLocalLibrary('local-lib', 'build/new-jenkins/library')
def setupNode() {
sh 'rm -vrf ./tmp'
checkout scm
distribution.stashBuildScripts()
}
pipeline {
agent none
options {
ansiColor('xterm')
timeout(60)
timestamps()
}
environment {
COMPOSE_FILE = 'docker-compose.new-jenkins.yml:docker-compose.new-jenkins-js.yml'
FORCE_FAILURE = configuration.forceFailureJS()
TAG_SUFFIX = imageTag.suffix()
IMAGE_CACHE_UNIQUE_SCOPE = "${imageTagVersion()}-$TAG_SUFFIX"
KARMA_RUNNER_PREFIX = configuration.buildRegistryPath('karma-runner')
KARMA_RUNNER_IMAGE = "$KARMA_RUNNER_PREFIX:$IMAGE_CACHE_UNIQUE_SCOPE"
BUILD_REGISTRY_FQDN = configuration.buildRegistryFQDN()
COMPOSE_DOCKER_CLI_BUILD = 1
DOCKER_BUILDKIT = 1
PROGRESS_NO_TRUNC = 1
}
stages {
stage('Environment') {
steps {
script {
def rspecNodeRequirements = [label: 'canvas-docker']
def postBuildHandler = [
onNodeReleasing: { stageName, stageConfig, result ->
buildSummaryReport.saveRunManifest()
copyArtifacts(
filter: 'coverage-report-js/**',
optional: false,
projectName: env.JOB_NAME,
selector: specific(env.BUILD_NUMBER),
)
withEnv(['COMPOSE_FILE=docker-compose.new-jenkins.yml']) {
sh """
docker run --interactive \
--volume \$(pwd)/coverage-report-js:/usr/src/app/tmp/coverage-report-js \
--name coverage-collator \
$KARMA_RUNNER_IMAGE bash -c "./build/new-jenkins/js/coverage-report.sh"
"""
sh 'docker cp coverage-collator:/usr/src/app/report-html coverage-report-js/report-html'
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: './coverage-report-js/report-html',
reportFiles: 'index.html',
reportName: 'Coverage Report'
]
uploadCoverage([
uploadSource: '/coverage-report-js/report-html',
uploadDest: 'canvas-lms-js/coverage'
])
}
}
]
extendedStage('Runner').obeysAllowStages(false).execute {
extendedStage('Builder').hooks(postBuildHandler).obeysAllowStages(false).nodeRequirements(rspecNodeRequirements).execute {
stage('Setup') {
setupNode()
}
extendedStage('Parallel Run Tests').obeysAllowStages(false).execute { stageConfig, buildConfig ->
def jsStages = [:]
extendedStage('Runner - Jest').nodeRequirements(label: 'canvas-docker', podTemplate: jsStage.jestNodeRequirementsTemplate()).obeysAllowStages(false).timeout(10).queue(jsStages) {
def tests = [:]
callableWithDelegate(jsStage.queueJestDistribution())(tests)
parallel(tests)
}
extendedStage('Runner - Coffee').nodeRequirements(label: 'canvas-docker', podTemplate: jsStage.coffeeNodeRequirementsTemplate()).obeysAllowStages(false).timeout(10).queue(jsStages) {
def tests = [:]
callableWithDelegate(jsStage.queueCoffeeDistribution())(tests)
parallel(tests)
}
extendedStage('Runner - Karma').nodeRequirements(label: 'canvas-docker', podTemplate: jsStage.karmaNodeRequirementsTemplate()).obeysAllowStages(false).timeout(10).queue(jsStages) {
def tests = [:]
callableWithDelegate(jsStage.queueKarmaDistribution())(tests)
parallel(tests)
}
parallel(jsStages)
} //parallel tests
} //builder
} //runner
} //script
} //steps
} //environment
} //stages
} //pipeline

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2022 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Due to an issue with babel-plugin-istanbul, we get different versions of
coverage reports, for example:
"<...>/src/app.js": {"path":"<...>/src/app.js","statementMap":{"0":{"start"...
vs
"<...>/src/aws.js": {"data":{"path":"<...>/src/aws.js","statementMap":{"0":{"start"...
The difference is the added "data" key, which when present, will crash istanbul-merge
with the error:
Error: Invalid file coverage object, missing keys, found:data
This script loops through all our js generated coverage reports and strips
the "data" key where present so we can merge all coverage reports into one
Currently this script is called from ~/buid/new-jenkins/js/coverage-report.sh
which is in turn called from Jenkinsfile.coverage
*/
const fs = require('fs')
const dirArgs = process.argv.slice(2)
const normalizeJestCoverage = obj => {
const result = {...obj}
Object.entries(result).forEach(([k, v]) => {
if (v.data) result[k] = v.data
})
return result
}
// Get the contents of the directory and loop over it.
const dirName = dirArgs[0]
fs.readdir(dirName, function (err, list) {
for (let i = 0; i < list.length; i++) {
// Get the contents of each file on iteration.
const filename = list[i]
fs.readFile(dirName + '/' + filename, function (err, data) {
const parsedData = JSON.parse(data)
const cleanedCoverage = normalizeJestCoverage(parsedData)
fs.writeFileSync(
dirArgs[1] + filename.slice(0, -5) + '-out.json',
JSON.stringify(cleanedCoverage, null, 4)
)
})
}
})

View File

@ -2,48 +2,24 @@
set -o errexit -o errtrace -o nounset -o pipefail -o xtrace
# we have to premake the directories because... docker.. or something..
# we have to premake the directories because for docker
# sub directories are problematic when making it on a mounted
# directory. permissions errors happen when making sub directories.
rm -vrf ./coverage-report-js
mkdir -v coverage-report-js
chmod -vvR 777 coverage-report-js
rm -vrf ./coverage-report-dir
mkdir -v coverage-report-dir
chmod -vvR 777 coverage-report-dir
# copy all the coverage reports and rename them so there are no
# file name collisions
counter=0
for coverage_file in `find $(pwd)/tmp -name coverage*.json`
do
echo $coverage_file
cp $coverage_file ./coverage-report-js/coverage-$counter.json
counter=$((counter+1))
done
# lets see whats inside the directory before we start aggregations
find ./coverage-report-js
# build the reports inside the canvas-lms image because it has the required executables
inputs=()
inputs+=("--volume $(pwd)/coverage-report-js:/usr/src/app/coverage-report-js")
cat <<EOF | docker run --interactive ${inputs[@]} $PATCHSET_TAG /bin/bash -
set -o errexit -o errtrace -o nounset -o pipefail -o xtrace
# babel-plugin-istanbul will produce differently formatted
# coverage json files, this script cleans them up to be uniform and what
# istanbul-merge requires
node ./build/new-jenkins/js/cleanup-coverage.js '/usr/src/app/tmp/coverage-report-js' '/usr/src/app/coverage-report-dir/'
# aggregate them to coverage-total
./node_modules/.bin/istanbul-merge --out coverage-report-js/coverage-total.json "coverage-report-js/coverage-*.json"
./node_modules/.bin/istanbul-merge --out coverage-report-dir/coverage-total.json "coverage-report-dir/coverage-*-out.json"
# prepare for creating reports
mkdir -v .nyc_output
cp -v coverage-report-js/coverage-total.json .nyc_output/total-coverage.json
cp -v coverage-report-dir/coverage-total.json .nyc_output/total-coverage.json
# the html report
./node_modules/.bin/nyc report --reporter=html --report-dir report-html
tar cf coverage-report-js/report-html.tar report-html
# the json report
./node_modules/.bin/nyc report --reporter=json-summary --report-dir report-json
tar cf coverage-report-js/report-json.tar report-json
EOF
# extract the reports
tar -vxf coverage-report-js/report-html.tar -C coverage-report-js/
tar -vxf coverage-report-js/report-json.tar -C coverage-report-js/
./node_modules/.bin/nyc report --reporter=html --report-dir report-html

View File

@ -64,6 +64,26 @@ def tearDownNode() {
copyToWorkspace srcBaseDir: '/usr/src/app', path: env.TEST_RESULT_OUTPUT_DIR
archiveArtifacts artifacts: "${env.TEST_RESULT_OUTPUT_DIR}/**/*.xml"
junit "${env.TEST_RESULT_OUTPUT_DIR}/**/*.xml"
if (env.COVERAGE == '1') {
/* groovylint-disable-next-line GStringExpressionWithinString */
sh '''#!/bin/bash
rm -vrf ./coverage-report-js
mkdir -v coverage-report-js
chmod -vvR 777 coverage-report-js
counter=0
for coverage_file in `find . -type d -name node_modules -prune -o -name coverage*.json -print`
do
stagearray=($STAGE_NAME)
new_file="./coverage-report-js/coverage-"${stagearray[0]}"-"$counter".json"
cp $coverage_file $new_file
((counter=counter+1))
done
'''
copyToWorkspace srcBaseDir: '/usr/src/app', path: 'coverage-report-js'
archiveArtifacts allowEmptyArchive: true, artifacts: 'coverage-report-js/*'
}
}
}

View File

@ -120,7 +120,7 @@ if (process.env.COVERAGE === '1') {
karmaConfig.webpack.module.rules.unshift({
test: /\.(js|coffee)$/,
use: {
loader: 'istanbul-instrumenter-loader',
loader: 'coverage-istanbul-loader',
options: {esModules: true, produceSourceMap: true}
},
enforce: 'post',

View File

@ -112,6 +112,7 @@
"classnames": "^2.2.5",
"color-slicer": "0.8.0",
"confetti-js": "^0.0.17",
"coverage-istanbul-loader": "^3.0.5",
"create-react-class": "^15.6.3",
"crypto-js": "^4.1.1",
"d3": "3.5.17",

View File

@ -42,10 +42,10 @@ module.exports = {
coveragePathIgnorePatterns: ['<rootDir>/src/i18n/flip-message.js'],
coverageThreshold: {
global: {
branches: 85,
functions: 85,
lines: 85,
statements: 85
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
testEnvironment: 'jest-environment-jsdom-fourteen'

View File

@ -4230,6 +4230,17 @@
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@jsdevtools/coverage-istanbul-loader@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26"
integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==
dependencies:
convert-source-map "^1.7.0"
istanbul-lib-instrument "^4.0.3"
loader-utils "^2.0.0"
merge-source-map "^1.1.0"
schema-utils "^2.7.0"
"@lerna/package@^3":
version "3.16.0"
resolved "https://registry.yarnpkg.com/@lerna/package/-/package-3.16.0.tgz#7e0a46e4697ed8b8a9c14d59c7f890e0d38ba13c"
@ -9338,6 +9349,13 @@ cosmiconfig@^7.0.0:
path-type "^4.0.0"
yaml "^1.10.0"
coverage-istanbul-loader@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#bf942efc0f4e3ac27565203c17dca5008eae6637"
integrity sha512-xsw2phF0VNqUPk47V/vHXkdcTyl0tkMSmaZfLrTOhoPhPMXFelNju7utl5s7I93KXzipqDEK0YwofQSSflPz8A==
dependencies:
"@jsdevtools/coverage-istanbul-loader" "3.0.5"
cp-file@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd"