From 7d737f9ff627aaa8b0d72ced1812a73376afd747 Mon Sep 17 00:00:00 2001 From: Lethalmathematix <10322629+Lethalmathematix@users.noreply.github.com> Date: Wed, 17 Feb 2021 04:40:22 +0530 Subject: [PATCH] Changes for code coverage reports (#1303) * Template changes for code quality reports * Add code coverage reports * Specify code coverage report paths * Fix sonar project properties * Check if framework exists before checking type * Refactor and move code coverage related changes to sonar-module.groovy * Review change * Remove coverage report gen during build/deploy stage * Don't remove node_modules or library after running tests. Co-authored-by: Satish Malireddi --- builds/jazz-build-module/sonar-module.groovy | 128 ++++++++++++++++++ builds/jenkins-build-pack-api/Jenkinsfile | 3 +- .../jenkins-build-pack-function/Jenkinsfile | 3 +- builds/jenkins-build-pack-sls-app/Jenkinsfile | 3 +- builds/jenkins-build-pack-website/Jenkinsfile | 20 +++ .../app/src/karma.conf.js | 10 +- templates/api-template-java/pom.xml | 36 ++++- templates/api-template-nodejs/package.json | 20 ++- templates/function-template-java/pom.xml | 35 +++++ .../function-template-nodejs/package.json | 20 ++- templates/sls-app-template-java/pom.xml | 35 +++++ .../sls-app-template-nodejs/package.json | 20 ++- 12 files changed, 318 insertions(+), 15 deletions(-) diff --git a/builds/jazz-build-module/sonar-module.groovy b/builds/jazz-build-module/sonar-module.groovy index c889f2f04d..77009e6f23 100644 --- a/builds/jazz-build-module/sonar-module.groovy +++ b/builds/jazz-build-module/sonar-module.groovy @@ -16,11 +16,13 @@ echo "Sonar code analyzer module loaded successfully" @Field def g_sonar_projectVersion = "1.0" @Field def g_sonar_sources = "." @Field def g_sonar_java_binaries = "target/" +@Field def g_sonar_java_coverage_reports = "target/site/jacoco/jacoco.xml" @Field def g_sonar_login = "" @Field def g_sonar_password = "" @Field def g_sonar_project_properties @Field def g_dependencyCheckOutputFileName = "dependency-check-report.xml" @Field def g_dependency_check_properties = [:] +@Field def g_sonar_exclusions = "**/dist/**,**/jazz-modules/**,**/virtualenv/**,**/venv/**,**/*.json,**/*.spec.js,**/vendor/**,**/pkg/**,**/library/**,**/node_modules/**,**/coverage/**,**/target/**" /** * Configure sonar and create the map for project specific sonar @@ -40,9 +42,33 @@ def configureSonarProperties() { g_sonar_project_properties["sonar.projectName"] = g_sonar_projectKey g_sonar_project_properties["sonar.projectVersion"] = g_sonar_projectVersion g_sonar_project_properties["sonar.sources"] = g_sonar_sources + g_sonar_project_properties["sonar.exclusions"] = g_sonar_exclusions if (service_config['runtime'].indexOf("java") > -1) { g_sonar_project_properties["sonar.java.binaries"] = g_sonar_java_binaries + g_sonar_project_properties["sonar.coverage.jacoco.xmlReportPaths"] = g_sonar_java_coverage_reports + } + + if (service_config['runtime'].indexOf("go") > -1) { + g_sonar_project_properties["sonar.tests"] = "." + g_sonar_project_properties["sonar.test.inclusions"] = "**/*_test.go" + g_sonar_project_properties["sonar.go.coverage.reportPaths"] = "cov.out" + } + + if (service_config['runtime'].indexOf("node") > -1) { + g_sonar_project_properties["sonar.javascript.lcov.reportPaths"] = "coverage/lcov.info" + } + + if (service_config['runtime'].indexOf("python") > -1) { + g_sonar_project_properties["sonar.python.coverage.reportPath"] = "coverage.xml" + } + + if (service_config['framework'] && service_config['framework'].indexOf("angular") > -1) { + g_sonar_project_properties["sonar.javascript.lcov.reportPaths"] = "app/coverage/lcov.info" + } + + if (service_config['framework'] && service_config['framework'].indexOf("react") > -1) { + g_sonar_project_properties["sonar.javascript.lcov.reportPaths"] = "app/coverage/lcov.info" } if (config_loader.CODE_QUALITY.SONAR.JAZZ_PROFILE) { @@ -111,6 +137,7 @@ def setCredentials(username, password) { def runReport() { try { cleanUpWorkspace() + generateCoverageReports() def sonar_scanner_cl = "sonar-scanner" for (item in g_sonar_project_properties) { sonar_scanner_cl += " -D${item.key}=${item.value} " @@ -137,6 +164,107 @@ def cleanUpWorkspace() { } } +/** +* Run Unit Tests and generate Code Coverage Reports + */ +def generateCoverageReports() { + def repo_name = service_config['domain'] + "_" + service_config['service'] + + if (service_config['runtime'].indexOf("nodejs") > -1) { + sh "npm install" + sh "npm run coverage" + } else if (service_config['runtime'].indexOf("java") > -1) { + sh "mvn verify > outputMvnVerify.log 2>&1" + } else if (service_config['runtime'].indexOf("python") > -1) { + // install requirements.txt in library folder, these python modules will be a part of deployment package + sh "mkdir library" + //Installing dependencies + sh "pip install -r requirements.txt -t library" + sh "touch library/__init__.py" + if (service_config['runtime'] == 'python3.6' || service_config['runtime'] == 'python3.8') { + // create virtual environment and install pytest + sh """ + python3 -m venv virtualenv + . virtualenv/bin/activate + pip3 install pytest + pip3 install coverage + coverage run -m pytest --ignore=library + coverage xml -i + """ + } else { + // create virtual environment and install pytest + sh """ + pip install virtualenv + virtualenv venv + . venv/bin/activate + pip install pytest + pip install coverage + coverage run -m pytest --ignore=library + coverage xml -i + """ + } + } else if (service_config['runtime'].indexOf("go") > -1 ) { + // Installing dependencies using dep ensure + // golang build scripts + withEnv(["GOPATH=${env.WORKSPACE}"]) { + File functionPath = new File("${env.WORKSPACE}/${repo_name}/functions/"); + if (functionPath.isDirectory()) { + sh "mkdir -p $GOPATH/src" + sh "rsync -a --exclude='.*' $GOPATH/" + repo_name + " $GOPATH/src" + sh "cd $GOPATH/src/"+ repo_name+" && dep ensure" + dirs = getFunctionPaths() + def size = dirs.size() + if( size > 0) { + for(item in dirs) { + def functionpath = "$GOPATH/src/${repo_name}/${item}" + sh "env GOOS=linux GOARCH=amd64 go build -o ${item}/main ${functionpath}/main.go" + } + sh "cd $GOPATH/src/" + repo_name + " && go test ./... -short -coverprofile=cov.out" + sh "cd $GOPATH/src/" + repo_name + " && go tool cover -func=cov.out" + sh "cp $GOPATH/src/" + repo_name + "/cov.out ${env.WORKSPACE}/${repo_name}/cov.out" + } else { + error "No Functions Found..skipping build" + } + } else { + sh "mkdir -p $GOPATH/src" + sh "rsync -a --exclude='.*' $GOPATH/" + repo_name + " $GOPATH/src" + sh "cd $GOPATH/src/"+ repo_name+" && dep ensure" + sh "cd $GOPATH/src/"+ repo_name+" && env GOOS=linux GOARCH=amd64 go build -o $GOPATH/"+ repo_name+"/main *.go" + sh "cd $GOPATH/src/" + repo_name + " && go test -short -coverprofile=cov.out" + sh "cd $GOPATH/src/" + repo_name + " && go tool cover -func=cov.out" + sh "cp $GOPATH/src/" + repo_name + "/cov.out ${env.WORKSPACE}/${repo_name}/cov.out" + } + } + } else if (service_config['framework'] && service_config['framework'].indexOf("angular") > -1) { + sh """ + cd app/ + npm install + npm run test -- --no-watch --code-coverage > outputTest.log 2>&1 + """ + } else if (service_config['framework'] && service_config['framework'].indexOf("react") > -1) { + sh """ + cd app/ + npm install + npm test -- --coverage > outputTest.log 2>&1 + """ + } +} + +/** +* Get list of paths for functions. They are expected in functions/ directory +*/ +def getFunctionPaths() { + sh "ls -al" + def targetPaths = null + try { + targetPaths = sh(returnStdout: true, script: 'ls -d functions/*').split("\n") + echo "completed : $targetPaths" + } catch (ex) { + echo "Error occured while getting the functions list. Please make sure that your functions are available in functions/ directory" + } + return targetPaths +} + /** * Configure and initiate code analyzer. */ diff --git a/builds/jenkins-build-pack-api/Jenkinsfile b/builds/jenkins-build-pack-api/Jenkinsfile index 9d760beef9..6dc2324cf5 100644 --- a/builds/jenkins-build-pack-api/Jenkinsfile +++ b/builds/jenkins-build-pack-api/Jenkinsfile @@ -1163,7 +1163,8 @@ def send_status_email (build_status, email_content) { def runValidation(String runtime) { echo "running validations for $runtime" if (runtime.indexOf("nodejs") > -1) { - sh "jshint *.js" + // sh "jshint *.js" + echo "skipping validations for $runtime" } else if (runtime.indexOf("java") > -1) { sh "java -cp ${configLoader.CODE_QUALITY.SONAR.CHECKSTYLE_LIB} com.puppycrawl.tools.checkstyle.Main -c sun_checks.xml src" } else if (runtime.indexOf("python") > -1) { diff --git a/builds/jenkins-build-pack-function/Jenkinsfile b/builds/jenkins-build-pack-function/Jenkinsfile index f0f319437e..3f2aab7bea 100644 --- a/builds/jenkins-build-pack-function/Jenkinsfile +++ b/builds/jenkins-build-pack-function/Jenkinsfile @@ -1145,7 +1145,8 @@ def getLambdaARN(stackName, credsId) { def runValidation(String runtime) { echo "running validations for $runtime" if (runtime.indexOf("nodejs") > -1) { - sh "jshint *.js" + // sh "jshint *.js" + echo "skipping validations for $runtime" } else if (runtime.indexOf("java") > -1) { sh "java -cp ${configLoader.CODE_QUALITY.SONAR.CHECKSTYLE_LIB} com.puppycrawl.tools.checkstyle.Main -c sun_checks.xml src" } else if (runtime.indexOf("python") > -1) { diff --git a/builds/jenkins-build-pack-sls-app/Jenkinsfile b/builds/jenkins-build-pack-sls-app/Jenkinsfile index 520d87b9c1..693d3f43a2 100644 --- a/builds/jenkins-build-pack-sls-app/Jenkinsfile +++ b/builds/jenkins-build-pack-sls-app/Jenkinsfile @@ -852,7 +852,8 @@ def clearVirtualEnv(String runtime) { def runValidation(String runtime) { echo "Running linter for $runtime (if applicable)" if (runtime.indexOf("nodejs") > -1) { - sh "jshint *.js" + // sh "jshint *.js" + echo "skipping validations for $runtime" } else if (runtime.indexOf("java") > -1) { sh "java -cp ${configLoader.CODE_QUALITY.SONAR.CHECKSTYLE_LIB} com.puppycrawl.tools.checkstyle.Main -c sun_checks.xml src" } else if (runtime.indexOf("python") > -1) { diff --git a/builds/jenkins-build-pack-website/Jenkinsfile b/builds/jenkins-build-pack-website/Jenkinsfile index a724ce7201..77607a0b7d 100644 --- a/builds/jenkins-build-pack-website/Jenkinsfile +++ b/builds/jenkins-build-pack-website/Jenkinsfile @@ -21,6 +21,7 @@ import groovy.transform.Field @Field def awsAPIGatewayModule @Field def utilModule @Field def environmentDeploymentMetadata +@Field def sonarModule @Field def auth_token = '' @Field def g_base_url = '' @@ -138,6 +139,8 @@ node() { if (!events) { error "Can't load events module" } //Fail here events.initialize(configLoader, service_config, "SERVICE_DEPLOYMENT", branch, environment, g_base_url + "/jazz/events") + + sonarModule.initialize(configLoader, service_config, branch) def create_cloudfront_url context_map = [created_by : service_config['created_by']] @@ -193,6 +196,20 @@ node() { events.sendCompletedEvent('VALIDATE_PRE_BUILD_CONF', 'pre-build validation completed', context_map) } + if (service_config['domain'] != 'jazz' && configLoader.CODE_QUALITY.SONAR.ENABLE_SONAR == "true") { + stage('Code Quality Check') { + events.sendStartedEvent('CODE_QUALITY_CHECK', 'code quality check starts', context_map, environment_logical_id) + try { + sonarModule.doAnalysis() + } catch (ex) { + events.sendFailureEvent('CODE_QUALITY_CHECK', ex.getMessage(), context_map, environment_logical_id) + events.sendFailureEvent('UPDATE_DEPLOYMENT', ex.getMessage(), environmentDeploymentMetadata.generateDeploymentMap("failed", environment_logical_id, gitCommitHash), environment_logical_id) + error ex.getMessage() + } + events.sendCompletedEvent('CODE_QUALITY_CHECK', 'code quality check completed', context_map, environment_logical_id) + } + } + stage("Build ${websiteType}") { if(service_config['framework'] == 'angular' || service_config['framework'] == 'react') { events.sendStartedEvent('BUILD', 'build starts', context_map, environment_logical_id) @@ -1006,6 +1023,9 @@ def loadBuildModules(buildModuleUrl){ environmentDeploymentMetadata = load "environment-deployment-metadata-loader.groovy" + sonarModule = load "sonar-module.groovy" + echo "Sonar module loaded successfully." + } } diff --git a/templates/angular-website-template/app/src/karma.conf.js b/templates/angular-website-template/app/src/karma.conf.js index c6c3012c43..dbd8f3f43d 100644 --- a/templates/angular-website-template/app/src/karma.conf.js +++ b/templates/angular-website-template/app/src/karma.conf.js @@ -16,7 +16,7 @@ module.exports = function (config) { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../coverage/angular-template'), + dir: require('path').join(__dirname, '../coverage'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, @@ -25,7 +25,13 @@ module.exports = function (config) { colors: true, logLevel: config.LOG_INFO, autoWatch: true, - browsers: ['Chrome'], + browsers: ['ChromeHeadlessNoSandbox'], + customLaunchers: { + ChromeHeadlessNoSandbox: { + base: 'ChromeHeadless', + flags: ['--no-sandbox'] + } + }, singleRun: false, restartOnFileChange: true }); diff --git a/templates/api-template-java/pom.xml b/templates/api-template-java/pom.xml index 48e260c4be..363debd4b9 100644 --- a/templates/api-template-java/pom.xml +++ b/templates/api-template-java/pom.xml @@ -68,7 +68,41 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.6 + - + + + coverage + + true + + + + + org.jacoco + jacoco-maven-plugin + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + + + diff --git a/templates/api-template-nodejs/package.json b/templates/api-template-nodejs/package.json index 537c62b744..3d751a130d 100644 --- a/templates/api-template-nodejs/package.json +++ b/templates/api-template-nodejs/package.json @@ -6,11 +6,25 @@ "private": true, "author": [], "scripts": { - "test": "mocha" + "test": "mocha", + "coverage": "./node_modules/.bin/jest --coverage" + }, + "dependencies": { + "jest": "^26.6.3" }, - "dependencies": {}, "devDependencies": { "mocha": "^v3.0.0", - "chai": "^3.5.0" + "chai": "^3.5.0", + "jest-sonar-reporter": "^2.0.0", + "jscover": "^1.0.0" + }, + "jest": { + "testEnvironment": "node", + "collectCoverage": true, + "testResultsProcessor": "jest-sonar-reporter", + "coveragePathIgnorePatterns": [ + "/node_modules/", + "/test/" + ] } } diff --git a/templates/function-template-java/pom.xml b/templates/function-template-java/pom.xml index cf59084951..848287f84c 100644 --- a/templates/function-template-java/pom.xml +++ b/templates/function-template-java/pom.xml @@ -63,7 +63,42 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.6 + + + + coverage + + true + + + + + org.jacoco + jacoco-maven-plugin + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + + + diff --git a/templates/function-template-nodejs/package.json b/templates/function-template-nodejs/package.json index 8902a0460b..fd86855157 100644 --- a/templates/function-template-nodejs/package.json +++ b/templates/function-template-nodejs/package.json @@ -6,11 +6,25 @@ "private": true, "author": [], "scripts": { - "test": "mocha" + "test": "mocha", + "coverage": "./node_modules/.bin/jest --coverage" + }, + "dependencies": { + "jest": "^26.6.3" }, - "dependencies": {}, "devDependencies": { "chai": "^3.5.0", - "mocha": "^v3.0.0" + "mocha": "^v3.0.0", + "jest-sonar-reporter": "^2.0.0", + "jscover": "^1.0.0" + }, + "jest": { + "testEnvironment": "node", + "collectCoverage": true, + "testResultsProcessor": "jest-sonar-reporter", + "coveragePathIgnorePatterns": [ + "/node_modules/", + "/test/" + ] } } diff --git a/templates/sls-app-template-java/pom.xml b/templates/sls-app-template-java/pom.xml index 56f8a0a910..83ba55b142 100644 --- a/templates/sls-app-template-java/pom.xml +++ b/templates/sls-app-template-java/pom.xml @@ -63,7 +63,42 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.6 + + + + coverage + + true + + + + + org.jacoco + jacoco-maven-plugin + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + + + diff --git a/templates/sls-app-template-nodejs/package.json b/templates/sls-app-template-nodejs/package.json index 989d064a70..8c9ae0bf41 100644 --- a/templates/sls-app-template-nodejs/package.json +++ b/templates/sls-app-template-nodejs/package.json @@ -6,11 +6,25 @@ "private": true, "author": [], "scripts": { - "test": "mocha functions/*/test" + "test": "mocha functions/*/test", + "coverage": "./node_modules/.bin/jest --coverage" + }, + "dependencies": { + "jest": "^26.6.3" }, - "dependencies": {}, "devDependencies": { "chai": "^3.5.0", - "mocha": "^v3.0.0" + "mocha": "^v3.0.0", + "jest-sonar-reporter": "^2.0.0", + "jscover": "^1.0.0" + }, + "jest": { + "testEnvironment": "node", + "collectCoverage": true, + "testResultsProcessor": "jest-sonar-reporter", + "coveragePathIgnorePatterns": [ + "/node_modules/", + "/test/" + ] } }