diff --git a/tools/FileUpdateTools.psm1 b/tools/FileUpdateTools.psm1 new file mode 100644 index 0000000000..f3d3f38c45 --- /dev/null +++ b/tools/FileUpdateTools.psm1 @@ -0,0 +1,230 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +#requires -Version 6.0 + +function ConvertToJToken +{ + param( + [Parameter()] + $Object + ) + + if ($null -eq $Object) + { + return [Newtonsoft.Json.Linq.JValue]::CreateNull() + } + + if ($Object -is [pscustomobject]) + { + $jObject = [Newtonsoft.Json.Linq.JObject]::new() + foreach ($field in $Object) + { + $jObject.Add($field, $Object.$field) + } + return $jObject + } + + if ($Object -is [version]) + { + return [Newtonsoft.Json.Linq.JToken]::new($Object.ToString()) + } + + return (,[Newtonsoft.Json.Linq.JToken]::FromObject($Object)) +} + +function New-StringWithSegment +{ + [OutputType([string])] + param( + [Parameter(Mandatory)] + [string] + $String, + + [Parameter(Mandatory)] + [string] + $NewSegment, + + [Parameter(Mandatory)] + [int] + $StartIndex, + + [Parameter()] + [int] + $EndIndex = $StartIndex, + + [switch] + $AutoIndent + ) + + if ($AutoIndent) + { + $indentBuilder = [System.Text.StringBuilder]::new() + $indentIdx = $StartIndex - 1 + $currChar = $String[$indentIdx] + while ($currChar -ne "`n") + { + $null = $indentBuilder.Append($currChar) + $indentIdx-- + $currChar = $String[$indentIdx] + } + $indent = $indentBuilder.ToString() + } + else + { + $indent = '' + } + + $newStringBuilder = [System.Text.StringBuilder]::new() + $null = $newStringBuilder.Append($String.Substring(0, $StartIndex)) + + $segmentLines = $NewSegment.Split("`n") + + $null = $newStringBuilder.Append($segmentLines[0]) + for ($i = 1; $i -lt $segmentLines.Length; $i++) + { + $null = $newStringBuilder.Append("`n").Append($indent).Append($segmentLines[$i]) + } + + $null = $newStringBuilder.Append($String.Substring($EndIndex)) + + return $newStringBuilder.ToString() +} + +function Get-StringOffsetFromSpan +{ + [OutputType([int])] + param( + [Parameter()] + [string] + $String, + + [Parameter()] + [int] + $EndLine, + + [Parameter()] + [int] + $StartLine = 1, + + [Parameter()] + [int] + $Column = 0, + + [Parameter()] + [int] + $InitialOffset = 0 + ) + + $lfChar = 0xA + + $idx = $InitialOffset + $spanLines = $EndLine - $StartLine + for ($i = 0; $i -lt $spanLines; $i++) + { + $idx = $String.IndexOf($lfChar, $idx + 1) + + if ($idx -lt 0) + { + return $idx + } + } + + return $idx + $Column +} + +function ConvertTo-IndentedJson +{ + param( + [Parameter(Position=0)] + $Object, + + [Parameter()] + [int] + $IndentWidth = 4, + + [Parameter()] + [char] + $IndentChar = ' ' + ) + + # Convert the object to a JToken + $jObject = ConvertToJToken $Object + + # Reformat the entry with tab-based indentation, like the existing file + $stringBuilder = [System.Text.StringBuilder]::new() + try + { + $stringWriter = [System.IO.StringWriter]::new($stringBuilder) + $jsonWriter = [Newtonsoft.Json.JsonTextWriter]::new($stringWriter) + $jsonWriter.Indentation = $IndentWidth + $jsonWriter.IndentChar = $IndentChar + $jsonWriter.Formatting = 'Indented' + $null = $jObject.WriteTo($jsonWriter) + } + finally + { + $jsonWriter.Dispose() + $stringWriter.Dispose() + } + return $stringBuilder.ToString().Replace([System.Environment]::NewLine, "`r`n") +} + +function Get-IncrementedVersion +{ + param( + [Parameter(Mandatory)] + [semver] + $Version, + + [Parameter(Mandatory)] + [ValidateSet('Major', 'Minor', 'Patch', 'Preview')] + [string] + $IncrementLevel + ) + + switch ($IncrementLevel) + { + 'Major' + { + return [semver]::new($version.Major+1, $version.Minor, $version.Patch, $version.PreReleaseLabel) + } + + 'Minor' + { + return [semver]::new($version.Major, $version.Minor+1, $version.Patch, $version.PreReleaseLabel) + } + + 'Patch' + { + return [semver]::new($version.Major, $version.Minor, $version.Patch+1, $version.PreReleaseLabel) + } + + 'Preview' + { + $newPreviewNumber = [int]$version.PreReleaseLabel.Substring(8) + 1 + return [semver]::new($version.Major, $version.Minor, $version.Patch, "preview.$newPreviewNumber") + } + } +} + +function Get-VersionFromSemVer +{ + [OutputType([version])] + param( + [Parameter(Mandatory)] + [semver] + $SemVer + ) + + $svStr = $SemVer.ToString() + + if (-not $SemVer.PreReleaseLabel) + { + return [version]$svStr + } + + return $svStr.Substring(0, $svStr.IndexOf('-')) +} + +Export-ModuleMember -Function New-StringWithSegment,Get-StringOffsetFromSpan,ConvertTo-IndentedJson,Get-IncrementedVersion,Get-VersionFromSemVer diff --git a/tools/GitHubTools.psm1 b/tools/GitHubTools.psm1 new file mode 100644 index 0000000000..bc5ad6b3fb --- /dev/null +++ b/tools/GitHubTools.psm1 @@ -0,0 +1,232 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +function GetHumanishRepositoryName +{ + param( + [string] + $Repository + ) + + if ($Repository.EndsWith('.git')) + { + $Repository = $Repository.Substring(0, $Repository.Length - 4) + } + else + { + $Repository = $Repository.Trim('/') + } + + return $Repository.Substring($Repository.LastIndexOf('/') + 1) +} + +function Exec +{ + param([scriptblock]$Invocation) + + & $Invocation + + if ($LASTEXITCODE -ne 0) + { + # Get caller location for easier debugging + $caller = Get-PSCallStack -ErrorAction SilentlyContinue + if($caller) + { + $callerLocationParts = $caller[1].Location -split ":\s*line\s*" + $callerFile = $callerLocationParts[0] + $callerLine = $callerLocationParts[1] + + $errorMessage = "Execution of {$Invocation} by ${callerFile}: line $callerLine failed with exit code $LASTEXITCODE" + throw $errorMessage + } + throw "Execution of {$Invocation} failed with exit code $LASTEXITCODE" + } +} + +function Copy-GitRepository +{ + param( + [Parameter(Mandatory)] + [string] + $OriginRemote, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] + $Destination = (GetHumanishRepositoryName $OriginRemote), + + [Parameter()] + [string] + $CloneBranch = 'master', + + [Parameter()] + [string] + $CheckoutBranch, + + [Parameter()] + [hashtable] + $Remotes, + + [switch] + $Clobber + ) + + $Destination = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Destination) + + if (Test-Path $Destination) + { + if (-not $Clobber) + { + throw "Cannot clone repo to '$Destination'; path already exists." + } + + Remove-Item -Force -Recurse $Destination -ErrorAction Stop + } + + $containingDir = Split-Path $Destination + if (-not (Test-Path $containingDir)) + { + New-Item -Path $containingDir -ItemType Directory -ErrorAction Stop + } + + Exec { git clone --single-branch --branch $CloneBranch $OriginRemote $Destination } + + Push-Location $Destination + try + { + Exec { git config core.autocrlf true } + + foreach ($remote in $Remotes.get_Keys()) + { + Exec { git remote add $remote $Remotes[$remote] } + } + + if ($remote['upstream']) + { + Exec { git pull upstream $CloneBranch } + } + + if ($CheckoutBranch) + { + Exec { git checkout -b $CheckoutBranch } + } + } + finally + { + Pop-Location + } +} + +function Submit-GitChanges +{ + param( + [Parameter(Mandatory)] + [string] + $Message, + + [Parameter(Mandatory)] + [string] + $Branch, + + [Parameter(Mandatory)] + [string] + $RepositoryLocation, + + [Parameter()] + [string[]] + $File, + + [Parameter()] + [string] + $Remote = 'origin' + ) + + Push-Location $RepositoryLocation + try + { + # Try to checkout the relevant branch + try + { + Exec { git checkout $Branch } + } + catch + { + Exec { git checkout -b $Branch } + } + + if ($File) + { + Exec { git add $File } + } + else + { + Exec { git add -A } + } + Exec { git commit -m $Message } + Exec { git push $Remote $Branch } + } + finally + { + Pop-Location + } +} + +function New-GitHubPR +{ + param( + [Parameter(Mandatory)] + [string] + $Branch, + + [Parameter(Mandatory)] + [string] + $Title, + + [Parameter(Mandatory)] + [string] + $GitHubToken, + + [Parameter(Mandatory)] + [string] + $Organization, + + [Parameter(Mandatory)] + $Repository, + + [Parameter()] + [string] + $TargetBranch = 'master', + + [Parameter()] + [string] + $Description = '', + + [Parameter()] + [string] + $FromOrg + ) + + $uri = "https://api.github.com/repos/$Organization/$Repository/pulls" + + if ($FromOrg -and $FromOrg -ne $Organization) + { + $Branch = "${FromOrg}:${Branch}" + } + + $body = @{ + title = $Title + body = $Description + head = $Branch + base = $TargetBranch + maintainer_can_modify = $true + } | ConvertTo-Json + + $headers = @{ + Accept = 'application/vnd.github.v3+json' + Authorization = "token $GitHubToken" + } + + Invoke-RestMethod -Method Post -Uri $uri -Body $body -Headers $headers +} + +Export-ModuleMember -Function Copy-GitRepository,Submit-GitChanges,New-GitHubPR diff --git a/tools/postReleaseScripts/updateAzureDataStudio.ps1 b/tools/postReleaseScripts/updateAzureDataStudio.ps1 new file mode 100644 index 0000000000..55ff86794c --- /dev/null +++ b/tools/postReleaseScripts/updateAzureDataStudio.ps1 @@ -0,0 +1,266 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +#requires -Version 6.0 + +param( + [Parameter(Mandatory)] + [string] + $GitHubToken, + + [Parameter(Mandatory)] + [version] + $ExtensionVersion, + + [Parameter()] + [string] + $GalleryFileName = 'extensionsGallery.json', + + [Parameter()] + [string] + $TargetFork = 'Microsoft', + + [Parameter()] + [string] + $BranchName = "update-psext-$ExtensionVersion", + + [Parameter()] + [string] + $PRDescription = "Updates the version of the PowerShell extension in ADS to $ExtensionVersion.`n**Note**: This is an automated PR." +) + +Import-Module "$PSScriptRoot/../GitHubTools.psm1" -Force +Import-Module "$PSScriptRoot/../FileUpdateTools.psm1" -Force + +function NewReleaseVersionEntry +{ + param( + [Parameter()] + [version] + $Version, + + [Parameter()] + [datetime] + $UpdateDate = [datetime]::Now.Date + ) + + return [ordered]@{ + version = "$Version" + lastUpdated = $UpdateDate.ToString('M/dd/yyyy') + assetUri = '' + fallbackAssetUri = 'fallbackAssetUri' + files = @( + [ordered]@{ + assetType = 'Microsoft.VisualStudio.Services.VSIXPackage' + source = "https://sqlopsextensions.blob.core.windows.net/extensions/powershell/PowerShell-$Version.vsix" + } + [ordered]@{ + assetType = 'Microsoft.VisualStudio.Services.Icons.Default' + source = 'https://raw.githubusercontent.com/PowerShell/vscode-powershell/master/images/PowerShell_icon.png' + } + [ordered]@{ + assetType = 'Microsoft.VisualStudio.Services.Content.Details' + source = 'https://raw.githubusercontent.com/PowerShell/vscode-powershell/master/docs/azure_data_studio/README_FOR_MARKETPLACE.md' + } + [ordered]@{ + assetType = 'Microsoft.VisualStudio.Code.Manifest' + source = 'https://raw.githubusercontent.com/PowerShell/vscode-powershell/master/package.json' + } + [ordered]@{ + assetType = 'Microsoft.VisualStudio.Services.Content.License' + source = 'https://raw.githubusercontent.com/PowerShell/vscode-powershell/master/LICENSE.txt' + } + ) + properties = @( + [ordered]@{ + key = 'Microsoft.VisualStudio.Code.ExtensionDependencies' + value = '' + } + [ordered]@{ + key = 'Microsoft.VisualStudio.Code.Engine' + value = '>=0.32.1' + } + [ordered]@{ + key = 'Microsoft.VisualStudio.Services.Links.Source' + value = 'https://github.com/PowerShell/vscode-powershell/' + } + ) + } +} + +function NewPowerShellExtensionEntry +{ + param( + [Parameter()] + [version] + $ExtensionVersion + ) + + return [ordered]@{ + extensionId = '35' + extensionName = 'powershell' + displayName = 'PowerShell' + shortDescription = 'Develop PowerShell scripts in Azure Data Studio' + publisher = [ordered]@{ + displayName = 'Microsoft' + publisherId = 'Microsoft' + publisherName = 'Microsoft' + } + versions = @( + NewReleaseVersionEntry -Version $ExtensionVersion + ) + statistics = @() + flags = 'preview' + } + +} + +function FindPSExtensionJsonSpan +{ + param( + [Parameter()] + [string] + $GalleryExtensionFileContent + ) + + try + { + $reader = [System.IO.StringReader]::new($GalleryExtensionFileContent) + $jsonReader = [Newtonsoft.Json.JsonTextReader]::new($reader) + + $depth = 0 + $startLine = -1 + $startColumn = -1 + $startDepth = -1 + $awaitingExtensionName = $false + $foundPowerShell = $false + while ($jsonReader.Read()) + { + switch ($jsonReader.TokenType) + { + 'StartObject' + { + if (-not $foundPowerShell) + { + $startDepth = $depth + $startLine = $jsonReader.LineNumber + $startColumn = $jsonReader.LinePosition + } + $depth++ + continue + } + + 'EndObject' + { + if ($foundPowerShell -and $depth -eq $startDepth + 1) + { + return @{ + Start = @{ + Line = $startLine + Column = $startColumn + } + End = @{ + Line = $jsonReader.LineNumber + Column = $jsonReader.LinePosition + } + } + } + $depth-- + continue + } + + 'PropertyName' + { + if ($jsonReader.Value -eq 'extensionName') + { + $awaitingExtensionName = $true + } + continue + } + + 'String' + { + if (-not $awaitingExtensionName) + { + continue + } + + $awaitingExtensionName = $false + + if ($jsonReader.Value -eq 'PowerShell') + { + $foundPowerShell = $true + } + + continue + } + } + } + } + finally + { + $reader.Dispose() + $jsonReader.Dispose() + } + + throw 'Did not find PowerShell extension' +} + +function UpdateGalleryFile +{ + param( + [Parameter(Mandatory)] + [version] + $ExtensionVersion, + + [Parameter()] + [string] + $GalleryFilePath = './extensionsGallery-insider.json' + ) + + # Create a new PowerShell extension entry + $powershellEntry = NewPowerShellExtensionEntry -ExtensionVersion $ExtensionVersion + $entryStr = ConvertTo-IndentedJson $powershellEntry -IndentChar "`t" -IndentWidth 1 + + # Find the position in the existing file where the PowerShell extension should go + $galleryFileContent = Get-Content -Raw $GalleryFilePath + $span = FindPSExtensionJsonSpan -GalleryExtensionFileContent $galleryFileContent + $startOffset = Get-StringOffsetFromSpan -String $galleryFileContent -EndLine $span.Start.Line -Column $span.Start.Column + $endOffset = Get-StringOffsetFromSpan -String $galleryFileContent -EndLine $span.End.Line -StartLine $span.Start.Line -Column $span.End.Column -InitialOffset $startOffset + + # Create the new file contents with the inserted segment + $newGalleryFileContent = New-StringWithSegment -String $galleryFileContent -NewSegment $entryStr -StartIndex $startOffset -EndIndex ($endOffset+1) -AutoIndent + + # Write out the new entry + Set-Content -Path $GalleryFilePath -Value $newGalleryFileContent -Encoding utf8NoBOM -NoNewline +} + +$repoLocation = Join-Path ([System.IO.Path]::GetTempPath()) 'ads-temp-checkout' + +$cloneParams = @{ + OriginRemote = 'https://github.com/rjmholt/AzureDataStudio' + Destination = $repoLocation + CloneBranch = 'release/extensions' + CheckoutBranch = $branchName + Clobber = $true + Remotes = @{ + upstream = 'https://github.com/Microsoft/AzureDataStudio' + } +} +Copy-GitRepository @cloneParams + +UpdateGalleryFile -ExtensionVersion $ExtensionVersion -GalleryFilePath "$repoLocation/$GalleryFileName" + +Submit-GitChanges -RepositoryLocation $repoLocation -File $GalleryFileName -Branch $branchName -Message "Update PS extension to v$ExtensionVersion" + +$prParams = @{ + Organization = $TargetFork + Repository = 'AzureDataStudio' + TargetBranch = 'release/extensions' + Branch = $branchName + Title = "Update PowerShell extension to v$ExtensionVersion" + Description = $PRDescription + GitHubToken = $GitHubToken + FromOrg = 'rjmholt' +} +New-GitHubPR @prParams diff --git a/tools/postReleaseScripts/updateExtensionVersions.ps1 b/tools/postReleaseScripts/updateExtensionVersions.ps1 new file mode 100644 index 0000000000..974a6e43f7 --- /dev/null +++ b/tools/postReleaseScripts/updateExtensionVersions.ps1 @@ -0,0 +1,286 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +#requires -Version 6.0 + +[CmdletBinding(DefaultParameterSetName='Increment')] +param( + [Parameter(ParameterSetName='Increment')] + [ValidateSet('Major', 'Minor', 'Patch', 'Preview')] + [string] + $IncrementLevel = 'Preview', + + [Parameter(Mandatory, ParameterSetName='SetVersion')] + [semver] + $NewVersion, + + [Parameter(Mandatory)] + [string] + $GitHubToken, + + [Parameter()] + [string] + $TargetFork = 'PowerShell', + + [Parameter()] + [string] + # Default set below, requires processing when $IncrementVersion is used + $BranchName, + + [Parameter()] + [string] + # Default set below, requires processing when $IncrementVersion is used + $PRDescription +) + +Import-Module -Force "$PSScriptRoot/../FileUpdateTools.psm1" +Import-Module -Force "$PSScriptRoot/../GitHubTools.psm1" + +function FindPackageJsonVersionSpan +{ + param( + [Parameter(Mandatory)] + [string] + $PackageJsonContent + ) + + try + { + $reader = [System.IO.StringReader]::new($PackageJsonContent) + $jsonReader = [Newtonsoft.Json.JsonTextReader]::new($reader) + + $depth = 0 + $seenVersion = $false + $versionStartOffset = -1 + $versionStartColumn = -1 + while ($jsonReader.Read()) + { + switch ($jsonReader.TokenType) + { + 'StartObject' + { + $depth++ + continue + } + + 'EndObject' + { + $depth-- + continue + } + + 'PropertyName' + { + if ($depth -ne 1) + { + continue + } + + $seenVersion = $jsonReader.Value -eq 'version' + + if (-not $seenVersion) + { + continue + } + + $currIndex = Get-StringOffsetFromSpan -String $PackageJsonContent -EndLine $jsonReader.LineNumber -Column $jsonReader.LinePosition + $versionStartOffset = $PackageJsonContent.IndexOf('"', $currIndex) + 1 + $versionStartColumn = $jsonReader.LinePosition + $versionStartOffset - $currIndex + + continue + } + + 'String' + { + if (-not $seenVersion -or $depth -ne 1) + { + continue + } + + return @{ + Start = $versionStartOffset + End = $versionStartOffset + $jsonReader.LinePosition - $versionStartColumn + } + + continue + } + } + } + } + finally + { + $reader.Dispose() + $jsonReader.Dispose() + } + + throw 'Did not find package.json version field' +} + +function FindRequiredPsesVersionSpan +{ + param( + [Parameter(Mandatory)] + [string] + $MainTsContent + ) + + $pattern = [regex]'const\s+requiredEditorServicesVersion\s+=\s+"(.*)"' + $versionGroup = $pattern.Match($MainTsContent).Groups[1] + + return @{ + Start = $versionGroup.Index + End = $versionGroup.Index + $versionGroup.Length + } +} + +function FindVstsBuildVersionSpan +{ + param( + [Parameter(Mandatory)] + [string] + $DockerFileContent + ) + + $pattern = [regex]'ENV VSTS_BUILD_VERSION=(.*)' + $versionGroup = $pattern.Match($DockerFileContent).Groups[1] + + return @{ + Start = $versionGroup.Index + End = $versionGroup.Index + $versionGroup.Length + } +} + +function UpdateMainTsPsesVersion +{ + param( + [Parameter(Mandatory)] + [string] + $MainTsPath, + + [Parameter(Mandatory)] + [version] + $Version + ) + + $mainTsContent = Get-Content -Raw $MainTsPath + $mainTsVersionSpan = FindRequiredPsesVersionSpan $mainTsContent + $newMainTsContent = New-StringWithSegment -String $mainTsContent -NewSegment $Version -StartIndex $mainTsVersionSpan.Start -EndIndex $mainTsVersionSpan.End + if ($newMainTsContent -ne $mainTsContent) + { + Set-Content -Path $MainTsPath -Value $newMainTsContent -Encoding utf8NoBOM -NoNewline + } +} + +function UpdateDockerFileVersion +{ + param( + [Parameter(Mandatory)] + [string] + $DockerFilePath, + + [Parameter(Mandatory)] + [version] + $Version + ) + + $vstsDockerFileContent = Get-Content -Raw $DockerFilePath + $vstsDockerFileVersionSpan = FindVstsBuildVersionSpan -DockerFileContent $vstsDockerFileContent + $newDockerFileContent = New-StringWithSegment -String $vstsDockerFileContent -NewSegment $Version -StartIndex $vstsDockerFileVersionSpan.Start -EndIndex $vstsDockerFileVersionSpan.End + Set-Content -Path $DockerFilePath -Value $newDockerFileContent -Encoding utf8NoBOM -NoNewline +} + +function GetMarketplaceVersionFromSemVer +{ + [OutputType([version])] + param( + [Parameter(Mandatory)] + [semver] + $SemVer + ) + + if (-not $SemVer.PreReleaseLabel) + { + return [version]($SemVer.ToString()) + } + + return [version]::new($NewVersion.Major, $NewVersion.Minor, $NewVersion.PreReleaseLabel.Substring(8)-1) +} + +# Define locations/branch name +$repoLocation = Join-Path ([System.IO.Path]::GetTempPath()) 'vscps-updateversion-temp' +$paths = @{ + packageJson = "$repoLocation/package.json" + mainTs = "$repoLocation/src/main.ts" + vstsDockerFile = "$repoLocation/tools/releaseBuild/Image/DockerFile" +} + +# Clone the repo +$cloneParams = @{ + OriginRemote = 'https://github.com/rjmholt/vscode-powershell' + Destination = $repoLocation + Clobber = $true + Remotes = @{ + upstream = 'https://github.com/PowerShell/vscode-powershell' + } +} +Copy-GitRepository @cloneParams + +# We may need the version from the package.json, so get that first +$packageJson = Get-Content -Raw $paths.packageJson +$pkgJsonVersionOffsetSpan = FindPackageJsonVersionSpan -PackageJsonContent $packageJson + +# If the option was to increment, we take the existing version and increment it +if ($IncrementLevel) +{ + $version = [semver]$packageJson.Substring($pkgJsonVersionOffsetSpan.Start, $pkgJsonVersionOffsetSpan.End - $pkgJsonVersionOffsetSpan.Start) + $NewVersion = Get-IncrementedVersion -Version $version -IncrementLevel $IncrementLevel +} + +if (-not $BranchName) +{ + $BranchName = "update-version-$NewVersion" +} + +if (-not $PRDescription) +{ + $PRDescription = "Updates version strings in vscode-PowerShell to $NewVersion.`n**Note**: This is an automated PR." +} + +# Get the marketplace/non-semver versions for various files +$psesVersion = Get-VersionFromSemVer -SemVer $NewVersion +$marketPlaceVersion = GetMarketplaceVersionFromSemVer -SemVer $NewVersion + +# Finally create the new package.json file +$newPkgJsonContent = New-StringWithSegment -String $packageJson -NewSegment $NewVersion -StartIndex $pkgJsonVersionOffsetSpan.Start -EndIndex $pkgJsonVersionOffsetSpan.End +Set-Content -Path $paths.packageJson -Value $newPkgJsonContent -Encoding utf8NoBOM -NoNewline + +# Create the new content for the main.ts required version file +UpdateMainTsPsesVersion -MainTsPath $paths.mainTs -Version $psesVersion + +# Create the new content for the VSTS dockerfile +UpdateDockerFileVersion -DockerFilePath $paths.vstsDockerFile -Version $marketPlaceVersion + +# Commit and push the changes +$commitParams = @{ + Message = "[Ignore] Increment version to $NewVersion" + Branch = $branchName + RepositoryLocation = $repoLocation + File = @( + 'package.json' + 'src/main.ts' + 'tools/releaseBuild/Image/DockerFile' + ) +} +Submit-GitChanges @commitParams + +# Open a new PR in GitHub +$prParams = @{ + Organization = $TargetFork + Repository = 'vscode-PowerShell' + Branch = $branchName + Title = "Update version to v$NewVersion" + Description = $PRDescription + GitHubToken = $GitHubToken + FromOrg = 'rjmholt' +} +New-GitHubPR @prParams diff --git a/tools/postReleaseScripts/updatePsesVersions.ps1 b/tools/postReleaseScripts/updatePsesVersions.ps1 new file mode 100644 index 0000000000..c4222d1d71 --- /dev/null +++ b/tools/postReleaseScripts/updatePsesVersions.ps1 @@ -0,0 +1,220 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +#requires -Version 6.0 + +[CmdletBinding(DefaultParameterSetName='Increment')] +param( + [Parameter(ParameterSetName='Increment')] + [ValidateSet('Major', 'Minor', 'Patch', 'Preview')] + [string] + $IncrementLevel = 'Preview', + + [Parameter(Mandatory, ParameterSetName='SetVersion')] + [semver] + $NewVersion, + + [Parameter(Mandatory)] + [string] + $GitHubToken, + + [Parameter()] + [string] + $TargetFork = 'PowerShell', + + [Parameter()] + [string] + $BranchName, + + [Parameter()] + [string] + $PRDescription +) + +Import-Module "$PSScriptRoot/../GitHubTools.psm1" -Force +Import-Module "$PSScriptRoot/../FileUpdateTools.psm1" -Force + +function FindPsesModuleSpan +{ + param( + [Parameter()] + [string] + $ModuleManifestContent + ) + + # Inscrutable regex looks for PSD1 "ModuleVersion = '2.0.0'" type of field + $pattern = [regex]'\s*ModuleVersion\s*=\s*(?:''|")(\d+?(?:\.\d+?(?:\.\d+)))(?:''|")' + + $versionGroup = $pattern.Match($ModuleManifestContent).Groups[1] + + return @{ + Start = $versionGroup.Index + End = $versionGroup.Index + $versionGroup.Length + } +} + +function UpdatePsesModuleVersion +{ + param( + [Parameter()] + [string] + $PsesModuleManifestPath, + + [Parameter()] + [semver] + $NewVersion + ) + + $version = Get-VersionFromSemVer -SemVer $NewVersion + + $PsesModuleManifestPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($PsesModuleManifestPath) + + $manifestContent = Get-Content -Raw $PsesModuleManifestPath + + $span = FindPsesModuleSpan -ModuleManifestContent $manifestContent + + $newContent = New-StringWithSegment -String $manifestContent -NewSegment $version -StartIndex $span.Start -EndIndex $span.End + + Set-Content -Path $PsesModuleManifestPath -Value $newContent -Encoding utf8NoBOM -NoNewline +} + +function GetPsesCurrentVersion +{ + [OutputType([semver])] + param( + [Parameter()] + [string] + $PsesPropsPath + ) + + $propsXml = [xml](Get-Content -Raw $PsesPropsPath) + + $version = $propsXml.Project.PropertyGroup.VersionPrefix + $prereleaseTag = $propsXml.Project.PropertyGroup.VersionSuffix + if ($prereleaseTag) + { + $version = "$version-$prereleaseTag" + } + + return [semver]$version +} + +function UpdatePsesPropsXml +{ + param( + [Parameter(Mandatory)] + [semver] + $NewVersion, + + [Parameter(Mandatory)] + [string] + $PsesPropsPath + ) + + $PsesPropsPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($PsesPropsPath) + + $propsXml = [xml](Get-Content -Raw $PsesPropsPath) + + $versionParts = $NewVersion.ToString().Split('-') + + if ($versionParts.Length -eq 2) + { + $propsXml.Project.PropertyGroup.VersionPrefix = $versionParts[0] + $propsXml.Project.PropertyGroup.VersionSuffix = $versionParts[1] + } + else + { + $propsXml.Project.PropertyGroup.VersionPrefix = $versionParts[0] + + # Remove the prerelease tag if it's present + $prereleaseNode = $propsXml.Project.PropertyGroup.GetElementsByTagName('VersionSuffix') + if ($prereleaseNode) + { + $null = $propsXml.Project.PropertyGroup.RemoveChild($prereleaseNode[0]) + } + } + + $xmlWriterSettings = [System.Xml.XmlWriterSettings]@{ + Encoding = [System.Text.UTF8Encoding]::new(<# BOM #>$false) + OmitXmlDeclaration = $true + Indent = $true + IndentChars = " " + NewLineHandling = 'Replace' + NewLineChars = "`r`n" + } + $xmlWriter = [System.Xml.XmlWriter]::Create($PsesPropsPath, $xmlWriterSettings) + try + { + $propsXml.Save($xmlWriter) + $xmlWriter.WriteWhitespace("`r`n") + } + finally + { + $xmlWriter.Dispose() + } +} + +$repoLocation = Join-Path ([System.IO.Path]::GetTempPath()) 'pses-update-temp' +$paths = @{ + props = "$repoLocation/PowerShellEditorServices.Common.props" + manifest = "$repoLocation/module/PowerShellEditorServices/PowerShellEditorServices.psd1" +} + +# Clone the PSES repo +$cloneParams = @{ + OriginRemote = 'https://github.com/rjmholt/PowerShellEditorServices' + Destination = $repoLocation + CheckoutBranch = $BranchName + Remotes = @{ + upstream = 'https://github.com/PowerShell/PowerShellEditorServices' + } + Clobber = $true +} +Copy-GitRepository @cloneParams + +# If we need to increment the version, do that +if ($IncrementLevel) +{ + $currVersion = GetPsesCurrentVersion -PsesPropsPath $paths.props + $NewVersion = Get-IncrementedVersion -Version $currVersion -IncrementLevel $IncrementLevel +} + +if (-not $BranchName) +{ + $BranchName = "update-pses-version-$NewVersion" +} + +if (-not $PRDescription) +{ + $PRDescription = "Updates PSES to version $NewVersion.**Note**: This is an automated PR." +} + +# Update the Props XML file +UpdatePsesPropsXml -NewVersion $NewVersion -PsesPropsPath $paths.props + +# Update the PSD1 file +UpdatePsesModuleVersion -PsesModuleManifestPath $paths.manifest -NewVersion $NewVersion + +# Commit changes +$commitParams = @{ + RepositoryLocation = $repoLocation + Message = "[Ignore] Update PSES version to $NewVersion" + Branch = $BranchName + File = @( + 'PowerShellEditorServices.Common.props' + 'module/PowerShellEditorServices/PowerShellEditorServices.psd1' + ) +} +Submit-GitChanges @commitParams + +# Open a PR +$prParams = @{ + Branch = $BranchName + Title = "Update PowerShellEditorServices version to $NewVersion" + GitHubToken = $GitHubToken + Organization = $TargetFork + Repository = 'PowerShellEditorServices' + Description = $PRDescription + FromOrg = 'rjmholt' +} +New-GitHubPR @prParams