#!/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 . */ 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() } def getKarmaRunnerImage() { KARMA_RUNNER_PREFIX = configuration.buildRegistryPath('karma-runner') if (env.GERRIT_REFSPEC.contains('master')) { IMAGE_CACHE_UNIQUE_SCOPE = "${env.GERRIT_BRANCH}" return "$KARMA_RUNNER_PREFIX:$IMAGE_CACHE_UNIQUE_SCOPE" } else { TAG_SUFFIX = imageTag.suffix() IMAGE_CACHE_UNIQUE_SCOPE = "${imageTagVersion()}-$TAG_SUFFIX" return "$KARMA_RUNNER_PREFIX:$IMAGE_CACHE_UNIQUE_SCOPE" } } def getPatchsetTag() { (env.GERRIT_REFSPEC.contains('master')) ? "${configuration.buildRegistryPath()}:${env.GERRIT_BRANCH}" : imageTag.patchset() } def generateSkippedSpecsReport() { try { copyArtifacts( filter: 'js-results/**', optional: false, projectName: env.JOB_NAME, selector: specific(env.BUILD_NUMBER), ) withEnv(['COMPOSE_FILE=docker-compose.new-jenkins.yml']) { withCredentials([usernamePassword(credentialsId: 'INSENG_CANVAS_CI_AWS_ACCESS', usernameVariable: 'INSENG_AWS_ACCESS_KEY_ID', passwordVariable: 'INSENG_AWS_SECRET_ACCESS_KEY')]) { def awsCreds = "AWS_DEFAULT_REGION=us-west-2 AWS_ACCESS_KEY_ID=${INSENG_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${INSENG_AWS_SECRET_ACCESS_KEY}" sh "$awsCreds aws s3 cp s3://instructure-canvas-ci/skipped_specs_js.json skipped_specs.json" sh """ docker run -v \$(pwd)/\$LOCAL_WORKDIR/js-results/:/tmp/js-results \ -v \$(pwd)/\$LOCAL_WORKDIR/skipped_specs.json:/usr/src/app/skipped_specs.json \ --name skipped-spec-collator $PATCHSET_TAG bash -c \ "bundle install; ruby build/new-jenkins/skipped_specs_manager.rb js" """ sh 'docker cp skipped-spec-collator:/tmp/skipped_specs.json skipped_specs.json' sh "$awsCreds aws s3 cp skipped_specs.json s3://instructure-canvas-ci/skipped_specs_js.json" } sendSkippedSpecsSlackReport() archiveArtifacts allowEmptyArchive: true, artifacts: 'skipped_specs.json' } } catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) { slackSend channel: '#canvas-test-stats', color: 'danger', message: "<$env.BUILD_URL|coverage-js failed to generate skipped specs report!>" } } def sendSkippedSpecsSlackReport() { def jsSkippedSpecs = sh(script: "grep -o 'file_indicator' skipped_specs.json | wc -l", returnStdout: true).trim() ?: '0' def color = 'danger' if(jsSkippedSpecs.toInteger() < 100) { color = 'good' } else if (jsSkippedSpecs.toInteger() < 300) { color = 'warning' } def jobInfo = "<$env.BUILD_URL|JS>" slackSend channel: '#canvas-test-stats', color: color, message: "$jsSkippedSpecs skipped specs in $jobInfo! " } 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 = commitMessageFlag('force-failure-js').asBooleanInteger() KARMA_RUNNER_IMAGE = getKarmaRunnerImage() PATCHSET_TAG = getPatchsetTag() 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' ]) } if (env.GERRIT_EVENT_TYPE != 'comment-added') { generateSkippedSpecsReport() } } ] 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 = [:] // increase node count for coverage jsStage.JEST_NODE_COUNT = 5 for (int i = 0; i < jsStage.JEST_NODE_COUNT; i++) { String index = i extendedStage("Runner - Jest ${i}").nodeRequirements(label: 'canvas-docker', podTemplate: jsStage.jestNodeRequirementsTemplate(index)).obeysAllowStages(false).timeout(10).queue(jsStages) { def tests = [:] callableWithDelegate(jsStage.queueJestDistribution(index))(tests) parallel(tests) } } parallel(jsStages) } //parallel tests } //builder } //runner } //script } //steps } //environment } //stages } //pipeline