@@ -133,12 +133,314 @@ const cascadingRuns = async (context, req) => {
133133
134134 return comment
135135 }
136+ if ( checkRunOwner === 'git-for-windows'
137+ && checkRunRepo === 'git'
138+ && name . startsWith ( 'git-artifacts-' ) ) {
139+ const output = req . body . check_run . output
140+ const match = output . summary . match (
141+ / B u i l d G i t ( \S + ) a r t i f a c t s f r o m c o m m i t ( \S + ) \( t a g - g i t r u n # ( \d + ) \) /
142+ )
143+ if ( ! match ) throw new Error (
144+ `Could not parse 'summary' attribute of check-run ${ req . body . check_run . id } : ${ output . summary } `
145+ )
146+ const [ , ver , commit , tagGitWorkflowRunID ] = match
147+ const snapshotTag = `prerelease-${ ver . replace ( / ^ v / , '' ) } `
148+
149+ // First, verify that the snapshot has not been uploaded yet
150+ const gitSnapshotsToken = await getToken ( context , checkRunOwner , 'git-snapshots' )
151+ const githubApiRequest = require ( './github-api-request' )
152+ try {
153+ const releasePath = `${ checkRunOwner } /git-snapshots/releases/tags/${ snapshotTag } `
154+ await githubApiRequest (
155+ context ,
156+ gitSnapshotsToken ,
157+ 'GET' ,
158+ `/repos/${ releasePath } ` ,
159+ )
160+ return `Ignoring ${ name } check-run because the snapshot for ${ commit } was already uploaded`
161+ + ` to https://github.com/${ releasePath } `
162+ } catch ( e ) {
163+ if ( e ?. statusCode !== 404 ) throw e
164+ // The snapshot does not exist yet
165+ }
166+
167+ // Next, check that the commit is on the `main` branch
168+ const gitToken = await getToken ( context , checkRunOwner , checkRunRepo )
169+ const { behind_by } = await githubApiRequest (
170+ context ,
171+ gitToken ,
172+ 'GET' ,
173+ `/repos/${ checkRunOwner } /${ checkRunRepo } /compare/HEAD...${ commit } ` ,
174+ )
175+ if ( behind_by > 0 ) {
176+ return `Ignoring ${ name } check-run because its corresponding commit ${ commit } is not on the main branch`
177+ }
178+
179+ const workFlowRunIDs = { }
180+ const { listCheckRunsForCommit, queueCheckRun } = require ( './check-runs' )
181+ for ( const architecture of [ 'x86_64' , 'i686' , 'aarch64' ] ) {
182+ const workflowName = `git-artifacts-${ architecture } `
183+ const runs = name === workflowName ? [ req . body . check_run ] : await listCheckRunsForCommit (
184+ context ,
185+ gitToken ,
186+ checkRunOwner ,
187+ checkRunRepo ,
188+ commit ,
189+ workflowName
190+ )
191+ const needle =
192+ `Build Git ${ ver } artifacts from commit ${ commit } (tag-git run #${ tagGitWorkflowRunID } )`
193+ const latest = runs
194+ . filter ( run => run . output . summary === needle )
195+ . sort ( ( a , b ) => a . id - b . id )
196+ . pop ( )
197+ if ( latest ) {
198+ if ( latest . status !== 'completed' ) {
199+ return `The '${ workflowName } ' run at ${ latest . html_url } did not complete yet.`
200+ }
201+ if ( latest . conclusion !== 'success' ) {
202+ throw new Error ( `The '${ workflowName } ' run at ${ latest . html_url } did not succeed.` )
203+ }
204+
205+ const match = latest . output . text . match (
206+ / F o r d e t a i l s , s e e \[ t h i s r u n \] \( h t t p s : \/ \/ g i t h u b .c o m \/ ( [ ^ / ] + ) \/ ( [ ^ / ] + ) \/ a c t i o n s \/ r u n s \/ ( \d + ) \) /
207+ )
208+ if ( ! match ) throw new Error ( `Unhandled 'text' attribute of git-artifacts run ${ latest . id } : ${ latest . url } ` )
209+ const owner = match [ 1 ]
210+ const repo = match [ 2 ]
211+ workFlowRunIDs [ architecture ] = match [ 3 ]
212+ if ( owner !== 'git-for-windows' || repo !== 'git-for-windows-automation' ) {
213+ throw new Error ( `Unexpected repository ${ owner } /${ repo } for git-artifacts run ${ latest . id } : ${ latest . url } ` )
214+ }
215+ } else {
216+ return `Won't trigger 'upload-snapshot' in reaction to ${ name } because the '${ workflowName } ' run does not exist.`
217+ }
218+ }
219+
220+ const checkRunTitle = `Upload snapshot ${ snapshotTag } `
221+ await queueCheckRun (
222+ context ,
223+ gitToken ,
224+ 'git-for-windows' ,
225+ 'git' ,
226+ commit ,
227+ 'upload-snapshot' ,
228+ checkRunTitle ,
229+ checkRunTitle
230+ )
231+
232+ const gitForWindowsAutomationToken =
233+ await getToken ( context , checkRunOwner , 'git-for-windows-automation' )
234+ const triggerWorkflowDispatch = require ( './trigger-workflow-dispatch' )
235+ const answer = await triggerWorkflowDispatch (
236+ context ,
237+ gitForWindowsAutomationToken ,
238+ 'git-for-windows' ,
239+ 'git-for-windows-automation' ,
240+ 'upload-snapshot.yml' ,
241+ 'main' , {
242+ git_artifacts_x86_64_workflow_run_id : workFlowRunIDs [ 'x86_64' ] ,
243+ git_artifacts_i686_workflow_run_id : workFlowRunIDs [ 'i686' ] ,
244+ git_artifacts_aarch64_workflow_run_id : workFlowRunIDs [ 'aarch64' ] ,
245+ }
246+ )
247+
248+ return `The 'upload-snapshot' workflow run was started at ${ answer . html_url } `
249+ }
136250 return `Not a cascading run: ${ name } ; Doing nothing.`
137251 }
138252 return `Unhandled action: ${ action } `
139253}
140254
255+ const handlePush = async ( context , req ) => {
256+ const pushOwner = req . body . repository . owner . login
257+ const pushRepo = req . body . repository . name
258+ const ref = req . body . ref
259+ const commit = req . body . after
260+
261+ if ( pushOwner !== 'git-for-windows' || pushRepo !== 'git' ) {
262+ throw new Error ( `Refusing to handle push to ${ pushOwner } /${ pushRepo } ` )
263+ }
264+
265+ if ( ref !== 'refs/heads/main' ) return `Ignoring push to ${ ref } `
266+
267+ // See whether there was are already a `tag-git` check-run for this commit
268+ const { listCheckRunsForCommit, queueCheckRun, updateCheckRun } = require ( './check-runs' )
269+ const gitToken = await getToken ( context , pushOwner , pushRepo )
270+ const runs = await listCheckRunsForCommit (
271+ context ,
272+ gitToken ,
273+ pushOwner ,
274+ pushRepo ,
275+ commit ,
276+ 'tag-git'
277+ )
278+
279+ const latest = runs
280+ . sort ( ( a , b ) => a . id - b . id )
281+ . pop ( )
282+
283+ if ( latest && latest . status !== 'completed' ) throw new Error ( `The 'tag-git' run at ${ latest . html_url } did not complete yet before ${ commit } was pushed to ${ ref } !` )
284+
285+ const gitForWindowsAutomationToken =
286+ await getToken ( context , pushOwner , 'git-for-windows-automation' )
287+ const triggerWorkflowDispatch = require ( './trigger-workflow-dispatch' )
288+ if ( ! latest ) {
289+ // There is no `tag-git` workflow run; Trigger it to build a new snapshot
290+ const tagGitCheckRunTitle = `Tag snapshot Git @${ commit } `
291+ const tagGitCheckRunId = await queueCheckRun (
292+ context ,
293+ gitForWindowsAutomationToken ,
294+ pushOwner ,
295+ pushRepo ,
296+ commit ,
297+ 'tag-git' ,
298+ tagGitCheckRunTitle ,
299+ tagGitCheckRunTitle
300+ )
301+
302+ try {
303+ const answer = await triggerWorkflowDispatch (
304+ context ,
305+ gitForWindowsAutomationToken ,
306+ pushOwner ,
307+ 'git-for-windows-automation' ,
308+ 'tag-git.yml' ,
309+ 'main' , {
310+ rev : commit ,
311+ owner : pushOwner ,
312+ repo : pushRepo ,
313+ snapshot : 'true'
314+ }
315+ )
316+ return `The 'tag-git' workflow run was started at ${ answer . html_url } `
317+ } catch ( e ) {
318+ await updateCheckRun (
319+ context ,
320+ gitForWindowsAutomationToken ,
321+ pushOwner ,
322+ pushRepo ,
323+ tagGitCheckRunId , {
324+ status : 'completed' ,
325+ conclusion : 'failure' ,
326+ output : {
327+ title : tagGitCheckRunTitle ,
328+ summary : tagGitCheckRunTitle ,
329+ text : e . message || JSON . stringify ( e , null , 2 )
330+ }
331+ }
332+ )
333+ throw e
334+ }
335+ }
336+
337+ if ( latest . conclusion !== 'success' ) throw new Error (
338+ `The 'tag-git' run at ${ latest . html_url } did not succeed (conclusion = ${ latest . conclusion } ).`
339+ )
340+
341+ // There is already a `tag-git` workflow run; Is there already an `upload-snapshot` run?
342+ const latestUploadSnapshotRun = ( await listCheckRunsForCommit (
343+ context ,
344+ gitToken ,
345+ pushOwner ,
346+ pushRepo ,
347+ commit ,
348+ 'upload-snapshot'
349+ ) ) . pop ( )
350+ if ( latestUploadSnapshotRun ) return `The 'upload-snapshot' check-run already exists for ${ commit } : ${ latestUploadSnapshotRun . html_url } `
351+
352+ // Trigger the `upload-snapshot` run directly
353+ const tagGitCheckRunTitle = `Upload snapshot Git @${ commit } `
354+ const tagGitCheckRunId = await queueCheckRun (
355+ context ,
356+ await getToken ( ) ,
357+ pushOwner ,
358+ pushRepo ,
359+ commit ,
360+ 'tag-git' ,
361+ tagGitCheckRunTitle ,
362+ tagGitCheckRunTitle
363+ )
364+
365+ const match = latest . output . summary . match ( / ^ T a g G i t ( \S + ) @ ( [ 0 - 9 a - f ] + ) $ / )
366+ if ( ! match ) throw new Error ( `Unexpected summary '${ latest . output . summary } ' of tag-git run: ${ latest . html_url } ` )
367+ if ( ! match [ 2 ] === commit ) throw new Error ( `Unexpected revision ${ match [ 2 ] } '${ latest . output . summary } ' of tag-git run: ${ latest . html_url } ` )
368+ const ver = match [ 1 ]
369+
370+ try {
371+ const workFlowRunIDs = { }
372+ for ( const architecture of [ 'x86_64' , 'i686' , 'aarch64' ] ) {
373+ const workflowName = `git-artifacts-${ architecture } `
374+ const runs = await listCheckRunsForCommit (
375+ context ,
376+ gitToken ,
377+ pushOwner ,
378+ pushRepo ,
379+ commit ,
380+ workflowName
381+ )
382+ const needle =
383+ `Build Git ${ ver } artifacts from commit ${ commit } (tag-git run #${ latest . id } )`
384+ const latest2 = runs
385+ . filter ( run => run . output . summary === needle )
386+ . sort ( ( a , b ) => a . id - b . id )
387+ . pop ( )
388+ if ( latest2 ) {
389+ if ( latest2 . status !== 'completed' || latest2 . conclusion !== 'success' ) {
390+ throw new Error ( `The '${ workflowName } ' run at ${ latest2 . html_url } did not succeed.` )
391+ }
392+
393+ const match = latest2 . output . text . match (
394+ / F o r d e t a i l s , s e e \[ t h i s r u n \] \( h t t p s : \/ \/ g i t h u b .c o m \/ ( [ ^ / ] + ) \/ ( [ ^ / ] + ) \/ a c t i o n s \/ r u n s \/ ( \d + ) \) /
395+ )
396+ if ( ! match ) throw new Error ( `Unhandled 'text' attribute of git-artifacts run ${ latest2 . id } : ${ latest2 . url } ` )
397+ const owner = match [ 1 ]
398+ const repo = match [ 2 ]
399+ workFlowRunIDs [ architecture ] = match [ 3 ]
400+ if ( owner !== 'git-for-windows' || repo !== 'git-for-windows-automation' ) {
401+ throw new Error ( `Unexpected repository ${ owner } /${ repo } for git-artifacts run ${ latest2 . id } : ${ latest2 . url } ` )
402+ }
403+ } else {
404+ return `Won't trigger 'upload-snapshot' on pushing ${ commit } because the '${ workflowName } ' run does not exist.`
405+ }
406+ }
407+
408+ const answer = await triggerWorkflowDispatch (
409+ context ,
410+ gitForWindowsAutomationToken ,
411+ pushRepo ,
412+ 'git-for-windows-automation' ,
413+ 'upload-snapshot.yml' ,
414+ 'main' , {
415+ git_artifacts_x86_64_workflow_run_id : workFlowRunIDs [ 'x86_64' ] ,
416+ git_artifacts_i686_workflow_run_id : workFlowRunIDs [ 'i686' ] ,
417+ git_artifacts_aarch64_workflow_run_id : workFlowRunIDs [ 'aarch64' ] ,
418+ }
419+ )
420+
421+ return `The 'upload-snapshot' workflow run was started at ${ answer . html_url } `
422+ } catch ( e ) {
423+ await updateCheckRun (
424+ context ,
425+ gitForWindowsAutomationToken ,
426+ pushOwner ,
427+ pushRepo ,
428+ tagGitCheckRunId , {
429+ status : 'completed' ,
430+ conclusion : 'failure' ,
431+ output : {
432+ title : tagGitCheckRunTitle ,
433+ summary : tagGitCheckRunTitle ,
434+ text : e . message || JSON . stringify ( e , null , 2 )
435+ }
436+ }
437+ )
438+ throw e
439+ }
440+ }
441+
141442module . exports = {
142443 triggerGitArtifactsRuns,
143- cascadingRuns
444+ cascadingRuns,
445+ handlePush
144446}
0 commit comments