|
| 1 | +# Copyright (c) Microsoft Corporation. |
| 2 | +# Licensed under the MIT License. |
| 3 | + |
| 4 | +#requires -Version 6.0 |
| 5 | + |
| 6 | +using module PowerShellForGitHub |
| 7 | + |
| 8 | +<# |
| 9 | +.SYNOPSIS |
| 10 | + Updates the CHANGELOG file with PRs merged since the last release. |
| 11 | +.DESCRIPTION |
| 12 | + Expects the Git repositories to be checked out correctly as it does not |
| 13 | + change branches or pull. Handles any merge option for PRs, but is a little |
| 14 | + slow as it queries all closed PRs first. |
| 15 | +#> |
| 16 | +[CmdletBinding(SupportsShouldProcess)] |
| 17 | +param( |
| 18 | + [Parameter(Mandatory)] |
| 19 | + [ValidateSet("vscode-powershell", "PowerShellEditorServices")] |
| 20 | + [string]$RepositoryName, |
| 21 | + |
| 22 | + [Parameter(Mandatory)] |
| 23 | + [ValidateScript({ $_.StartsWith("v") })] |
| 24 | + [string]$Version |
| 25 | +) |
| 26 | + |
| 27 | +# TODO: Refactor into functions to map over these. |
| 28 | +$RepoNames = "vscode-powershell", "PowerShellEditorServices" |
| 29 | + |
| 30 | +# NOTE: This a side effect neccesary for Git operations to work. |
| 31 | +Set-Location (Resolve-Path "$PSScriptRoot/../../$RepositoryName") |
| 32 | + |
| 33 | +# Get the repo object, latest release, and commits since its tag. |
| 34 | +$Repo = Get-GitHubRepository -OwnerName PowerShell -RepositoryName $RepositoryName |
| 35 | +$Release = $Repo | Get-GitHubRelease | Select-Object -First 1 |
| 36 | +$Commits = git rev-list "$($Release.tag_name)..." |
| 37 | +# NOTE: This is a slow API as it gets all closed PRs, and then filters. |
| 38 | +$PullRequests = $Repo | Get-GitHubPullRequest -State Closed | |
| 39 | + Where-Object { $_.merge_commit_sha -in $Commits } | |
| 40 | + Where-Object { $_.user.UserName -notmatch "\[bot\]$" } | |
| 41 | + Where-Object { $_.labels.LabelName -notcontains "Ignore" } |
| 42 | + |
| 43 | +$SkipThanks = @( |
| 44 | + 'andschwa' |
| 45 | + 'daxian-dbw' |
| 46 | + 'PaulHigin' |
| 47 | + 'rjmholt' |
| 48 | + 'SteveL-MSFT' |
| 49 | + 'TylerLeonhardt' |
| 50 | +) |
| 51 | + |
| 52 | +$LabelEmoji = @{ |
| 53 | + 'Issue-Enhancement' = '✨' |
| 54 | + 'Issue-Bug' = '🐛' |
| 55 | + 'Issue-Performance' = '⚡️' |
| 56 | + 'Area-Build & Release' = '👷' |
| 57 | + 'Area-Code Formatting' = '💎' |
| 58 | + 'Area-Configuration' = '🔧' |
| 59 | + 'Area-Debugging' = '🔍' |
| 60 | + 'Area-Documentation' = '📖' |
| 61 | + 'Area-Engine' = '🚂' |
| 62 | + 'Area-Folding' = '📚' |
| 63 | + 'Area-Integrated Console' = '📟' |
| 64 | + 'Area-IntelliSense' = '🧠' |
| 65 | + 'Area-Logging' = '💭' |
| 66 | + 'Area-Pester' = '🐢' |
| 67 | + 'Area-Script Analysis' = '🕵️' |
| 68 | + 'Area-Snippets' = '✂️' |
| 69 | + 'Area-Startup' = '🛫' |
| 70 | + 'Area-Symbols & References' = '🔗' |
| 71 | + 'Area-Tasks' = '✅' |
| 72 | + 'Area-Test' = '🚨' |
| 73 | + 'Area-Threading' = '⏱️' |
| 74 | + 'Area-UI' = '📺' |
| 75 | + 'Area-Workspaces' = '📁' |
| 76 | +} |
| 77 | + |
| 78 | +$CloseKeywords = @( |
| 79 | + 'close' |
| 80 | + 'closes' |
| 81 | + 'closed' |
| 82 | + 'fix' |
| 83 | + 'fixes' |
| 84 | + 'fixed' |
| 85 | + 'resolve' |
| 86 | + 'resolves' |
| 87 | + 'resolved' |
| 88 | +) |
| 89 | + |
| 90 | +$IssueRegex = "(" + ($CloseKeywords -join "|") + ")\s+(?<issue>\S+)" |
| 91 | + |
| 92 | +$Bullets = $PullRequests | ForEach-Object { |
| 93 | + # Map all the labels to emoji (or use a default). |
| 94 | + # NOTE: Whitespacing here is weird. |
| 95 | + $emoji = if ($_.labels) { |
| 96 | + $LabelEmoji[$_.labels.LabelName] -join "" |
| 97 | + } else { '#️⃣ 🙏' } |
| 98 | + # Get a linked issue number if it exists (or use the PR). |
| 99 | + $link = if ($_.body -match $IssueRegex) { |
| 100 | + $issue = $Matches.issue |
| 101 | + $number = if ($issue -match "(?<number>\d+)$") { |
| 102 | + $Matches.number |
| 103 | + } else { Write-Error "Couldn't find issue number!" } |
| 104 | + # Handle links to issues in both or repos, in both shortcode and URLs. |
| 105 | + $name = $RepoNames | Where-Object { $issue -match $_ } | Select-Object -First 1 |
| 106 | + "$name #$number" |
| 107 | + } else { "$RepositoryName #$($_.number)" } |
| 108 | + # Thank the contributor if they are not one of us. |
| 109 | + $thanks = if ($_.user.UserName -notin $SkipThanks) { |
| 110 | + "(Thanks @$($_.user.UserName)!)" |
| 111 | + } |
| 112 | + # Put the bullet point together. |
| 113 | + "-", $emoji, "[$link]($($_.html_url))", "-", "$($_.title).", $thanks -join " " |
| 114 | +} |
| 115 | + |
| 116 | +$ChangelogPath = "CHANGELOG.md" |
| 117 | +$CurrentChangelog = Get-Content -Path $ChangelogPath |
| 118 | +# TODO: Handle vscode-powershell edge case. |
| 119 | +$NewChangelog = @( |
| 120 | + "## $Version" |
| 121 | + "### $([datetime]::Now.ToString('dddd, MMMM dd, yyyy'))`n" |
| 122 | + $Bullets |
| 123 | +) |
| 124 | +@( |
| 125 | + $CurrentChangelog[0..1] |
| 126 | + $NewChangelog |
| 127 | + $CurrentChangelog[1..$CurrentChangelog.Length] |
| 128 | +) | Set-Content -Encoding utf8NoBOM -Path $ChangelogPath |
| 129 | + |
| 130 | +if ($PSCmdlet.ShouldProcess("$RepositoryName/$ChangelogPath", "git")) { |
| 131 | + $branch = git branch --show-current |
| 132 | + if ($branch -ne "release/$version") { |
| 133 | + git checkout -b "release/$version" |
| 134 | + } |
| 135 | + git add $ChangelogPath |
| 136 | + git commit -m "Update CHANGELOG for $Version" |
| 137 | + git push |
| 138 | +} |
| 139 | + |
| 140 | +# TODO: Do this in a separate function (will require reading from disk). |
| 141 | +$ReleaseParams = @{ |
| 142 | + Draft = $true |
| 143 | + Tag = $Version |
| 144 | + Name = $Version |
| 145 | + Body = $NewChangelog |
| 146 | + PreRelease = $Version -match "-preview" |
| 147 | +} |
| 148 | +$Repo | New-GitHubRelease @ReleaseParams |
0 commit comments