11import { task } from 'gulp' ;
22import { join } from 'path' ;
33import { statSync } from 'fs' ;
4- import { spawnSync } from 'child_process' ;
5- import { isTravisMasterBuild } from '../util/travis-ci' ;
6- import { openFirebaseDashboardApp } from '../util/firebase' ;
4+ import { isTravisBuild , isTravisMasterBuild } from '../util/travis-ci' ;
75import { buildConfig } from '../packaging/build-config' ;
6+ import { openFirebaseDashboardApp , openFirebaseDashboardAppAsGuest } from '../util/firebase' ;
7+
8+ // These imports lack of type definitions.
9+ const request = require ( 'request' ) ;
810
911/** Path to the directory where all bundles are living. */
1012const bundlesDir = join ( buildConfig . outputDir , 'bundles' ) ;
1113
1214/** Task which runs test against the size of material. */
13- task ( 'payload' , [ 'material:clean-build' ] , ( ) => {
15+ task ( 'payload' , [ 'material:clean-build' ] , async ( ) => {
1416
15- let results = {
17+ const results = {
1618 timestamp : Date . now ( ) ,
1719 // Material bundles
1820 material_umd : getBundleSize ( 'material.umd.js' ) ,
@@ -29,9 +31,23 @@ task('payload', ['material:clean-build'], () => {
2931 // Print the results to the console, so we can read it from the CI.
3032 console . log ( 'Payload Results:' , JSON . stringify ( results , null , 2 ) ) ;
3133
32- // Publish the results to firebase when it runs on Travis and not as a PR.
33- if ( isTravisMasterBuild ( ) ) {
34- return publishResults ( results ) ;
34+ if ( isTravisBuild ( ) ) {
35+ // Open a connection to Firebase. For PRs the connection will be established as a guest.
36+ const firebaseApp = isTravisMasterBuild ( ) ?
37+ openFirebaseDashboardApp ( ) :
38+ openFirebaseDashboardAppAsGuest ( ) ;
39+ const database = firebaseApp . database ( ) ;
40+ const currentSha = process . env [ 'TRAVIS_PULL_REQUEST_SHA' ] || process . env [ 'TRAVIS_COMMIT' ] ;
41+
42+ // Upload the payload results and calculate the payload diff in parallel. Otherwise the
43+ // payload task will take much more time inside of Travis builds.
44+ await Promise . all ( [
45+ uploadPayloadResults ( database , currentSha , results ) ,
46+ calculatePayloadDiff ( database , currentSha , results )
47+ ] ) ;
48+
49+ // Disconnect database connection because Firebase otherwise prevents Gulp from exiting.
50+ firebaseApp . delete ( ) ;
3551 }
3652
3753} ) ;
@@ -46,14 +62,81 @@ function getFilesize(filePath: string) {
4662 return statSync ( filePath ) . size / 1000 ;
4763}
4864
49- /** Publishes the given results to the firebase database. */
50- function publishResults ( results : any ) {
51- const latestSha = spawnSync ( 'git' , [ 'rev-parse' , 'HEAD' ] ) . stdout . toString ( ) . trim ( ) ;
52- const dashboardApp = openFirebaseDashboardApp ( ) ;
53- const database = dashboardApp . database ( ) ;
65+ /**
66+ * Calculates the difference between the last and current library payload.
67+ * The results will be published as a commit status on Github.
68+ */
69+ async function calculatePayloadDiff ( database : any , currentSha : string , currentPayload : any ) {
70+ const authToken = process . env [ 'FIREBASE_ACCESS_TOKEN' ] ;
71+
72+ if ( ! authToken ) {
73+ console . error ( 'Cannot calculate Payload diff because there is no "FIREBASE_ACCESS_TOKEN" ' +
74+ 'environment variable set.' ) ;
75+ return ;
76+ }
77+
78+ const previousPayload = await getLastPayloadResults ( database ) ;
79+
80+ if ( ! previousPayload ) {
81+ console . warn ( 'There are no previous payload results uploaded. Cannot calculate payload ' +
82+ 'difference for this job' ) ;
83+ return ;
84+ }
85+
86+ // Calculate library sizes by combining the CDK and Material FESM 2015 bundles.
87+ const previousSize = previousPayload . cdk_fesm_2015 + previousPayload . material_fesm_2015 ;
88+ const currentSize = currentPayload . cdk_fesm_2015 + currentPayload . material_fesm_2015 ;
89+ const deltaSize = currentSize - previousSize ;
90+
91+ // Update the Github status of the current commit by sending a request to the dashboard
92+ // firebase http trigger function.
93+ await updateGithubStatus ( currentSha , deltaSize , authToken ) ;
94+ }
95+
96+ /**
97+ * Updates the Github status of a given commit by sending a request to a Firebase function of
98+ * the dashboard. The function has access to the Github repository and can set status for PRs too.
99+ */
100+ async function updateGithubStatus ( commitSha : string , payloadDiff : number , authToken : string ) {
101+ const options = {
102+ url : 'https://us-central1-material2-board.cloudfunctions.net/payloadGithubStatus' ,
103+ headers : {
104+ 'User-Agent' : 'Material2/PayloadTask' ,
105+ 'auth-token' : authToken ,
106+ 'commit-sha' : commitSha ,
107+ 'commit-payload-diff' : payloadDiff
108+ }
109+ } ;
110+
111+ return new Promise ( ( resolve , reject ) => {
112+ request ( options , ( err : any , response : any , body : string ) => {
113+ if ( err ) {
114+ reject ( `Dashboard Error ${ err . toString ( ) } ` ) ;
115+ } else {
116+ console . info ( 'Dashboard Response:' , JSON . parse ( body ) . message ) ;
117+ resolve ( response . statusCode ) ;
118+ }
119+ } ) ;
120+ } ) ;
121+ }
122+
123+ /** Uploads the current payload results to the Dashboard database. */
124+ async function uploadPayloadResults ( database : any , currentSha : string , currentPayload : any ) {
125+ if ( isTravisMasterBuild ( ) ) {
126+ await database . ref ( 'payloads' ) . child ( currentSha ) . set ( currentPayload ) ;
127+ }
128+ }
129+
130+ /** Gets the last payload uploaded from previous Travis builds. */
131+ async function getLastPayloadResults ( database : admin . database . Database ) {
132+ const snapshot = await database . ref ( 'payloads' )
133+ . orderByChild ( 'timestamp' )
134+ . limitToLast ( 1 )
135+ . once ( 'value' ) ;
136+
137+ // The value of the DataSnapshot is an object with the SHA as a key. Later only the
138+ // first value of the object will be returned because the SHA is unnecessary.
139+ const results = snapshot . val ( ) ;
54140
55- // Write the results to the payloads object with the latest Git SHA as key.
56- return database . ref ( 'payloads' ) . child ( latestSha ) . set ( results )
57- . catch ( ( err : any ) => console . error ( err ) )
58- . then ( ( ) => dashboardApp . delete ( ) ) ;
141+ return snapshot . hasChildren ( ) ? results [ Object . keys ( results ) [ 0 ] ] : null ;
59142}
0 commit comments