Skip to content

Commit 2ea7d5c

Browse files
authored
Merge pull request #13872 from MicrosoftDocs/workflows-test
add PR close functionality
2 parents dff3e84 + 5ee90b5 commit 2ea7d5c

2 files changed

Lines changed: 155 additions & 136 deletions

File tree

.github/workflows/Shared-AutoLabelMsftContributor.yml

Lines changed: 154 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Label Microsoft contrib
1+
name: Label Microsoft contrib and close PR
22

33
permissions:
44
pull-requests: write
@@ -23,13 +23,25 @@ jobs:
2323
if: github.repository_owner == 'MicrosoftDocs'
2424
runs-on: ubuntu-latest
2525
steps:
26+
27+
- name: Create App Token
28+
id: app-token
29+
uses: actions/create-github-app-token@v1
30+
with:
31+
app-id: ${{ secrets.ClientId }}
32+
private-key: ${{ secrets.PrivateKey }}
33+
owner: ${{ github.repository_owner }}
34+
2635
- name: Script
2736
shell: pwsh
2837
env:
2938
PayloadJson: ${{ inputs.PayloadJson }}
39+
AppGitHubAccessToken: ${{ steps.app-token.outputs.token }}
3040
AccessToken: ${{ secrets.AccessToken }}
3141
ClientId: ${{ secrets.ClientId }}
3242
PrivateKey: ${{ secrets.PrivateKey }}
43+
IsPublicOnlyRepo: ${{ contains(github.event.repository.topics, 'publiconly') }}
44+
3345

3446
run: |
3547
@@ -38,144 +50,21 @@ jobs:
3850
# Get payload data from GitHub.
3951
$GitHubData = $env:PayloadJson | ConvertFrom-Json -Depth 50
4052
$GitRequestEvent = $GitHubData.event_name
53+
$GitHubAction = $GithubData.event.action
4154
$AccessToken = $env:AccessToken
55+
$AppGitHubAccessToken = $env:AppGitHubAccessToken
4256
$ClientId = $env:ClientId
4357
$PrivateKey = $env:PrivateKey
58+
$IsPublicOnlyRepo = [System.Convert]::ToBoolean($env:IsPublicOnlyRepo)
4459
4560
$RepoName = $GitHubData.event.repository.name
4661
$RepoUrl = $GitHubData.event.repository.url
47-
$RepoTopicUrl = "$RepoUrl/topics"
4862
4963
# URL of the org team to check. We're using the "everyone" team in MicrosoftDocs.
5064
$OrgTeamUrl = "https://api.github.com/orgs/microsoftdocs/teams/everyone/memberships/"
5165
52-
###########################################
53-
###########################################
54-
# GitHub app/installation token block start
55-
###########################################
56-
###########################################
57-
58-
Function ConvertTo-JwtBase64 {
59-
param(
60-
[Parameter(Mandatory = $True)]
61-
[string]$RawJson
62-
)
63-
64-
# Convert UTF-8 bytes to Base64, then make it URL-safe for JWT
65-
$Encoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($RawJson))
66-
Return $Encoded.TrimEnd('=') -replace '\+','-' -replace '/','_'
67-
}
68-
69-
Function New-GitHubAppJWT {
70-
param(
71-
[Parameter(Mandatory = $True)]
72-
[string]$ClientId,
73-
74-
[Parameter(Mandatory = $True)]
75-
[string]$PrivateKey,
76-
77-
[int]$ExpiresInMinutes = 10
78-
)
79-
80-
Write-Host "Create JWT"
81-
82-
# Build the header
83-
$Header = @{
84-
alg = "RS256"
85-
typ = "JWT"
86-
} | ConvertTo-Json -Compress
87-
88-
$HeaderEncoded = ConvertTo-JwtBase64 -RawJson $Header
89-
90-
# Build the payload
91-
$Now = [System.DateTimeOffset]::UtcNow
92-
$Payload = @{
93-
iat = $now.AddSeconds(-10).ToUnixTimeSeconds()
94-
exp = $now.AddMinutes($ExpiresInMinutes).ToUnixTimeSeconds()
95-
iss = $ClientId
96-
} | ConvertTo-Json -Compress
97-
98-
$PayloadEncoded = ConvertTo-JwtBase64 -RawJson $Payload
99-
100-
# Combine and sign
101-
$Rsa = [System.Security.Cryptography.RSA]::Create()
102-
$Rsa.ImportFromPem($PrivateKey)
103-
104-
$Combined = "$HeaderEncoded.$PayloadEncoded"
105-
$SignatureBytes = $Rsa.SignData(
106-
[System.Text.Encoding]::UTF8.GetBytes($Combined),
107-
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
108-
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1
109-
)
110-
111-
$SignatureEncoded = [Convert]::ToBase64String($SignatureBytes).TrimEnd('=') -replace '\+','-' -replace '/','_'
112-
113-
Return "$HeaderEncoded.$PayloadEncoded.$SignatureEncoded"
114-
}
115-
116-
Function Get-GitHubAppInstallationToken {
117-
param(
118-
[Parameter(Mandatory = $true)]
119-
[string]$ClientId,
120-
121-
[Parameter(Mandatory = $true)]
122-
[string]$PrivateKey,
123-
124-
[Parameter(Mandatory = $true)]
125-
[string]$Organization,
126-
127-
[int]$TokenTTLMinutes = 10
128-
)
129-
130-
# Create the JWT
131-
$Jwt = New-GitHubAppJWT -ClientId $ClientId -PrivateKey $PrivateKey -ExpiresInMinutes $TokenTTLMinutes
132-
133-
# Prepare headers
134-
$JwtHeaders = @{
135-
Authorization = "Bearer $Jwt"
136-
Accept = "application/vnd.github+json"
137-
"X-GitHub-Api-Version"= "2022-11-28"
138-
}
139-
140-
Write-Host "Request org installation ID"
141-
142-
# 1) Retrieve the installation ID for the org
143-
Try {
144-
$InstallationInfo = Invoke-RestMethod `
145-
-Uri "https://api.github.com/orgs/$Organization/installation" `
146-
-Headers $JwtHeaders `
147-
-ErrorAction Stop
148-
149-
$InstallationId = $InstallationInfo.id
150-
}
151-
Catch {
152-
Write-Error "Failed to get installation ID from GitHub. $_"
153-
Return $Null
154-
}
155-
156-
Write-Host "Get installation token"
157-
158-
# 2) Use the installation ID to request an installation token
159-
Try {
160-
$TokenResponse = Invoke-RestMethod `
161-
-Uri "https://api.github.com/app/installations/$InstallationId/access_tokens" `
162-
-Headers $JwtHeaders `
163-
-Method Post `
164-
-ErrorAction Stop
165-
166-
Return $TokenResponse.token
167-
}
168-
Catch {
169-
Write-Error "Failed to get access token from GitHub. $_"
170-
Return $Null
171-
}
172-
}
173-
174-
#########################################
175-
#########################################
176-
# GitHub app/installation token block end
177-
#########################################
178-
#########################################
66+
$WorkflowsResourcePath = "https://api.github.com/repos/MicrosoftDocs/microsoft-365-docs/contents/.github/workflows/resources"
67+
$WorkflowsRef = "workflows-prod"
17968
18069
#####################
18170
#####################
@@ -298,9 +187,118 @@ jobs:
298187
299188
#####################
300189
#####################
301-
# Main
190+
# Close-Pr
191+
Function Close-Pr {
192+
193+
param(
194+
195+
$PrUrl,
196+
$Headers
197+
198+
)
199+
200+
$Body = @{
201+
202+
state = "closed"
203+
204+
} | ConvertTo-Json
205+
206+
Try {
207+
208+
Write-Host "Closing PR URL: $PrUrl"
209+
210+
Invoke-RestMethod -Uri $PrUrl -Headers $Headers -Method PATCH -Body $Body -ErrorAction Stop
211+
212+
$PrClosed = $True
213+
214+
} Catch {
215+
216+
Write-Host "ERROR: Failed to close PR URL: $PrUrl. Error: $_"
217+
218+
$PrClosed = $False
219+
220+
}
221+
222+
Return $PrClosed
223+
224+
}
225+
226+
#####################
227+
#####################
228+
# Set-PrMessage
229+
230+
Function Set-PrMessage {
231+
232+
Param(
233+
$Message,
234+
$CommentsUrl,
235+
$Headers
236+
)
237+
238+
$BodyHash = @{}
239+
$BodyHash.body = $Message
240+
$BodyJson = $BodyHash | ConvertTo-Json
241+
$BodyJson
242+
243+
Try {
244+
245+
Write-Host "Setting message on comment URL: $CommentsUrl"
246+
247+
$Result = Invoke-RestMethod -Uri $CommentsUrl -Body $BodyJson -Headers $Headers -Method POST -ErrorAction Stop
248+
249+
$PostCommentSuccess = $True
250+
251+
} Catch {
252+
253+
Write-Host "ERROR: Failed to post message to comments URL: $CommentsUrl. Error: $_"
254+
255+
256+
$PostCommentSuccess = $False
257+
258+
}
259+
260+
Return $PostCommentSuccess
261+
262+
}
263+
264+
#####################
265+
#####################
266+
# Get-PrMessage
267+
268+
Function Get-PrMessage {
302269
270+
[cmdletbinding()]
271+
Param(
272+
[Parameter(Mandatory=$True)]
273+
$PrMessageFileName,
274+
[Parameter(Mandatory=$True)]
275+
$Headers
276+
)
277+
278+
$PrMessageFile = "$WorkflowsResourcePath/$PrMessageFileName`?ref=$WorkflowsRef"
279+
280+
Try {
281+
282+
Write-Host "Getting PR message from $PrMessageFile"
283+
284+
$PrMessageData = Invoke-RestMethod -Uri $PrMessageFile -Headers $Headers
285+
$PrMessage = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($PrMessageData.content));
286+
287+
} Catch {
288+
289+
Write-Host "Failed to get PR message $PrMessageFileName. Error: $_"
290+
291+
$PrMessage = $Null
292+
293+
}
294+
295+
Return $PrMessage
296+
297+
}
303298
299+
#####################
300+
#####################
301+
# Main
304302
305303
Write-Host "Repo: $($GitHubData.event.repository.name)"
306304
Write-Host "Sender: $($GitHubData.event.sender.login)"
@@ -316,16 +314,14 @@ jobs:
316314
# Create team read GitHub HTTP authentication header. Need a token that has access to org scope. Get-GitHubAppInstallationToken
317315
# requests an installation token from our GitHub app so that we can authenticate. GITHUB_TOKEN that is used to populate $AccessToken
318316
# doesn't have access to org scope so using our GitHub app's access.
319-
$GitHubAccessToken = Get-GitHubAppInstallationToken -ClientId $ClientId -PrivateKey $PrivateKey -Organization MicrosoftDocs -TokenTTLMinutes 10
320-
321317
$TeamReadGitHubHeaders = @{}
322-
$TeamReadGitHubHeaders.Add("Authorization","token $($GitHubAccessToken)")
318+
$TeamReadGitHubHeaders.Add("Authorization","token $($AppGitHubAccessToken)")
323319
$TeamReadGitHubHeaders.Add("User-Agent", "OfficeDocs")
324320
$TeamReadGitHubHeaders.Add("Accept","application/vnd.github.mercy-preview+json")
325321
326322
# -and ($GitHubData.event.action -eq "opened")
327323
# Only process event types of 'pull_request_target' that are 'opened' (PR created)
328-
If (($GitRequestEvent -eq "pull_request_target") ) {
324+
If (($GitRequestEvent -eq "pull_request_target") -and ($GitHubAction -eq "opened")) {
329325
330326
$Contributor = $GitHubData.event.pull_request.user.login
331327
$LabelName = "Microsoft submitter"
@@ -390,7 +386,6 @@ jobs:
390386
391387
Write-Host "Setting label on PR."
392388
393-
394389
$LabelUrl = $GitHubData.event.pull_request.issue_url
395390
396391
Write-Host "Using pull request URL $LabelUrl."
@@ -409,6 +404,30 @@ jobs:
409404
410405
}
411406
407+
If ($IsPublicOnlyRepo) {
408+
409+
Write-Host "Repo is a public-only repo. Not closing PR."
410+
411+
} Else {
412+
413+
$PrUrl = $GitHubData.event.pull_request.url
414+
$CommentsUrl = $GitHubData.event.pull_request.comments_url
415+
416+
$CloseSuccess = Close-Pr -PrUrl $PrUrl -Headers $GitHubHeaders
417+
418+
If ($CloseSuccess) {
419+
420+
$PrMessage = Get-PrMessage -PrMessageFileName "AutoLabelMsftContributor-PrCloseMessage.md" -Headers $GitHubHeaders
421+
422+
If ($PrMessage) {
423+
424+
Set-PrMessage -Message $PrMessage -Headers $GitHubHeaders -CommentsUrl $CommentsUrl
425+
426+
}
427+
428+
}
429+
430+
}
412431
413432
} Else {
414433

.github/workflows/resources/AutoLabelMsftContributor-PrCloseMessage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ For example, if the public repository name is `contoso-help`, the private reposi
55
Please re-submit your contribution to the private repository. If you can’t find the private repository to use, do one of the following:
66

77
- **(Preferred)** Go to <https://aka.ms/SaveTime> and paste the public **Learn** (not GitHub) URL of the page you want to update into the field provided. If you haven't used the Learn Editor before, check out the [Learn Editor documentation](https://learn.microsoft.com/en-us/help/platform/learn-editor-overview). If you see a 404, be sure to sign into Learn by clicking **Sign in** in the top-right corner of the page.
8-
- Install the **Microsoft Learn maintenance tool** and, while on the article you want to update, click the extension in the toolbar, then choose **MD** or **YML**.
8+
- Install the **Microsoft Learn maintenance tool** browser extension and, while on the article you want to update, click the extension in the toolbar, then choose **MD** or **YML**.
99
- Retrieve the article source URL from page source:
1010
1. Right-click on the article page and select **View source**.
1111
2. Find *original_content_git_url* and paste that URL into the address bar of a new tab.

0 commit comments

Comments
 (0)