File size: 5,151 Bytes
9a14526
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env node

/* eslint-disable */

const jwt = require('jsonwebtoken');
const github = require('@octokit/rest')();
const prettyBytes = require('pretty-bytes');
const fs = require('fs');
const {execSync} = require('child_process');
const zlib = require('zlib');

const SIZE_CHECK_APP_ID = 14028;
const SIZE_CHECK_APP_INSTALLATION_ID = 229425;

const file = process.argv[2];
const label = process.argv[3];
const name = `Size - ${label}`;

if (!file || !label) {
    console.log(`Usage: ${process.argv[0]} ${process.argv[1]} FILE LABEL`);
    process.exit(1);
}

const {size} = fs.statSync(file);
const gzipSize = zlib.gzipSync(fs.readFileSync(file)).length;

process.on('unhandledRejection', error => {
    // don't log `error` directly, because errors from child_process.execSync
    // contain an (undocumented) `envPairs` with environment variable values
    console.log(error.message || 'Error');
    process.exit(1)
});

const pk = process.env['SIZE_CHECK_APP_PRIVATE_KEY'];
if (!pk) {
    console.log('Fork PR; not computing size.');
    process.exit(0);
}

const key = Buffer.from(pk, 'base64').toString('binary');
const payload = {
    exp: Math.floor(Date.now() / 1000) + 60,
    iat: Math.floor(Date.now() / 1000),
    iss: SIZE_CHECK_APP_ID
};

const token = jwt.sign(payload, key, {algorithm: 'RS256'});
github.authenticate({type: 'app', token});

function getMergeBase() {
    const pr = process.env['CIRCLE_PULL_REQUEST'];
    if (pr) {
        const number = +pr.match(/\/(\d+)\/?$/)[1];
        return github.pullRequests.get({
            owner: 'mapbox',
            repo: 'mapbox-gl-js',
            pull_number: number
        }).then(({data}) => {
            const base = data.base.ref;
            const head = process.env['CIRCLE_SHA1'];
            return execSync(`git merge-base origin/${base} ${head}`).toString().trim();
        });
    } else {
        // Walk backward through the history (maximum of 10 commits) until
        // finding a commit on either main or release-*; assume that's the
        // base branch.
        const head = process.env['CIRCLE_SHA1'];
        for (const sha of execSync(`git rev-list --max-count=10 ${head}`).toString().trim().split('\n')) {
            const base = execSync(`git branch -r --contains ${sha} origin/main origin/release-* origin/publisher-production`).toString().split('\n')[0].trim().replace(/^origin\//, '');
            if (base) {
                return Promise.resolve(execSync(`git merge-base origin/${base} ${head}`).toString().trim());
            }
        }
    }

    return Promise.resolve(null);
}

function getPriorSize(mergeBase) {
    if (!mergeBase) {
        console.log('No merge base available.');
        return Promise.resolve(null);
    }

    return github.checks.listForRef({
        owner: 'mapbox',
        repo: 'mapbox-gl-js',
        ref: mergeBase
    }).then(({data}) => {
        const run = data.check_runs.find(run => run.name === name);
        if (run) {
            const match = run.output.summary.match(/`[^`]+` is (\d+) bytes \([^\)]+\) uncompressed, (\d+) bytes \([^\)]+\) gzipped\./);
            if (match) {
                const prior = { size: +match[1], gzipSize: +match[2] };
                console.log(`Prior size was ${prettyBytes(prior.size)}, gzipped ${prior.gzipSize}.`);
                return prior;
            }
        }
        console.log('No matching check found.');
        return Promise.resolve(null);
    });
}

function formatSize(size, priorSize) {
    if (priorSize) {
        const change = size - priorSize;
        const percent = (change / priorSize) * 100;
        return `${change >= 0 ? '+' : ''}${prettyBytes(change)} ${percent.toFixed(3)}% (${prettyBytes(size)})`;
    } else {
        return prettyBytes(size);
    }
}

github.apps.createInstallationToken({installation_id: SIZE_CHECK_APP_INSTALLATION_ID})
    .then(({data}) => {
        github.authenticate({type: 'token', token: data.token});
        getMergeBase().then(getPriorSize).then(prior => {
            const title = `${formatSize(size, prior ? prior.size : null)}, gzipped ${formatSize(gzipSize, prior ? prior.gzipSize : null)}`;

            const megabit = Math.pow(2, 12);
            const downloadTime3G = (gzipSize / (3 * megabit)).toFixed(0);
            const downloadTime4G = (gzipSize / (10 * megabit)).toFixed(0);
            const summary = `\`${file}\` is ${size} bytes (${prettyBytes(size)}) uncompressed, ${gzipSize} bytes (${prettyBytes(gzipSize)}) gzipped.
That's **${downloadTime3G} seconds** over slow 3G (3 Mbps), **${downloadTime4G} seconds** over fast 4G (10 Mbps).`;

            console.log(`Posting check result:\n${title}\n${summary}`);

            return github.checks.create({
                owner: 'mapbox',
                repo: 'mapbox-gl-js',
                name,
                head_branch: process.env['CIRCLE_BRANCH'],
                head_sha: process.env['CIRCLE_SHA1'],
                status: 'completed',
                conclusion: 'success',
                completed_at: new Date().toISOString(),
                output: { title, summary }
            });
        })
    });