Skip to content

Commit

Permalink
Changes for code coverage reports (#1303)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
Lethalmathematix and devsatishm authored Feb 16, 2021
1 parent 2da2b53 commit 7d737f9
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 15 deletions.
128 changes: 128 additions & 0 deletions builds/jazz-build-module/sonar-module.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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} "
Expand All @@ -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.
*/
Expand Down
3 changes: 2 additions & 1 deletion builds/jenkins-build-pack-api/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion builds/jenkins-build-pack-function/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion builds/jenkins-build-pack-sls-app/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
20 changes: 20 additions & 0 deletions builds/jenkins-build-pack-website/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ''
Expand Down Expand Up @@ -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']]
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1006,6 +1023,9 @@ def loadBuildModules(buildModuleUrl){

environmentDeploymentMetadata = load "environment-deployment-metadata-loader.groovy"

sonarModule = load "sonar-module.groovy"
echo "Sonar module loaded successfully."

}
}

Expand Down
10 changes: 8 additions & 2 deletions templates/angular-website-template/app/src/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand All @@ -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
});
Expand Down
36 changes: 35 additions & 1 deletion templates/api-template-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,41 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>coverage</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
20 changes: 17 additions & 3 deletions templates/api-template-nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
]
}
}
35 changes: 35 additions & 0 deletions templates/function-template-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,42 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>coverage</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Loading

0 comments on commit 7d737f9

Please sign in to comment.