Skip to content

Commit

Permalink
build: payload github status
Browse files Browse the repository at this point in the history
* Show payload deta on PRs using the Github Statuses API.
  • Loading branch information
devversion committed Jun 5, 2017
1 parent 7af93bc commit bc6d4ff
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 18 deletions.
105 changes: 89 additions & 16 deletions tools/gulp/tasks/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import {task} from 'gulp';
import {join} from 'path';
import {statSync} from 'fs';
import {DIST_ROOT} from '../build-config';
import {spawnSync} from 'child_process';
import {isTravisMasterBuild} from '../util/travis-ci';
import {isTravisBuild, isTravisMasterBuild} from '../util/travis-ci';
import {openFirebaseDashboardApp} from '../util/firebase';

// These imports lack of type definitions.
const request = require('request');

const bundlesDir = join(DIST_ROOT, 'bundles');

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

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

// Publish the results to firebase when it runs on Travis and not as a PR.
if (isTravisMasterBuild()) {
return publishResults(results);
if (isTravisBuild()) {
// Open a connection to Firebase. For PRs the connection will be established as a guest.
const firebaseApp = openFirebaseDashboardApp(!isTravisMasterBuild());
const database = firebaseApp.database();
const currentSha = process.env['TRAVIS_PULL_REQUEST_SHA'] || process.env['TRAVIS_COMMIT'];

// Upload the payload results and calculate the payload diff in parallel. Otherwise the
// payload task will take much more time inside of Travis builds.
await Promise.all([
uploadPayloadResults(database, currentSha, results),
calculatePayloadDiff(database, currentSha, results)
]);

// Disconnect database connection because Firebase otherwise prevents Gulp from exiting.
firebaseApp.delete();
}

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

/** Publishes the given results to the firebase database. */
function publishResults(results: any) {
const latestSha = spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trim();
const dashboardApp = openFirebaseDashboardApp();
const database = dashboardApp.database();
/**
* Calculates the difference between the last and current library payload.
* The results will be published as a commit status on Github.
*/
async function calculatePayloadDiff(database: any, currentSha: string, currentPayload: any) {
const authToken = process.env['FIREBASE_ACCESS_TOKEN'];

if (!authToken) {
console.error('Cannot calculate Payload diff because there is no "FIREBASE_ACCESS_TOKEN" ' +
'environment variable set.');
return;
}

const previousPayload = await getLastPayloadResults(database);

// Calculate library sizes by combining the CDK and Material FESM 2015 bundles.
const previousSize = previousPayload.cdk_fesm_2015 + previousPayload.material_fesm_2015;
const currentSize = currentPayload.cdk_fesm_2015 + currentPayload.material_fesm_2015;
const deltaSize = currentSize - previousSize;

// Update the Github status of the current commit by sending a request to the dashboard
// firebase http trigger function.
await updateGithubStatus(currentSha, deltaSize, authToken);
}

/**
* Updates the Github status of a given commit by sending a request to a Firebase function of
* the dashboard. The function has access to the Github repository and can set status for PRs too.
*/
async function updateGithubStatus(commitSha: string, payloadDiff: number, authToken: string) {
const options = {
url: 'https://us-central1-material2-dashboard.cloudfunctions.net/payloadGithubStatus',
headers: {
'User-Agent': 'Material2/PayloadTask',
'auth-token': authToken,
'commit-sha': commitSha,
'commit-payload-diff': payloadDiff
}
};

return new Promise((resolve, reject) => {
request(options, (err: any, response: any, body: string) => {
if (err) {
reject(`Dashboard Error ${err.toString()}`);
} else {
console.info('Dashboard Response: ', JSON.parse(body).message);
resolve(response.statusCode);
}
});
});
}

/** Uploads the current payload results to the Dashboard database. */
async function uploadPayloadResults(database: any, currentSha: string, currentPayload: any) {
if (isTravisMasterBuild()) {
await database.ref('payloads').child(currentSha).set(currentPayload);
}
}

/** Gets the last payload uploaded from previous Travis builds. */
async function getLastPayloadResults(database: admin.database.Database) {
const snapshot = await database.ref('payloads')
.orderByChild('timestamp')
.limitToLast(1)
.once('value');

// Write the results to the payloads object with the latest Git SHA as key.
return database.ref('payloads').child(latestSha).set(results)
.catch((err: any) => console.error(err))
.then(() => dashboardApp.delete());
// The value of the DataSnapshot is an object with the SHA as a key. Only return the
// value of the object because the SHA is not necessary.
return snapshot.val()[Object.keys(snapshot.val())[0]];
}
12 changes: 10 additions & 2 deletions tools/gulp/util/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@ const cloudStorage = require('@google-cloud/storage');
const screenshotFirebaseConfig = require('../../screenshot-test/functions/config.json');

/** Opens a connection to the Firebase dashboard app. */
export function openFirebaseDashboardApp() {
export function openFirebaseDashboardApp(asGuest = false) {
const databaseURL = 'https://material2-board.firebaseio.com';

// In some situations the service account credentials are not available and authorizing as
// a guest works fine. For example in Pull Requests the payload task just wants to read data.
if (asGuest) {
return firebase.initializeApp({ databaseURL });
}

// Initialize the Firebase application with firebaseAdmin credentials.
// Credentials need to be for a Service Account, which can be created in the Firebase console.
return firebaseAdmin.initializeApp({
databaseURL,
credential: firebaseAdmin.credential.cert({
project_id: 'material2-board',
client_email: 'material2-board@appspot.gserviceaccount.com',
// In Travis CI the private key will be incorrect because the line-breaks are escaped.
// The line-breaks need to persist in the service account private key.
private_key: decode(process.env['MATERIAL2_BOARD_FIREBASE_SERVICE_KEY'])
}),
databaseURL: 'https://material2-board.firebaseio.com'
});
}

Expand Down
4 changes: 4 additions & 0 deletions tools/gulp/util/travis-ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
export function isTravisMasterBuild() {
return process.env['TRAVIS_PULL_REQUEST'] === 'false';
}

export function isTravisBuild() {
return process.env['TRAVIS'] === 'true';
}

0 comments on commit bc6d4ff

Please sign in to comment.