1
1
import { task } from 'gulp' ;
2
2
import { join } from 'path' ;
3
3
import { statSync } from 'fs' ;
4
- import { spawnSync } from 'child_process' ;
5
- import { isTravisMasterBuild } from '../util/travis-ci' ;
4
+ import { isTravisBuild , isTravisMasterBuild } from '../util/travis-ci' ;
6
5
import { openFirebaseDashboardApp } from '../util/firebase' ;
7
6
import { buildConfig } from '../packaging/build-config' ;
8
7
8
+ // These imports lack of type definitions.
9
+ const request = require ( 'request' ) ;
10
+
9
11
/** Path to the directory where all bundles are living. */
10
12
const bundlesDir = join ( buildConfig . outputDir , 'bundles' ) ;
11
13
12
14
/** Task which runs test against the size of material. */
13
- task ( 'payload' , [ 'material:clean-build' ] , ( ) => {
15
+ task ( 'payload' , [ 'material:clean-build' ] , async ( ) => {
14
16
15
- let results = {
17
+ const results = {
16
18
timestamp : Date . now ( ) ,
17
19
// Material bundles
18
20
material_umd : getBundleSize ( 'material.umd.js' ) ,
@@ -29,9 +31,21 @@ task('payload', ['material:clean-build'], () => {
29
31
// Print the results to the console, so we can read it from the CI.
30
32
console . log ( 'Payload Results:' , JSON . stringify ( results , null , 2 ) ) ;
31
33
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 = openFirebaseDashboardApp ( ! isTravisMasterBuild ( ) ) ;
37
+ const database = firebaseApp . database ( ) ;
38
+ const currentSha = process . env [ 'TRAVIS_PULL_REQUEST_SHA' ] || process . env [ 'TRAVIS_COMMIT' ] ;
39
+
40
+ // Upload the payload results and calculate the payload diff in parallel. Otherwise the
41
+ // payload task will take much more time inside of Travis builds.
42
+ await Promise . all ( [
43
+ uploadPayloadResults ( database , currentSha , results ) ,
44
+ calculatePayloadDiff ( database , currentSha , results )
45
+ ] ) ;
46
+
47
+ // Disconnect database connection because Firebase otherwise prevents Gulp from exiting.
48
+ firebaseApp . delete ( ) ;
35
49
}
36
50
37
51
} ) ;
@@ -46,14 +60,73 @@ function getFilesize(filePath: string) {
46
60
return statSync ( filePath ) . size / 1000 ;
47
61
}
48
62
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 ( ) ;
63
+ /**
64
+ * Calculates the difference between the last and current library payload.
65
+ * The results will be published as a commit status on Github.
66
+ */
67
+ async function calculatePayloadDiff ( database : any , currentSha : string , currentPayload : any ) {
68
+ const authToken = process . env [ 'FIREBASE_ACCESS_TOKEN' ] ;
69
+
70
+ if ( ! authToken ) {
71
+ console . error ( 'Cannot calculate Payload diff because there is no "FIREBASE_ACCESS_TOKEN" ' +
72
+ 'environment variable set.' ) ;
73
+ return ;
74
+ }
75
+
76
+ const previousPayload = await getLastPayloadResults ( database ) ;
77
+
78
+ // Calculate library sizes by combining the CDK and Material FESM 2015 bundles.
79
+ const previousSize = previousPayload . cdk_fesm_2015 + previousPayload . material_fesm_2015 ;
80
+ const currentSize = currentPayload . cdk_fesm_2015 + currentPayload . material_fesm_2015 ;
81
+ const deltaSize = currentSize - previousSize ;
82
+
83
+ // Update the Github status of the current commit by sending a request to the dashboard
84
+ // firebase http trigger function.
85
+ await updateGithubStatus ( currentSha , deltaSize , authToken ) ;
86
+ }
87
+
88
+ /**
89
+ * Updates the Github status of a given commit by sending a request to a Firebase function of
90
+ * the dashboard. The function has access to the Github repository and can set status for PRs too.
91
+ */
92
+ async function updateGithubStatus ( commitSha : string , payloadDiff : number , authToken : string ) {
93
+ const options = {
94
+ url : 'https://us-central1-material2-dashboard.cloudfunctions.net/payloadGithubStatus' ,
95
+ headers : {
96
+ 'User-Agent' : 'Material2/PayloadTask' ,
97
+ 'auth-token' : authToken ,
98
+ 'commit-sha' : commitSha ,
99
+ 'commit-payload-diff' : payloadDiff
100
+ }
101
+ } ;
102
+
103
+ return new Promise ( ( resolve , reject ) => {
104
+ request ( options , ( err : any , response : any , body : string ) => {
105
+ if ( err ) {
106
+ reject ( `Dashboard Error ${ err . toString ( ) } ` ) ;
107
+ } else {
108
+ console . info ( 'Dashboard Response: ' , JSON . parse ( body ) . message ) ;
109
+ resolve ( response . statusCode ) ;
110
+ }
111
+ } ) ;
112
+ } ) ;
113
+ }
114
+
115
+ /** Uploads the current payload results to the Dashboard database. */
116
+ async function uploadPayloadResults ( database : any , currentSha : string , currentPayload : any ) {
117
+ if ( isTravisMasterBuild ( ) ) {
118
+ await database . ref ( 'payloads' ) . child ( currentSha ) . set ( currentPayload ) ;
119
+ }
120
+ }
121
+
122
+ /** Gets the last payload uploaded from previous Travis builds. */
123
+ async function getLastPayloadResults ( database : admin . database . Database ) {
124
+ const snapshot = await database . ref ( 'payloads' )
125
+ . orderByChild ( 'timestamp' )
126
+ . limitToLast ( 1 )
127
+ . once ( 'value' ) ;
54
128
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 ( ) ) ;
129
+ // The value of the DataSnapshot is an object with the SHA as a key. Only return the
130
+ // value of the object because the SHA is not necessary.
131
+ return snapshot . val ( ) [ Object . keys ( snapshot . val ( ) ) [ 0 ] ] ;
59
132
}
0 commit comments