From daaf84aab66e8dbe911f0327e4ad722b8f5c18e8 Mon Sep 17 00:00:00 2001 From: Sudarsana Babu Nagineni Date: Tue, 12 Mar 2019 13:50:05 +0200 Subject: [core] Add code coverage metrics for core Collect code coverage ratio from codecov.io for a given commit hash, and upload it to the S3 bucket. --- ci.template | 3 +- circle.yml | 6 ++ scripts/publish_code_coverage.js | 162 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100755 scripts/publish_code_coverage.js diff --git a/ci.template b/ci.template index f29d8d1608..33487396e6 100644 --- a/ci.template +++ b/ci.template @@ -161,7 +161,8 @@ "Resource": [ "arn:aws:s3:::mapbox-loading-dock/raw/mobile.binarysize/*", "arn:aws:s3:::mapbox-loading-dock/raw/mobile.codecoverage/*", - "arn:aws:s3:::mapbox-loading-dock/raw/mobile_staging.docs_coverage/*" + "arn:aws:s3:::mapbox-loading-dock/raw/mobile_staging.docs_coverage/*", + "arn:aws:s3:::mapbox-loading-dock/raw/mobile_staging.codecoverage/*" ] } ] diff --git a/circle.yml b/circle.yml index 6bd4c9d724..d194e54070 100644 --- a/circle.yml +++ b/circle.yml @@ -864,6 +864,12 @@ jobs: curl -sSfL -o codecov https://codecov.io/bash chmod +x codecov ./codecov -c + - run: + name: Upload coverage metrics to s3 + command: | + if [[ $CIRCLE_BRANCH == master ]]; then + scripts/publish_code_coverage.js -p Linux -s Core + fi # ------------------------------------------------------------------------------ linux-doxygen: diff --git a/scripts/publish_code_coverage.js b/scripts/publish_code_coverage.js new file mode 100755 index 0000000000..280b0c9b51 --- /dev/null +++ b/scripts/publish_code_coverage.js @@ -0,0 +1,162 @@ +#!/usr/bin/env node + +// Script to retrieve total code coverage ratio from codecov.io +// for a given commit hash, and upload it to the S3 bucket. + +const https = require('https'); +const zlib = require('zlib'); +const AWS = require('aws-sdk'); +const {execSync} = require('child_process'); + +const args = process.argv.slice(2); +const options = { + help: false +}; + +const usage = 'usage: publish_code_coverage.js [options]\n' + +'options: \n' + +' -h, --help \n' + +' -p, --platform \n' + +' -s, --sdk \n' + +' -c, --commit \n'; + +for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + switch(arg) { + case '-h': + case '--help': + options.help = true; + break; + case '-s': + case '--sdk': + options.sdkName = args[i + 1]; + break; + case '-p': + case '--platform': + options.platformName = args[i + 1]; + break; + case '-c': + case '--ccommit': + options.commitId = args[i + 1]; + break; + } +} + +if (options.help == true) { + console.log(usage); + process.exit(0); +} + +if (!options.sdkName || !options.platformName) { + console.log(usage); + process.exit(0); +} + +// Commit hash +const commitHash = options.commitId ? options.commitId : process.env['CIRCLE_SHA1']; +if (!commitHash) { + console.log(usage); + process.exit(0); +} + +// Commit Message +const commitMessage = execSync(`git show --pretty=format:%s -s ${commitHash}`).toString().trim(); +if (!commitMessage) { + throw new Error ('Commit message is missing'); +} + +const date = new Date().toISOString().substring(0, 19); + +process.on('uncaughtException', (err) => { + console.error(err); + process.exit(1); +}); + +// Parse the response received from codecov.io and build the +// data point that is going to be uploaded to S3 bucket. +function parseResponse(data) { + if (data && data.commit) { + if (!data.commit.timestamp || !data.commit.totals || !data.commit.totals.c) { + return; + } + + const source = { + code_coverage: Number(data.commit.totals.c), + platform: options.platformName, + sdk: options.sdkName, + commit: commitHash, + commit_message: commitMessage, + created_at: data.commit.timestamp + }; + + return source; + } +} + +// Upload to data source used by Mapbox internal metrics dashboards +function uploadData(data) { + return new AWS.S3({region: 'us-east-1'}).putObject({ + Body: zlib.gzipSync(JSON.stringify(data)), + Bucket: 'mapbox-loading-dock', + Key: `raw/mobile_staging.codecoverage/${date.substring(0,10)}/mobile-staging-codecoverage-${process.env['CIRCLE_SHA1']}.json.gz`, + CacheControl: 'max-age=300', + ContentEncoding: 'gzip', + ContentType: 'application/json' + }).promise(); +} + +// Attempt to retrieve code coverage report from codecov.io +// for a given commit hash. +function httpRequest() { + const options = { + hostname: 'codecov.io', + port: 443, + path: '/api/gh/mapbox/mapbox-gl-native/commit/' + commitHash, + method: 'GET' + }; + + return new Promise((resolve, reject) => { + setTimeout(function() { + const req = https.request(options, (res) => { + var body = []; + res.on('data', (chunk) => { + body.push(chunk); + }).on('error', (error) => { + reject(error); + }).on('end', () => { + if (res.statusCode < 200 || res.statusCode >= 300) { + return reject(new Error('Failed to fetch the results from codecov.io. StatusCode=' + res.statusCode)); + } + + try { + body = JSON.parse(Buffer.concat(body).toString()); + resolve(body); + } catch(e) { + reject(e); + } + }); + }); + + // Reject on error + req.on('error', (err) => { + reject(err); + }); + + req.end(); + }, 30000); + }); +} + +httpRequest().then((body) => { + const dataSource = parseResponse(body); + if (dataSource) { + return uploadData(dataSource); + } else { + throw new Error('Failed to parse the results received from codecov.io.'); + } +}).then(data => { + console.log('Successfully uploaded code coverage metrics to S3'); +}).catch(err => { + console.error('Failed to upload code coverage metrics to S3: ' + err.message); +}); -- cgit v1.2.1