summaryrefslogtreecommitdiff
path: root/scripts/publish_core_codecoverage.js
blob: 43de662a13ccce9c9f5e7394da18bbddfa6c37cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/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 <Platform Name>\n' +
'  -s, --sdk <Sdk Name> \n' +
'  -c, --commit <Commit Hash> \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 '--commit':
        options.commitId = args[i + 1];
        break;
    }
}

if (options.help == true) {
    console.log(usage);
    process.exit(0);
}

// Commit hash
const commitHash = options.commitId ? options.commitId : process.env['CIRCLE_SHA1'];
if (!options.sdkName || !options.platformName || !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.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: date
        };

        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)}/${options.sdkName}-coverage-${commitHash}.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 coverage report 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);
    });
}

var errResponse = false;
const publishWithRetry = (maxRetries) =>  {
    httpRequest().then((body) => {
        const dataSource = parseResponse(body);
        if (dataSource) {
            return uploadData(dataSource);
        } else {
            errResponse = true;
            throw new Error('Failed to parse coverage report received from codecov.io.');
        }
    }).then(data => {
        console.log('Successfully uploaded code coverage metrics to S3');
    }).catch(err => {
        if (maxRetries > 1 && errResponse) {
            console.log('Invalid coverage report received. Trying to retrieve again.');
            errResponse = false;
            return publishWithRetry(maxRetries - 1);
        }

        console.error('Failed to upload code coverage metrics to S3: ' + err.message);
    });
};

// Fetch and publish code coverage report
publishWithRetry(5);