Skip to content

Commit d6560d2

Browse files
committed
Teach /release to trigger the release-git workflow
This concludes the transition of Git for Windows' release process off of Azure Pipelines onto GitHub workflows. The added code is a bit more verbose than the previous one because we have to do all the hard work of maintaining the Check Run in the git-for-windows/git PR manually. This is actually a net improvement over the previous situation, where there was _no_ update in that PR whatsoever as to the current state of the release. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 826d911 commit d6560d2

2 files changed

Lines changed: 133 additions & 6 deletions

File tree

GitForWindowsHelper/slash-commands.js

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,101 @@ module.exports = async (context, req) => {
308308
await checkPermissions()
309309
await thumbsUp()
310310

311-
const { releaseGitArtifacts } = require('./azure-pipelines')
312-
const answer = await releaseGitArtifacts(context, req.body.issue.number)
311+
// Find the `git-artifacts` runs' IDs
312+
const { getPRCommitSHA } = require('./issues')
313+
const commitSHA = await getPRCommitSHA(context, await getToken(), owner, repo, issueNumber)
313314

314-
const { appendToIssueComment } = require('./issues')
315-
const answer2 = await appendToIssueComment(context, await getToken(), owner, repo, commentId, `The Azure Release Pipeline run [was started](${answer.url})`)
316-
return `I edited the comment: ${answer2.html_url}`
315+
const { listCheckRunsForCommit, queueCheckRun, updateCheckRun } = require('./check-runs')
316+
const releaseCheckRunId = await queueCheckRun(
317+
context,
318+
await getToken(),
319+
'git-for-windows',
320+
repo,
321+
commitSHA,
322+
'github-release',
323+
`Publish Git for Windows @${commitSHA}`,
324+
`Downloading the Git artifacts from the 'git-artifacts' runs and publishing them as a new GitHub Release at ${owner}/${repo}`
325+
)
326+
327+
try {
328+
let gitVersion
329+
let tagGitWorkflowRunID
330+
const workFlowRunIDs = {}
331+
for (const architecture of ['x86_64', 'i686']) {
332+
const workflowName = `git-artifacts-${architecture}`
333+
const runs = await listCheckRunsForCommit(
334+
context,
335+
await getToken(owner, repo),
336+
owner,
337+
repo,
338+
commitSHA,
339+
workflowName
340+
)
341+
const latest = runs
342+
.filter(run => run.output.summary.indexOf(` from commit ${commitSHA} ` > 0))
343+
.sort((a, b) => a.id - b.id)
344+
.pop()
345+
if (latest) {
346+
if (latest.status !== 'completed' || latest.conclusion !== 'success') {
347+
throw new Error(`The '${workflowName}}' run at ${latest.html_url} did not succeed.`)
348+
}
349+
workFlowRunIDs[architecture] = latest.id
350+
const gitVersionMatch = latest.output.summary.match(/^Build Git (\S+) artifacts from commit (\S+) \(tag-git run #(\d+)\)$/)
351+
if (!gitVersionMatch) throw new Error(`Could not parse summary '${latest.output.summary}' of run ${latest.id}`)
352+
if (!gitVersion) gitVersion = gitVersionMatch[1]
353+
else if (gitVersion !== gitVersionMatch[1]) throw new Error(`The 'git-artifacts' runs disagree on the Git version`)
354+
if (!tagGitWorkflowRunID) tagGitWorkflowRunID = gitVersionMatch[3]
355+
else if (tagGitWorkflowRunID !== gitVersionMatch[3]) throw new Error(`The 'git-artifacts' runs are based on different 'tag-git' runs`)
356+
} else {
357+
throw new Error(`The '${workflowName}' run was not found`)
358+
}
359+
}
360+
361+
await updateCheckRun(
362+
context,
363+
await getToken(),
364+
owner,
365+
repo,
366+
releaseCheckRunId, {
367+
output: {
368+
title: `Publish ${gitVersion} for @${commitSHA}`,
369+
summary: `Downloading the Git artifacts from ${workFlowRunIDs['x86_64']} and ${workFlowRunIDs['i686']} and publishing them as a new GitHub Release at ${owner}/${repo}`
370+
}
371+
}
372+
)
373+
374+
const triggerWorkflowDispatch = require('./trigger-workflow-dispatch')
375+
const answer = await triggerWorkflowDispatch(
376+
context,
377+
await getToken(),
378+
'git-for-windows',
379+
'git-for-windows-automation',
380+
'release-git.yml',
381+
'main', {
382+
git_artifacts_x86_64_workflow_run_id: workFlowRunIDs['x86_64'],
383+
git_artifacts_i686_workflow_run_id: workFlowRunIDs['i686']
384+
}
385+
)
386+
387+
const { appendToIssueComment } = require('./issues')
388+
const answer2 = await appendToIssueComment(context, await getToken(), owner, repo, commentId, `The \`release-git\` workflow run [was started](${answer.html_url})`)
389+
return `I edited the comment: ${answer2.html_url}`
390+
} catch (e) {
391+
await updateCheckRun(
392+
context,
393+
await getToken(),
394+
owner,
395+
repo,
396+
releaseCheckRunId, {
397+
status: 'completed',
398+
conclusion: 'failure',
399+
output: {
400+
text: e.toString()
401+
}
402+
}
403+
)
404+
throw e
405+
}
317406
}
318407

319408
const relNotesMatch = command.match(/^\/add (relnote|release ?note)(\s+(blurb|feature|bug)\s+([^]*))?$/i)

__tests__/index.test.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ let mockGitHubApiRequest = jest.fn((_context, _token, method, requestPath, paylo
106106
if (method === 'GET' && requestPath.endsWith('/pulls/4322')) return {
107107
head: { sha: 'c8edb521bdabec14b07e9142e48cab77a40ba339' }
108108
}
109+
if (method === 'GET' && requestPath.endsWith('/pulls/4328')) return {
110+
head: { sha: 'this-will-be-rc2' }
111+
}
109112
throw new Error(`Unhandled ${method}-${requestPath}-${JSON.stringify(payload)}`)
110113
})
111114
jest.mock('../GitForWindowsHelper/github-api-request', () => {
@@ -262,7 +265,15 @@ The MINGW workflow run [was started](dispatched-workflow-open-pr.yml)`
262265

263266
let mockQueueCheckRun = jest.fn(() => 'check-run-id')
264267
let mockUpdateCheckRun = jest.fn()
265-
let mockListCheckRunsForCommit = jest.fn((_context, _token, _owner, _repo, _rev, checkRunName) => {
268+
let mockListCheckRunsForCommit = jest.fn((_context, _token, _owner, _repo, rev, checkRunName) => {
269+
if (rev === 'this-will-be-rc2') {
270+
const output = {
271+
title: 'Build Git -rc2 artifacts',
272+
summary: 'Build Git -rc2 artifacts from commit this-will-be-rc2 (tag-git run #987)'
273+
}
274+
if (checkRunName === 'git-artifacts-x86_64') return [{ id: 8664, status: 'completed', conclusion: 'success', output }]
275+
if (checkRunName === 'git-artifacts-i686') return [{ id: 686, status: 'completed', conclusion: 'success', output }]
276+
}
266277
if (checkRunName === 'git-artifacts-x86_64') return [{
267278
status: 'completed',
268279
conclusion: 'success',
@@ -422,3 +433,30 @@ The \`tag-git\` workflow run [was started](dispatched-workflow-tag-git.yml)`,
422433
snapshot: false
423434
})
424435
})
436+
437+
testIssueComment('/release', {
438+
issue: {
439+
number: 4328,
440+
title: 'Rebase to v2.40.0-rc2',
441+
pull_request: {
442+
html_url: 'https://github.com/git-for-windows/git/pull/4328'
443+
}
444+
}
445+
}, async (context) => {
446+
expect(await index(context, context.req)).toBeUndefined()
447+
expect(context.res).toEqual({
448+
body: `I edited the comment: appended-comment-body-existing comment body
449+
450+
The \`release-git\` workflow run [was started](dispatched-workflow-release-git.yml)`,
451+
headers: undefined,
452+
status: undefined
453+
})
454+
expect(mockGetInstallationAccessToken).toHaveBeenCalledTimes(1)
455+
expect(mockGitHubApiRequestAsApp).not.toHaveBeenCalled()
456+
expect(dispatchedWorkflows).toHaveLength(1)
457+
expect(dispatchedWorkflows[0].html_url).toEqual('dispatched-workflow-release-git.yml')
458+
expect(dispatchedWorkflows[0].payload.inputs).toEqual({
459+
git_artifacts_x86_64_workflow_run_id: 8664,
460+
git_artifacts_i686_workflow_run_id: 686
461+
})
462+
})

0 commit comments

Comments
 (0)