Skip to content

Commit 7a4556e

Browse files
committed
build: payload github status
* Show payload deta on PRs using the Github Statuses API.
1 parent cebb516 commit 7a4556e

File tree

3 files changed

+103
-18
lines changed

3 files changed

+103
-18
lines changed

tools/gulp/tasks/payload.ts

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@ import {task} from 'gulp';
22
import {join} from 'path';
33
import {statSync} from 'fs';
44
import {DIST_ROOT} from '../build-config';
5-
import {spawnSync} from 'child_process';
6-
import {isTravisMasterBuild} from '../util/travis-ci';
5+
import {isTravisBuild, isTravisMasterBuild} from '../util/travis-ci';
76
import {openFirebaseDashboardApp} from '../util/firebase';
87

8+
// These imports lack of type definitions.
9+
const request = require('request');
10+
911
const bundlesDir = join(DIST_ROOT, 'bundles');
1012

1113
/** Task which runs test against the size of material. */
12-
task('payload', ['material:clean-build'], () => {
14+
task('payload', ['material:clean-build'], async () => {
1315

14-
let results = {
16+
const results = {
1517
timestamp: Date.now(),
1618
// Material bundles
1719
material_umd: getBundleSize('material.umd.js'),
@@ -28,9 +30,21 @@ task('payload', ['material:clean-build'], () => {
2830
// Print the results to the console, so we can read it from the CI.
2931
console.log('Payload Results:', JSON.stringify(results, null, 2));
3032

31-
// Publish the results to firebase when it runs on Travis and not as a PR.
32-
if (isTravisMasterBuild()) {
33-
return publishResults(results);
33+
if (isTravisBuild()) {
34+
// Open a connection to Firebase. For PRs the connection will be established as a guest.
35+
const firebaseApp = openFirebaseDashboardApp(!isTravisMasterBuild());
36+
const database = firebaseApp.database();
37+
const currentSha = process.env['TRAVIS_PULL_REQUEST_SHA'] || process.env['TRAVIS_COMMIT'];
38+
39+
// Upload the payload results and calculate the payload diff in parallel. Otherwise the
40+
// payload task will take much more time inside of Travis builds.
41+
await Promise.all([
42+
uploadPayloadResults(database, currentSha, results),
43+
calculatePayloadDiff(database, currentSha, results)
44+
]);
45+
46+
// Disconnect database connection because Firebase otherwise prevents Gulp from exiting.
47+
firebaseApp.delete();
3448
}
3549

3650
});
@@ -45,14 +59,73 @@ function getFilesize(filePath: string) {
4559
return statSync(filePath).size / 1000;
4660
}
4761

48-
/** Publishes the given results to the firebase database. */
49-
function publishResults(results: any) {
50-
const latestSha = spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trim();
51-
const dashboardApp = openFirebaseDashboardApp();
52-
const database = dashboardApp.database();
62+
/**
63+
* Calculates the difference between the last and current library payload.
64+
* The results will be published as a commit status on Github.
65+
*/
66+
async function calculatePayloadDiff(database: any, currentSha: string, currentPayload: any) {
67+
const authToken = process.env['FIREBASE_ACCESS_TOKEN'];
68+
69+
if (!authToken) {
70+
console.error('Cannot calculate Payload diff because there is no "FIREBASE_ACCESS_TOKEN" ' +
71+
'environment variable set.');
72+
return;
73+
}
74+
75+
const previousPayload = await getLastPayloadResults(database);
76+
77+
// Calculate library sizes by combining the CDK and Material FESM 2015 bundles.
78+
const previousSize = previousPayload.cdk_fesm_2015 + previousPayload.material_fesm_2015;
79+
const currentSize = currentPayload.cdk_fesm_2015 + currentPayload.material_fesm_2015;
80+
const deltaSize = currentSize - previousSize;
81+
82+
// Update the Github status of the current commit by sending a request to the dashboard
83+
// firebase http trigger function.
84+
await updateGithubStatus(currentSha, deltaSize, authToken);
85+
}
86+
87+
/**
88+
* Updates the Github status of a given commit by sending a request to a Firebase function of
89+
* the dashboard. The function has access to the Github repository and can set status for PRs too.
90+
*/
91+
async function updateGithubStatus(commitSha: string, payloadDiff: number, authToken: string) {
92+
const options = {
93+
url: 'https://us-central1-material2-dashboard.cloudfunctions.net/payloadGithubStatus',
94+
headers: {
95+
'User-Agent': 'Material2/PayloadTask',
96+
'auth-token': authToken,
97+
'commit-sha': commitSha,
98+
'commit-payload-diff': payloadDiff
99+
}
100+
};
101+
102+
return new Promise((resolve, reject) => {
103+
request(options, (err: any, response: any, body: string) => {
104+
if (err) {
105+
reject(`Dashboard Error ${err.toString()}`);
106+
} else {
107+
console.info('Dashboard Response: ', JSON.parse(body).message);
108+
resolve(response.statusCode);
109+
}
110+
});
111+
});
112+
}
113+
114+
/** Uploads the current payload results to the Dashboard database. */
115+
async function uploadPayloadResults(database: any, currentSha: string, currentPayload: any) {
116+
if (isTravisMasterBuild()) {
117+
await database.ref('payloads').child(currentSha).set(currentPayload);
118+
}
119+
}
120+
121+
/** Gets the last payload uploaded from previous Travis builds. */
122+
async function getLastPayloadResults(database: admin.database.Database) {
123+
const snapshot = await database.ref('payloads')
124+
.orderByChild('timestamp')
125+
.limitToLast(1)
126+
.once('value');
53127

54-
// Write the results to the payloads object with the latest Git SHA as key.
55-
return database.ref('payloads').child(latestSha).set(results)
56-
.catch((err: any) => console.error(err))
57-
.then(() => dashboardApp.delete());
128+
// The value of the DataSnapshot is an object with the SHA as a key. Only return the
129+
// value of the object because the SHA is not necessary.
130+
return snapshot.val()[Object.keys(snapshot.val())[0]];
58131
}

tools/gulp/util/firebase.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,26 @@ const cloudStorage = require('@google-cloud/storage');
66
const screenshotFirebaseConfig = require('../../screenshot-test/functions/config.json');
77

88
/** Opens a connection to the Firebase dashboard app. */
9-
export function openFirebaseDashboardApp() {
9+
export function openFirebaseDashboardApp(asGuest = false) {
10+
const databaseURL = 'https://material2-dashboard.firebaseio.com';
11+
12+
// In some situations the service account credentials are not available and authorizing as
13+
// a guest works fine. For example in Pull Requests the payload task just wants to read data.
14+
if (asGuest) {
15+
return firebase.initializeApp({ databaseURL });
16+
}
17+
1018
// Initialize the Firebase application with firebaseAdmin credentials.
1119
// Credentials need to be for a Service Account, which can be created in the Firebase console.
1220
return firebaseAdmin.initializeApp({
21+
databaseURL,
1322
credential: firebaseAdmin.credential.cert({
1423
project_id: 'material2-dashboard',
1524
client_email: 'firebase-adminsdk-ch1ob@material2-dashboard.iam.gserviceaccount.com',
1625
// In Travis CI the private key will be incorrect because the line-breaks are escaped.
1726
// The line-breaks need to persist in the service account private key.
1827
private_key: decode(process.env['MATERIAL2_DASHBOARD_FIREBASE_KEY'])
1928
}),
20-
databaseURL: 'https://material2-dashboard.firebaseio.com'
2129
});
2230
}
2331

tools/gulp/util/travis-ci.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
export function isTravisMasterBuild() {
33
return process.env['TRAVIS_PULL_REQUEST'] === 'false';
44
}
5+
6+
export function isTravisBuild() {
7+
return process.env['TRAVIS'] === 'true';
8+
}

0 commit comments

Comments
 (0)