automatically split jsg nodes
refs DE-123 Test Plan: 1. Ensure that the number of tests run per JS job is the same 2. Ensure that test reporting works correctly flag = none Change-Id: If25f6f4fe6af6d8982b5aed62c61c65b1d4daca9 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/242341 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Ryan Norton <rnorton@instructure.com> QA-Review: Aaron Ogata <aogata@instructure.com> Product-Review: Aaron Ogata <aogata@instructure.com>
This commit is contained in:
parent
321238cfb3
commit
ff78f2bf0b
|
@ -20,11 +20,33 @@
|
|||
|
||||
library "canvas-builds-library"
|
||||
|
||||
def DEFAULT_NODE_COUNT = 1
|
||||
def JSG_NODE_COUNT = 3
|
||||
|
||||
def copyFiles(dockerName, dockerPath, hostPath) {
|
||||
sh "mkdir -vp ./$hostPath"
|
||||
sh "docker cp \$(docker ps -qa -f name=$dockerName):/usr/src/app/$dockerPath ./$hostPath"
|
||||
}
|
||||
|
||||
def makeKarmaStage(group, ciNode, ciTotal) {
|
||||
return {
|
||||
withEnv([
|
||||
"CI_NODE_INDEX=${ciNode}",
|
||||
"CI_NODE_TOTAL=${ciTotal}",
|
||||
"CONTAINER_NAME=tests-karma-${group}-${ciNode}",
|
||||
"JSPEC_GROUP=${group}"
|
||||
]) {
|
||||
try {
|
||||
credentials.withSentryCredentials {
|
||||
sh 'build/new-jenkins/js/tests-karma.sh'
|
||||
}
|
||||
} finally {
|
||||
copyFiles(env.CONTAINER_NAME, 'coverage-js', "./tmp/${env.CONTAINER_NAME}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline {
|
||||
agent { label 'canvas-docker' }
|
||||
options { ansiColor('xterm') }
|
||||
|
@ -86,18 +108,12 @@ pipeline {
|
|||
sh 'build/new-jenkins/js/tests-quizzes.sh'
|
||||
}
|
||||
|
||||
['coffee', 'jsa', 'jsg', 'jsh'].each { group ->
|
||||
tests["Karma - Spec Group - ${group}"] = {
|
||||
withEnv(["CONTAINER_NAME=tests-karma-${group}", "JSPEC_GROUP=${group}"]) {
|
||||
try {
|
||||
credentials.withSentryCredentials {
|
||||
sh 'build/new-jenkins/js/tests-karma.sh'
|
||||
}
|
||||
} finally {
|
||||
copyFiles(env.CONTAINER_NAME, 'coverage-js', "./tmp/${env.CONTAINER_NAME}")
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < JSG_NODE_COUNT; i++) {
|
||||
tests["Karma - Spec Group - jsg${i}"] = makeKarmaStage('jsg', i, JSG_NODE_COUNT)
|
||||
}
|
||||
|
||||
['coffee', 'jsa', 'jsh'].each { group ->
|
||||
tests["Karma - Spec Group - ${group}"] = makeKarmaStage(group, 0, DEFAULT_NODE_COUNT)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,4 +8,4 @@ if [[ "${COVERAGE:-}" == "1" ]]; then
|
|||
sentry="-e SENTRY_URL -e SENTRY_DSN -e SENTRY_ORG -e SENTRY_PROJECT -e SENTRY_AUTH_TOKEN -e DEPRECATION_SENTRY_DSN"
|
||||
fi
|
||||
|
||||
docker-compose run --name $CONTAINER_NAME -e COVERAGE -e FORCE_FAILURE $sentry karma yarn test:karma:headless
|
||||
docker-compose run --name $CONTAINER_NAME -e CI_NODE_INDEX -e CI_NODE_TOTAL -e COVERAGE -e FORCE_FAILURE $sentry karma yarn test:karma:headless
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
process.env.NODE_ENV = 'test'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const testWebpackConfig = require('./frontend_build/baseWebpackConfig')
|
||||
|
@ -55,7 +56,45 @@ testWebpackConfig.plugins.push(new webpack.EnvironmentPlugin({
|
|||
GIT_COMMIT: null
|
||||
}))
|
||||
|
||||
const getAllFiles = (dirPath, arrayOfFiles, callback) => {
|
||||
const files = fs.readdirSync(dirPath)
|
||||
|
||||
files.forEach(file => {
|
||||
if (fs.statSync(dirPath + '/' + file).isDirectory()) {
|
||||
arrayOfFiles = getAllFiles(dirPath + '/' + file, arrayOfFiles, callback)
|
||||
} else {
|
||||
const filePath = callback(dirPath + '/' + file)
|
||||
|
||||
if (filePath) {
|
||||
arrayOfFiles.push(filePath)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return arrayOfFiles
|
||||
}
|
||||
|
||||
const isPartitionMatch = (resource, partitions, partitionIndex) => {
|
||||
return isNaN(partitionIndex) || partitions[partitionIndex].indexOf(resource) >= 0
|
||||
}
|
||||
|
||||
const makeSortedPartitions = (arr, partitionCount) => {
|
||||
const sortedArr = arr.sort()
|
||||
const sortedArrLength = sortedArr.length
|
||||
const chunkSize = Math.floor(sortedArrLength / partitionCount)
|
||||
const R = []
|
||||
|
||||
for (let i = 0; i < sortedArrLength; i += chunkSize) {
|
||||
R.push(sortedArr.slice(i, i + chunkSize))
|
||||
}
|
||||
|
||||
return R
|
||||
}
|
||||
|
||||
if (process.env.JSPEC_GROUP) {
|
||||
const nodeIndex = +process.env.CI_NODE_INDEX
|
||||
const nodeTotal = +process.env.CI_NODE_TOTAL
|
||||
|
||||
let ignoreResource = () => {
|
||||
throw new Error(`Unknown JSPEC_GROUP ${process.env.JSPEC_GROUP}`)
|
||||
}
|
||||
|
@ -75,13 +114,30 @@ if (process.env.JSPEC_GROUP) {
|
|||
)
|
||||
}
|
||||
} else if (process.env.JSPEC_GROUP === 'jsg') {
|
||||
let partitions = null
|
||||
|
||||
if (!isNaN(nodeIndex) && !isNaN(nodeTotal)) {
|
||||
const allFiles = getAllFiles(CONTEXT_JSX_SPEC, [], filePath => {
|
||||
const relativePath = filePath.replace(CONTEXT_JSX_SPEC, '.').replace(/\.js$/, '')
|
||||
|
||||
return RESOURCE_JSG_SPLIT_SPEC.test(relativePath) ? relativePath : null
|
||||
})
|
||||
|
||||
partitions = makeSortedPartitions(allFiles, nodeTotal)
|
||||
}
|
||||
|
||||
ignoreResource = (resource, context) => {
|
||||
return (
|
||||
context.endsWith(CONTEXT_COFFEESCRIPT_SPEC) ||
|
||||
context.endsWith(CONTEXT_EMBER_GRADEBOOK_SPEC) ||
|
||||
(context.endsWith(CONTEXT_JSX_SPEC) &&
|
||||
RESOURCE_JSX_SPEC.test(resource) &&
|
||||
!RESOURCE_JSG_SPLIT_SPEC.test(resource))
|
||||
!RESOURCE_JSG_SPLIT_SPEC.test(resource)) ||
|
||||
(partitions &&
|
||||
context.endsWith(CONTEXT_JSX_SPEC) &&
|
||||
RESOURCE_JSX_SPEC.test(resource) &&
|
||||
RESOURCE_JSG_SPLIT_SPEC.test(resource) &&
|
||||
!isPartitionMatch(resource, partitions, nodeIndex))
|
||||
)
|
||||
}
|
||||
} else if (process.env.JSPEC_GROUP === 'jsh') {
|
||||
|
|
Loading…
Reference in New Issue