2
2
# cSpell:ignore PULLREQUEST
3
3
# cSpell:ignore TARGETBRANCH
4
4
# cSpell:ignore SOURCECOMMITID
5
+
6
+ function RequestGithubGraphQL {
7
+ param (
8
+ [string ]$query ,
9
+ [object ]$variables = @ {}
10
+ )
11
+
12
+ $payload = @ {
13
+ query = $query
14
+ variables = $variables
15
+ } | ConvertTo-Json - Depth 100
16
+
17
+ $response = $payload | gh api graphql -- input -
18
+ $data = $response | ConvertFrom-Json
19
+
20
+ if ($LASTEXITCODE ) {
21
+ LogWarning " Failed graphql operation:"
22
+ LogWarning ($payload -replace ' \\n' , " `n " )
23
+ if ($data.errors ) {
24
+ LogWarning ($data.errors.message -join " `n " )
25
+ }
26
+ throw " graphql operation failed ($LASTEXITCODE )"
27
+ }
28
+
29
+ return $data
30
+ }
31
+
5
32
function Get-ChangedFiles {
6
33
param (
7
- [string ]$SourceCommittish = " ${env: SYSTEM_PULLREQUEST_SOURCECOMMITID} " ,
34
+ [string ]$SourceCommittish = " ${env: SYSTEM_PULLREQUEST_SOURCECOMMITID} " ,
8
35
[string ]$TargetCommittish = (" origin/${env: SYSTEM_PULLREQUEST_TARGETBRANCH} " -replace " refs/heads/" ),
9
36
[string ]$DiffPath ,
10
37
[string ]$DiffFilterType = " d"
@@ -21,18 +48,18 @@ function Get-ChangedFiles {
21
48
# Git PR diff: https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-comparing-branches-in-pull-requests#three-dot-and-two-dot-git-diff-comparisons
22
49
$command = " git -c core.quotepath=off -c i18n.logoutputencoding=utf-8 diff `" $TargetCommittish ...$SourceCommittish `" --name-only --diff-filter=$DiffFilterType "
23
50
if ($DiffPath ) {
24
- $command = $command + " -- `' $DiffPath `' "
51
+ $command = $command + " -- `' $DiffPath `' "
25
52
}
26
53
Write-Host $command
27
54
$changedFiles = Invoke-Expression - Command $command
28
- if (! $changedFiles ) {
55
+ if (! $changedFiles ) {
29
56
Write-Host " No changed files in git diff between $TargetCommittish and $SourceCommittish "
30
57
}
31
58
else {
32
- Write-Host " Here are the diff files:"
33
- foreach ($file in $changedFiles ) {
59
+ Write-Host " Here are the diff files:"
60
+ foreach ($file in $changedFiles ) {
34
61
Write-Host " $file "
35
- }
62
+ }
36
63
}
37
64
return $changedFiles
38
65
}
@@ -58,7 +85,7 @@ class ConflictedFile {
58
85
$this.ParseContent ($this.Content )
59
86
}
60
87
61
- [array ] Left(){
88
+ [array ] Left() {
62
89
if ($this.IsConflicted ) {
63
90
# we are forced to get this line by line and reassemble via join because of how powershell is interacting with
64
91
# git show --textconv commitsh:path
@@ -75,7 +102,7 @@ class ConflictedFile {
75
102
}
76
103
}
77
104
78
- [array ] Right(){
105
+ [array ] Right() {
79
106
if ($this.IsConflicted ) {
80
107
$toShow = " $ ( $this.RightSource ) :$ ( $this.Path ) " -replace " \\" , " /"
81
108
Write-Host " git show $toShow "
@@ -92,7 +119,7 @@ class ConflictedFile {
92
119
$l = @ ()
93
120
$r = @ ()
94
121
95
- foreach ($line in $lines ) {
122
+ foreach ($line in $lines ) {
96
123
if ($line -match " ^<<<<<<<\s*(.+)" ) {
97
124
$this.IsConflicted = $true
98
125
$this.LeftSource = $matches [1 ]
@@ -247,4 +274,89 @@ function Write-RateLimit {
247
274
Write-Host " remaining=$ ( $RateLimit.remaining ) "
248
275
Write-Host " reset=$ ( $RateLimit.reset ) "
249
276
Write-Host " "
250
- }
277
+ }
278
+
279
+ function GetUnresolvedAIReviewThreads {
280
+ param (
281
+ [string ]$repoOwner ,
282
+ [string ]$repoName ,
283
+ [string ]$prNumber ,
284
+ [array ]$reviewers
285
+ )
286
+
287
+ $reviewers ?? = @ (' copilot-pull-request-reviewer' )
288
+
289
+ $reviewThreadsQuery = @'
290
+ query ReviewThreads($owner: String!, $name: String!, $number: Int!) {
291
+ repository(owner: $owner, name: $name) {
292
+ pullRequest(number: $number) {
293
+ reviewThreads(first: 100) {
294
+ nodes {
295
+ id
296
+ isResolved
297
+ comments(first: 100) {
298
+ nodes {
299
+ body
300
+ author {
301
+ login
302
+ }
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ '@
311
+
312
+ $variables = @ { owner = $repoOwner ; name = $repoName ; number = [int ]$prNumber }
313
+ $response = RequestGithubGraphQL - query $reviewThreadsQuery - variables $variables
314
+ $reviews = $response.data.repository.pullRequest.reviewThreads.nodes
315
+
316
+ $threadIds = @ ()
317
+ # There should be only one threadId for copilot, but make it an array in case there
318
+ # are more, or if we want to resolve threads from multiple ai authors in the future.
319
+ # Don't mark threads from humans as resolved, as those may be real questions/blockers.
320
+ foreach ($thread in $reviews ) {
321
+ if ($thread.comments.nodes | Where-Object { $_.author.login -in $reviewers }) {
322
+ if (! $thread.isResolved ) {
323
+ $threadIds += $thread.id
324
+ }
325
+ continue
326
+ }
327
+ }
328
+
329
+ return $threadIds
330
+ }
331
+
332
+ function TryResolveAIReviewThreads {
333
+ param (
334
+ [string ]$repoOwner ,
335
+ [string ]$repoName ,
336
+ [string ]$prNumber ,
337
+ [array ]$reviewers
338
+ )
339
+
340
+ $resolveThreadMutation = @'
341
+ mutation ResolveThread($id: ID!) {
342
+ resolveReviewThread(input: { threadId: $id }) {
343
+ thread {
344
+ isResolved
345
+ }
346
+ }
347
+ }
348
+ '@
349
+
350
+ $threadIds = GetUnresolvedAIReviewThreads - repoOwner $repoOwner - repoName $repoName - prNumber $prNumber - reviewers $reviewers
351
+
352
+ if (! $threadIds ) {
353
+ return $false
354
+ }
355
+
356
+ foreach ($threadId in $threadIds ) {
357
+ LogInfo " Resolving review thread '$threadId ' for '$repoName ' PR '$prNumber '"
358
+ RequestGithubGraphQL - query $resolveThreadMutation - variables @ { id = $threadId }
359
+ }
360
+
361
+ return $true
362
+ }
0 commit comments