diff --git a/.vsts-ci/templates/ci-general.yml b/.vsts-ci/templates/ci-general.yml index 93018ec5be..9498d7529b 100644 --- a/.vsts-ci/templates/ci-general.yml +++ b/.vsts-ci/templates/ci-general.yml @@ -7,13 +7,6 @@ parameters: default: false steps: -- task: PowerShell@2 - displayName: PowerShell version - inputs: - targetType: inline - script: $PSVersionTable - pwsh: ${{ parameters.pwsh }} - - checkout: self # NOTE: We either checkout the Git repo for PowerShellEditorServices, or we @@ -62,19 +55,32 @@ steps: version: 6.0.x performMultiLevelLookup: true +# The build script is always run with PowerShell Core - task: PowerShell@2 - displayName: Build and test + displayName: Build and package inputs: targetType: inline script: | - Get-ChildItem env: - Get-Module -ListAvailable Pester Install-Module InvokeBuild -Scope CurrentUser -Force Install-Module platyPS -Scope CurrentUser -Force - Invoke-Build -Configuration Release + Invoke-Build -Configuration Release Package $PackageJson = Get-Content -Raw package.json | ConvertFrom-Json Write-Host "##vso[task.setvariable variable=vsixPath]$(Resolve-Path powershell-$($PackageJson.version).vsix)" workingDirectory: $(Build.SourcesDirectory)/vscode-powershell + pwsh: true + +# Tests in particular are run with either PowerShell Core or Windows PowerShell +- task: PowerShell@2 + displayName: Run unit tests + inputs: + targetType: inline + script: | + $PSVersionTable + Get-ChildItem env: + Get-Module -ListAvailable Pester + Install-Module InvokeBuild -Scope CurrentUser -Force + Invoke-Build -Configuration Release Test + workingDirectory: $(Build.SourcesDirectory)/vscode-powershell pwsh: ${{ parameters.pwsh }} - task: PowerShell@2 @@ -87,7 +93,7 @@ steps: Write-Host '##vso[task.LogIssue type=error;]PowerShell Editor Services bits were not built in release configuration!' exit 1 } - pwsh: ${{ parameters.pwsh }} + pwsh: true - publish: $(vsixPath) artifact: vscode-powershell-vsix-$(System.JobId) diff --git a/.vsts-ci/templates/publish-markets.yml b/.vsts-ci/templates/publish-markets.yml index 538bacdb35..41a79e68ca 100644 --- a/.vsts-ci/templates/publish-markets.yml +++ b/.vsts-ci/templates/publish-markets.yml @@ -7,11 +7,13 @@ steps: - pwsh: | npm ci --loglevel=error - $PackageJson = Get-Content -Raw $(Build.SourcesDirectory)/package.json | ConvertFrom-Json + Import-Module $(Build.SourcesDirectory)/tools/VersionTools.psm1 + $Version = Get-Version -RepositoryName vscode-powershell + $PackageVersion = Get-MajorMinorPatch -Version $Version $PublishArgs = @( - if ($PackageJson.preview) { '--pre-release' } + if (Test-IsPreRelease) { '--pre-release' } '--packagePath' - "$(Pipeline.Workspace)/vscode-powershell/powershell-$($PackageJson.version).vsix" + "$(Pipeline.Workspace)/vscode-powershell/powershell-$PackageVersion.vsix" '--pat' '$(VsceToken)' ) diff --git a/CHANGELOG.md b/CHANGELOG.md index 431dfcfab6..aa845eda88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -854,7 +854,7 @@ major update. These major updates have also been tested over the last 6 months, in 13 releases of our [PowerShell Preview extension for Visual Studio -Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell-preview). A +Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode.powershell). A huge thank you to all of the community members who have tested these changes to the extension and have worked with us to polish the extension before releasing it through our stable channel. @@ -1079,7 +1079,7 @@ are updating the stable extension to bring some bug fixes forward. Please try ou [PowerShell Preview extension][] for the latest and hopefully greatest experience, and help us squash those bugs! -[PowerShell Preview extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell-Preview +[PowerShell Preview extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.powershell #### [PowerShellEditorServices](https://github.com/PowerShell/PowerShellEditorServices) diff --git a/extension-dev.code-workspace b/extension-dev.code-workspace index 4d28833762..dc9f8bfeab 100644 --- a/extension-dev.code-workspace +++ b/extension-dev.code-workspace @@ -17,7 +17,7 @@ "josefpihrt-vscode.roslynator", "ms-azure-devops.azure-pipelines", "ms-dotnettools.csharp", - "ms-vscode.powershell-preview" + "ms-vscode.powershell" ] }, "settings": { diff --git a/package.json b/package.json index 0ccc189041..a3137a7546 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "powershell", "displayName": "PowerShell", "version": "2023.3.1", - "preview": true, + "preview": false, "publisher": "ms-vscode", "description": "Develop PowerShell modules, commands and scripts in Visual Studio Code!", "engines": { @@ -23,7 +23,7 @@ "PowerShell", "pwsh" ], - "icon": "media/PowerShell_Preview_Icon.png", + "icon": "media/PowerShell_Icon.png", "galleryBanner": { "color": "#ACD1EC", "theme": "light" diff --git a/src/features/ExternalApi.ts b/src/features/ExternalApi.ts index 63332bad70..c054eb1ba6 100644 --- a/src/features/ExternalApi.ts +++ b/src/features/ExternalApi.ts @@ -72,9 +72,8 @@ export class ExternalApiFeature extends LanguageClientConsumer implements IPower throw new Error(`No extension installed with id '${id}'. You must use a valid extension id.`); } - // These are only allowed to be used in our unit tests. - if ((id === "ms-vscode.powershell" || id === "ms-vscode.powershell-preview") - && !(this.extensionContext.extensionMode === vscode.ExtensionMode.Test)) { + // Our ID is only only allowed to be used in our unit tests. + if (id === "ms-vscode.powershell" && !(this.extensionContext.extensionMode === vscode.ExtensionMode.Test)) { throw new Error("You can't use the PowerShell extension's id in this registration."); } diff --git a/src/main.ts b/src/main.ts index 99a998a3ce..1b0be4c067 100644 --- a/src/main.ts +++ b/src/main.ts @@ -56,14 +56,6 @@ export async function activate(context: vscode.ExtensionContext): Promise -function Use-Repository { - [CmdletBinding()] +function Update-Branch { + [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [ValidateSet([RepoNames])] - [string]$RepositoryName, - - [Parameter(Mandatory)] - [scriptblock]$Script + [string]$RepositoryName ) - try { - switch ($RepositoryName) { - "vscode-powershell" { - Push-Location -Path "$PSScriptRoot/../" - } - "PowerShellEditorServices" { - Push-Location -Path "$PSScriptRoot/../../PowerShellEditorServices" + Use-Repository -RepositoryName $RepositoryName -Script { + $Branch = git branch --show-current + if ($Branch -ne "release") { + if ($PSCmdlet.ShouldProcess("release", "git checkout -B")) { + git checkout -B "release" } } - & $Script - } finally { - Pop-Location } } @@ -143,72 +129,6 @@ function Get-Bullets { } } -<# -.SYNOPSIS - Gets the unpublished content from the changelog. -.DESCRIPTION - This is used so that we can manually touch-up the automatically updated - changelog, and then bring its contents into the extension's changelog or - the GitHub release. It just gets the first header's contents. -#> -function Get-FirstChangelog { - param( - [Parameter(Mandatory)] - [ValidateSet([RepoNames])] - [string]$RepositoryName - ) - $Changelog = Use-Repository -RepositoryName $RepositoryName -Script { - Get-Content -Path $ChangelogFile - } - # NOTE: The space after the header marker is important! Otherwise ### matches. - $Header = $Changelog.Where({$_.StartsWith("## ")}, "First") - $Changelog.Where( - { $_ -eq $Header }, "SkipUntil" - ).Where( - { $_.StartsWith("## ") -and $_ -ne $Header }, "Until" - ) -} - -<# -.SYNOPSIS - Creates and checks out `release` if not already on it. -#> -function Update-Branch { - [CmdletBinding(SupportsShouldProcess)] - param( - [Parameter(Mandatory)] - [ValidateSet([RepoNames])] - [string]$RepositoryName - ) - Use-Repository -RepositoryName $RepositoryName -Script { - $Branch = git branch --show-current - if ($Branch -ne "release") { - if ($PSCmdlet.ShouldProcess("release", "git checkout -B")) { - git checkout -B "release" - } - } - } -} - -<# -.SYNOPSIS - Gets current version from changelog as `[semver]`. -#> -function Get-Version { - param( - [Parameter(Mandatory)] - [ValidateSet([RepoNames])] - [string]$RepositoryName - ) - # NOTE: The first line should always be the header. - $Changelog = (Get-FirstChangelog -RepositoryName $RepositoryName)[0] - if ($Changelog -match '## v(?\d+\.\d+\.\d+(-preview\.?\d*)?)') { - return [semver]$Matches.version - } else { - Write-Error "Couldn't find version from changelog!" - } -} - <# .SYNOPSIS Updates the CHANGELOG file with PRs merged since the last release. @@ -224,12 +144,13 @@ function Update-Changelog { [ValidateSet([RepoNames])] [string]$RepositoryName, - # TODO: Validate version style for each repo. [Parameter(Mandatory)] - [ValidateScript({ $_.StartsWith("v") })] [string]$Version ) + # Since we depend on both parameters, we can't do this with `ValidateScript`. + Test-VersionIsValid -RepositoryName $RepositoryName -Version $Version + # Get the repo object, latest release, and commits since its tag. $Repo = Get-GitHubRepository -OwnerName PowerShell -RepositoryName $RepositoryName $Commits = Use-Repository -RepositoryName $RepositoryName -Script { @@ -300,8 +221,8 @@ function Update-Changelog { - package.json: - `version` field with `"X.Y.Z"` and no prefix or suffix - - `preview` field set to `true` or `false` if version is a preview - - `icon` field has `_Preview ` inserted if preview + - `preview` field is always `false` because now we do "pre-releases" + - TODO: `icon` field has `_Preview ` inserted if preview #> function Update-Version { [CmdletBinding(SupportsShouldProcess)] @@ -311,28 +232,26 @@ function Update-Version { [string]$RepositoryName ) $Version = Get-Version -RepositoryName $RepositoryName - $v = "$($Version.Major).$($Version.Minor).$($Version.Patch)" + $v = Get-MajorMinorPatch -Version $Version Update-Branch -RepositoryName $RepositoryName Use-Repository -RepositoryName $RepositoryName -Script { switch ($RepositoryName) { "vscode-powershell" { - if ($Version.PreReleaseLabel) { - $preview = "true" - $icon = "media/PowerShell_Preview_Icon.png" - } else { - $preview = "false" - $icon = "media/PowerShell_Icon.png" - } + # TODO: Bring this back when the marketplace supports it. + # if ($Version.PreReleaseLabel) { + # $icon = "media/PowerShell_Preview_Icon.png" + # } else { + # $icon = "media/PowerShell_Icon.png" + # } $path = "package.json" $f = Get-Content -Path $path # NOTE: The prefix regex match two spaces exactly to avoid matching # nested objects in the file. $f = $f -replace '^(? "version":\s+")(.+)(?",)$', "`${prefix}${v}`${suffix}" - $f = $f -replace '^(? "preview":\s+)(.+)(?,)$', "`${prefix}${preview}`${suffix}" - $f = $f -replace '^(? "icon":\s+")(.+)(?",)$', "`${prefix}${icon}`${suffix}" + # TODO: $f = $f -replace '^(? "icon":\s+")(.+)(?",)$', "`${prefix}${icon}`${suffix}" $f | Set-Content -Path $path git add $path } diff --git a/tools/VersionTools.psm1 b/tools/VersionTools.psm1 new file mode 100644 index 0000000000..1fae38518c --- /dev/null +++ b/tools/VersionTools.psm1 @@ -0,0 +1,153 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +#requires -Version 7.0 + +using namespace System.Management.Automation + +class RepoNames : IValidateSetValuesGenerator { + # NOTE: This is super over-engineered, but it was fun. + static [string[]] $Values = "vscode-powershell", "PowerShellEditorServices" + [String[]] GetValidValues() { return [RepoNames]::Values } +} + +$ChangelogFile = "CHANGELOG.md" + +<# +.SYNOPSIS + Given the repository name, execute the script in its directory. +#> +function Use-Repository { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateSet([RepoNames])] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [scriptblock]$Script + ) + try { + switch ($RepositoryName) { + "vscode-powershell" { + Push-Location -Path "$PSScriptRoot/../" + } + "PowerShellEditorServices" { + Push-Location -Path "$PSScriptRoot/../../PowerShellEditorServices" + } + } + & $Script + } finally { + Pop-Location + } +} + +<# +.SYNOPSIS + Gets the unpublished content from the changelog. +.DESCRIPTION + This is used so that we can manually touch-up the automatically updated + changelog, and then bring its contents into the extension's changelog or + the GitHub release. It just gets the first header's contents. +#> +function Get-FirstChangelog { + param( + [Parameter(Mandatory)] + [ValidateSet([RepoNames])] + [string]$RepositoryName + ) + $Changelog = Use-Repository -RepositoryName $RepositoryName -Script { + Get-Content -Path $ChangelogFile + } + # NOTE: The space after the header marker is important! Otherwise ### matches. + $Header = $Changelog.Where({$_.StartsWith("## ")}, "First") + $Changelog.Where( + { $_ -eq $Header }, "SkipUntil" + ).Where( + { $_.StartsWith("## ") -and $_ -ne $Header }, "Until" + ) +} + +<# +.SYNOPSIS + Gets current version from changelog as `[semver]`. +#> +function Get-Version { + param( + [Parameter(Mandatory)] + [ValidateSet([RepoNames])] + [string]$RepositoryName + ) + # NOTE: The first line should always be the header. + $Changelog = (Get-FirstChangelog -RepositoryName $RepositoryName)[0] + if ($Changelog -match '## v(?\d+\.\d+\.\d+(-preview\.?\d*)?)') { + return [semver]$Matches.version + } else { + Write-Error "Couldn't find version from changelog!" + } +} + +<# +.SYNOPSIS + Gets the version as a semantic version string without the 'v' prefix or + pre-release suffix. +#> +function Get-MajorMinorPatch { + param( + [Parameter(Mandatory)] + [semver]$Version + ) + return "$($Version.Major).$($Version.Minor).$($Version.Patch)" +} + +<# +.SYNOPSIS + Tests if this is a pre-release (specifically for the extension). +#> +function Test-IsPreRelease { + $Version = Get-Version -RepositoryName vscode-powershell + return [bool]$Version.PreReleaseLabel +} + +<# +.SYNOPSIS + Validates the given version string. +#> +function Test-VersionIsValid { + param( + [Parameter(Mandatory)] + [ValidateSet([RepoNames])] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [string]$Version + ) + if (!$Version.StartsWith("v")) { + throw "Version should start with 'v' prefix!" + } + + $SemanticVersion = [semver]$Version.Substring(1) + switch ($RepositoryName) { + "vscode-powershell" { + $Date = Get-Date + if ($SemanticVersion.Major -ne $Date.Year) { + throw "Major version should be the current year!" + } + if ($SemanticVersion.Minor -ne $Date.Month) { + throw "Minor version should be the current month!" + } + if ($SemanticVersion.PreReleaseLabel) { + if ($SemanticVersion.PreReleaseLabel -ne "preview") { + throw "Suffix should only be 'preview'!" + } + } + } + "PowerShellEditorServices" { + if ($SemanticVersion.PreReleaseLabel) { + if ($SemanticVersion.PreReleaseLabel -ne "preview") { + throw "Suffix should only be 'preview'!" + } + } + } + } +} diff --git a/vscode-powershell.build.ps1 b/vscode-powershell.build.ps1 index f31f990e8f..9acf2fbc18 100644 --- a/vscode-powershell.build.ps1 +++ b/vscode-powershell.build.ps1 @@ -7,11 +7,7 @@ param( [string]$EditorServicesRepoPath = $null ) -#Requires -Modules @{ModuleName="InvokeBuild";ModuleVersion="3.0.0"} - -# Grab package.json data which is used throughout the build. -$script:PackageJson = Get-Content -Raw $PSScriptRoot/package.json | ConvertFrom-Json -Write-Host "`n### Extension: $($script:PackageJson.name)-$($script:PackageJson.version)`n" -ForegroundColor Green +#Requires -Modules @{ ModuleName = "InvokeBuild"; ModuleVersion = "3.0.0" } function Get-EditorServicesPath { $psesRepoPath = if ($EditorServicesRepoPath) { @@ -95,6 +91,7 @@ task Build Restore, { Write-Host "`n### Building vscode-powershell`n" -ForegroundColor Green Assert-Build (Test-Path ./modules/PowerShellEditorServices/bin) "Extension requires PSES" + Write-Host "`n### Linting TypeScript`n" -ForegroundColor Green Invoke-BuildExec { & npm run lint } # TODO: When supported we should use `esbuild` for the tests too. Although @@ -128,9 +125,17 @@ task TestEditorServices -If (Get-EditorServicesPath) { #region Package tasks task Package Build, { - Write-Host "`n### Packaging $($script:PackageJson.name)-$($script:PackageJson.version).vsix`n" -ForegroundColor Green + # Sanity check our changelog version versus package.json (which lacks pre-release label) + Import-Module $PSScriptRoot/tools/VersionTools.psm1 + $version = Get-Version -RepositoryName vscode-powershell + $packageVersion = Get-MajorMinorPatch -Version $version + $packageJson = Get-Content -Raw $PSScriptRoot/package.json | ConvertFrom-Json + Assert-Build ($packageJson.version -eq $packageVersion) + + Write-Host "`n### Packaging powershell-$packageVersion.vsix`n" -ForegroundColor Green Assert-Build ((Get-Item ./modules).LinkType -ne "SymbolicLink") "Packaging requires a copy of PSES, not a symlink!" - if ($script:PackageJson.preview) { + if (Test-IsPreRelease) { + Write-Host "`n### This is a pre-release!`n" -ForegroundColor Green Invoke-BuildExec { & npm run package -- --pre-release } } else { Invoke-BuildExec { & npm run package }