@@ -103,32 +103,153 @@ function isConfig(config: any): config is Config {
103
103
commit_sha : event . after
104
104
} ) ;
105
105
106
- // Calculate which file paths have changed in this push
107
- const { stdout : filesChangedRaw } = await execFile ( 'git' , [ 'diff' , '--name-only' , `${ event . before } ..${ event . after } ` ] ) ;
108
- const queriesChanged = new Set ( filesChangedRaw . split ( '\n' )
109
- . map ( s => s . trim ( ) )
110
- . filter ( s => s . endsWith ( '.ql' ) ) ) ;
111
- console . log ( `${ pluralize ( queriesChanged . size , 'query' ) } updated in this push` ) ;
112
- comment += `${ pluralize ( queriesChanged . size , 'query' ) } changed `
113
- comment += `[between \`${ event . before . substr ( 0 , 7 ) } \` and \`${ event . after . substr ( 0 , 7 ) } \`]`
114
- comment += `(${ event . repository . html_url } /compare/${ event . before } ...${ event . after } ) after push to \`${ event . ref } \`` ;
115
- if ( queriesChanged . size > 0 ) {
116
- comment += ':\n' ;
117
- for ( const query of queriesChanged ) {
118
- console . log ( `- ${ query } ` ) ;
119
- const exists = await access ( query , fs . constants . R_OK ) . then ( ( ) => true , ( ) => false ) ;
120
- comment += `* \`${ query } \`${ exists ? '' : ' *(deleted)*' } \n` ;
106
+ /**
107
+ * File paths changed by the user (if we're not just running all queries)
108
+ *
109
+ * This is used to reduce the number of queries we need to run to only those
110
+ * that currently interest the user.
111
+ */
112
+ const queriesChanged = new Set < string > ( ) ;
113
+ let unableToGetChangedQueries = false ;
114
+
115
+ if ( RUN_ALL ) {
116
+
117
+ /*
118
+ * There are a few different ways in which we may determine which queries
119
+ * are currently interesting to the user, in decreasing usefulness:
120
+ *
121
+ * 1. If the user just pushed to a branch that currently has an open pull
122
+ * request, the interesting queries are those that are changed in the pull
123
+ * request (and not just those changed in the most recent push).
124
+ * 2. If there's no active pull request, then what's probably most
125
+ * interesting are the queries that have changed in the last push (i.e.
126
+ * between the previous head and the new head)
127
+ * 3. If that's not possible (e.g. the push could have created the branch for
128
+ * the first time, and so there is no "previous ref"), then comparing this
129
+ * branch to the default branch of a repo will probably give the most
130
+ * accurate results.
131
+ * 4. Finally, if all else fails (e.g. the push was the initial push to the
132
+ * default branch of the repo), then we should just run every query we
133
+ * recognize (as if RUN_ALL was true). We do this by setting
134
+ * unableToGetChangedQueries to true.
135
+ */
136
+
137
+ /**
138
+ * The output from a successful call to `git diff --name-only`
139
+ */
140
+ let diff : {
141
+ baseSha : string ;
142
+ filesChangedRaw : string ;
143
+ } | null = null ;
144
+
145
+ // Try (1) - find any PR associated with the branch of this push
146
+
147
+ // Get branch name
148
+ // This is expected to fail if e.g. the push was to a tag not a branch
149
+ const branch = / ^ r e f s \/ h e a d s \/ ( .* ) $ / . exec ( event . ref ) ?. [ 1 ] ;
150
+
151
+ if ( branch ) {
152
+ try {
153
+ const pulls = await api . pulls . list ( {
154
+ owner : event . repository . owner . login ,
155
+ repo : event . repository . name ,
156
+ head : `${ event . repository . owner . login } :${ branch } `
157
+ } ) ;
158
+ if ( pulls && pulls . data . length > 0 ) {
159
+ // Just use first PR
160
+ const pr = pulls . data [ 0 ] ;
161
+ const baseBranch = pr . base . ref ;
162
+ // Ensure we have the commits from that ref
163
+ await execFile ( 'git' , [ 'fetch' , 'origin' , baseBranch ] ) ;
164
+ diff = {
165
+ baseSha : baseBranch ,
166
+ filesChangedRaw : ( await execFile (
167
+ 'git' , [ 'diff' , '--name-only' , `origin/${ baseBranch } ..${ event . after } ` ]
168
+ ) ) . stdout
169
+ }
170
+ } else {
171
+ console . log ( 'No pull requests associated with the current push' ) ;
172
+ }
173
+ } catch ( err ) {
174
+ console . warn ( err ) ;
175
+ console . log ( `Failed to use PRs to calculate changed files branch ${ branch } .` ) ;
176
+ }
177
+ } else {
178
+ console . log (
179
+ 'Push was not for a branch, calculating changed files differently'
180
+ ) ;
181
+ }
182
+
183
+ // Try (2) - see what files have changed in the last push
184
+
185
+ if ( ! diff ) {
186
+ try {
187
+ const result = await execFile (
188
+ 'git' , [ 'diff' , '--name-only' , `${ event . before } ..${ event . after } ` ]
189
+ ) ;
190
+ if ( result )
191
+ diff = {
192
+ baseSha : event . before ,
193
+ filesChangedRaw : result . stdout
194
+ } ;
195
+ } catch ( err ) {
196
+ console . warn ( err ) ;
197
+ console . log ( 'Failed to get diff for push' ) ;
198
+ }
199
+ }
200
+
201
+ // Try (3) - see how the current HEAD differs from the default branch
202
+
203
+ if ( ! diff ) {
204
+ try {
205
+ const defaultBranchSha = await ( await execFile (
206
+ 'git' , [ 'rev-parse' , 'refs/remotes/origin/HEAD' ]
207
+ ) ) . stdout . trim ( ) ;
208
+ const result = await execFile (
209
+ 'git' , [ 'diff' , '--name-only' , `${ defaultBranchSha } ..${ event . after } ` ]
210
+ ) ;
211
+ if ( result )
212
+ diff = {
213
+ baseSha : defaultBranchSha ,
214
+ filesChangedRaw : result . stdout
215
+ }
216
+ } catch ( err ) {
217
+ console . warn ( err ) ;
218
+ console . log ( 'Failed to diff against default branch' ) ;
219
+ }
220
+ }
221
+
222
+ if ( ! diff ) {
223
+ unableToGetChangedQueries = true ;
224
+ } else {
225
+ // We have successfully obtained the diff for this push
226
+ diff . filesChangedRaw . split ( '\n' )
227
+ . map ( s => s . trim ( ) )
228
+ . filter ( s => s . endsWith ( '.ql' ) )
229
+ . forEach ( s => queriesChanged . add ( s ) ) ;
230
+ console . log ( `${ pluralize ( queriesChanged . size , 'query' ) } updated in this push` ) ;
231
+ comment += `${ pluralize ( queriesChanged . size , 'query' ) } changed `
232
+ comment += `[between \`${ diff . baseSha . substr ( 0 , 7 ) } \` and \`${ event . after . substr ( 0 , 7 ) } \`]`
233
+ comment += `(${ event . repository . html_url } /compare/${ diff . baseSha } ...${ event . after } ) after push to \`${ event . ref } \`` ;
234
+ if ( queriesChanged . size > 0 ) {
235
+ comment += ':\n' ;
236
+ for ( const query of queriesChanged ) {
237
+ console . log ( `- ${ query } ` ) ;
238
+ const exists = await access ( query , fs . constants . R_OK ) . then ( ( ) => true , ( ) => false ) ;
239
+ comment += `* \`${ query } \`${ exists ? '' : ' *(deleted)*' } \n` ;
240
+ }
241
+ } else {
242
+ comment += '\n' ;
243
+ }
121
244
}
122
- } else {
123
- comment += '\n' ;
124
245
}
125
246
126
247
// Work out which queries to run, based on config and changed & existing queries
127
248
const queriesToRun : string [ ] = [ ] ;
128
249
for ( const query of Object . keys ( config . expectedResults ) ) {
129
250
const exists = await access ( query , fs . constants . R_OK ) . then ( ( ) => true , ( ) => false ) ;
130
251
// Run the query if either it's changed, or runAll is true
131
- if ( exists && ( RUN_ALL || queriesChanged . has ( query ) ) ) {
252
+ if ( exists && ( RUN_ALL || unableToGetChangedQueries || queriesChanged . has ( query ) ) ) {
132
253
queriesToRun . push ( query ) ;
133
254
}
134
255
}
0 commit comments