Add tests to report when package references are out of date (#7661)

Add tests to report when package references are out of date
  - Also, update the packaging script to generate updated `files.wxs`
This commit is contained in:
Travis Plunk
2018-08-31 12:50:51 -07:00
committed by GitHub
parent e66b74f55e
commit e2a45f0992
6 changed files with 490 additions and 16 deletions
@@ -67,7 +67,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.9.0" />
<PackageReference Include="NJsonSchema" Version="9.10.70" />
<PackageReference Include="NJsonSchema" Version="9.10.71" />
</ItemGroup>
</Project>
+68
View File
@@ -0,0 +1,68 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
$moduleRootFilePath = Split-Path -Path $PSScriptRoot -Parent
# Identify the repository root path of the resource module
$repoRootPath = (Resolve-Path -LiteralPath (Join-path $moduleRootFilePath "../..")).ProviderPath
Import-Module "$repoRootPath/tools/releaseTools.psm1"
Describe 'Common Tests - Package Reference' -Tag 'CI' {
BeforeAll {
$testCases = @()
Get-NewOfficalPackage -IncludeAll | ForEach-Object {
$testCases += @{
CsProj = $_.CsProj
PackageName = $_.PackageName
CsProjVersion = $_.CsProjVersion
NuGetRevision = $_.NuGetRevision
NuGetVersion = $_.NuGetVersion
}
}
}
# This test should always be enabled
It "<CsProj> reference to <PackageName> <CsProjVersion> should not need to be updated by a revision" -TestCases $testCases {
param(
[string]
$CsProj,
[string]
$PackageName,
[string]
$CsProjVersion,
[string]
$NuGetRevision,
[string]
$NuGetVersion
)
$NuGetRevision | Should -BeExactly $CsProjVersion
}
# This test should be enabled when we are developing
It "<CsProj> reference to <PackageName> <CsProjVersion> should not need to be updated by a new version" -TestCases $testCases {
param(
[string]
$CsProj,
[string]
$PackageName,
[string]
$CsProjVersion,
[string]
$NuGetRevision,
[string]
$NuGetVersion
)
$NuGetVersion | Should -BeExactly $CsProjVersion
}
}
+101
View File
@@ -0,0 +1,101 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
# Adds an attribute to a XmlElement
function New-XmlAttribute
{
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[object]$Value,
[Parameter(Mandatory)]
[System.Xml.XmlElement]$Element,
[Parameter(Mandatory)]
[System.Xml.XmlDocument]$XmlDoc
)
$attribute = $XmlDoc.CreateAttribute($Name)
$attribute.Value = $value
$null = $Element.Attributes.Append($attribute)
}
# Adds an XmlElement to an XmlNode
# Returns the new Element
function New-XmlElement
{
param(
[Parameter(Mandatory)]
[string]$LocalName,
[Parameter(Mandatory)]
[System.Xml.XmlDocument]$XmlDoc,
[Parameter(Mandatory)]
[System.Xml.XmlNode]$Node,
[Switch]$PassThru,
[string]$NamespaceUri
)
if($NamespaceUri)
{
$newElement = $XmlDoc.CreateElement($LocalName, $NamespaceUri)
}
else
{
$newElement = $XmlDoc.CreateElement($LocalName)
}
$null = $Node.AppendChild($newElement)
if($PassThru.IsPresent)
{
return $newElement
}
}
# Removes an XmlElement and it's parent if it is empty
function Remove-XmlElement
{
param(
[Parameter(Mandatory)]
[System.Xml.XmlElement]$Element,
[Switch]$RemoveEmptyParents
)
$parentNode = $Element.ParentNode
$null = $parentNode.RemoveChild($Element)
if(!$parentNode.HasChildNodes -and $RemoveEmptyParent.IsPresent)
{
Remove-XmlElement -Element $parentNode -RemoveEmptyParents
}
}
# Get a node by XPath
# Returns null if the node is not found
function Get-XmlNodeByXPath
{
param(
[Parameter(Mandatory)]
[System.Xml.XmlDocument]
$XmlDoc,
[System.Xml.XmlNamespaceManager]
$XmlNsManager,
[Parameter(Mandatory)]
[string]
$XPath
)
if($XmlNsManager)
{
return $XmlDoc.SelectSingleNode($XPath,$XmlNsManager)
}
else
{
return $XmlDoc.SelectSingleNode($XPath)
}
}
Export-ModuleMember -Function @(
'Get-XmlNodeByXPath'
'Remove-XmlElement'
'New-XmlElement'
'New-XmlAttribute'
)
+1
View File
@@ -626,6 +626,7 @@ function Invoke-AppveyorFinish
}
catch {
Write-Host -Foreground Red $_
Write-Host -Foreground Red $_.ScriptStackTrace
throw $_
}
}
+187 -6
View File
@@ -3,6 +3,7 @@
$Environment = Get-EnvironmentInformation
$packagingStrings = Import-PowerShellDataFile "$PSScriptRoot\packaging.strings.psd1"
Import-Module "$PSScriptRoot\..\Xml" -ErrorAction Stop -Force
$DebianDistributions = @("ubuntu.14.04", "ubuntu.16.04", "ubuntu.17.10", "ubuntu.18.04", "debian.8", "debian.9")
function Start-PSPackage {
@@ -2554,15 +2555,19 @@ function Test-FileWxs
$filesAssetString = (Get-Content -Raw -Path $FilesWxsPath).Replace('$(var.FileArchitecture)',$env:FileArchitecture)
[xml] $filesAssetXml = $filesAssetString
[xml] $newFilesAssetXml = $filesAssetString
$xmlns=[System.Xml.XmlNamespaceManager]::new($newFilesAssetXml.NameTable)
$xmlns.AddNamespace('Wix','http://schemas.microsoft.com/wix/2006/wi')
[xml] $heatFilesXml = Get-Content -Raw -Path $HeatFilesWxsPath
$assetFiles = $filesAssetXml.GetElementsByTagName('File')
$heatFiles = $heatFilesXml.GetElementsByTagName('File')
$indexedHeatFiles = @()
$heatNodesByFile = @{}
# Index the list of files generated by heat
foreach($file in $heatFiles)
{
$indexedHeatFiles += $file.Source
$heatNodesByFile.Add($file.Source, $file)
}
# Index the files from the asset wxs
@@ -2572,42 +2577,218 @@ function Test-FileWxs
foreach($file in $assetFiles)
{
$name = $file.Source
if($indexedHeatFiles -inotcontains $name)
if($heatNodesByFile.Keys -inotcontains $name)
{
$passed = $false
Write-Warning "{$name} is no longer in product and should be removed from {$FilesWxsPath}"
$componentId = $file.ParentNode.Id
$componentXPath = '//Wix:Component[@Id="{0}"]' -f $componentId
$componentNode = Get-XmlNodeByXPath -XmlDoc $newFilesAssetXml -XmlNsManager $xmlns -XPath $componentXPath
if($componentNode)
{
# Remove the Component
Remove-XmlElement -Element $componentNode -RemoveEmptyParents
# Remove teh ComponentRef
Remove-ComponentRefNode -Id $componentId -XmlDoc $newFilesAssetXml -XmlNsManager $xmlns
}
else
{
Write-Warning "Could not remove this node!"
}
}
$indexedAssetFiles += $name
}
# verify that no files have been added.
foreach($file in $indexedHeatFiles)
foreach($file in $heatNodesByFile.Keys)
{
if($indexedAssetFiles -inotcontains $file)
{
$passed = $false
Write-Warning "new file {$file} need to be added to {$FilesWxsPath}"
$folder = Split-Path -Path $file
$name = Split-Path -Path $file -Leaf
$heatNode = $heatNodesByFile[$file]
$compGroupNode = Get-ComponentGroupNode -XmlDoc $newFilesAssetXml -XmlNsManager $xmlns
$filesNode = Get-DirectoryNode -Node $heatNode -XmlDoc $newFilesAssetXml -XmlNsManager $xmlns
# Create new Component
$newComponent = New-XmlElement -XmlDoc $newFilesAssetXml -LocalName 'Component' -Node $filesNode -PassThru -NamespaceUri 'http://schemas.microsoft.com/wix/2006/wi'
$componentId = New-WixId -Prefix 'cmp'
New-XmlAttribute -XmlDoc $newFilesAssetXml -Element $newComponent -Name 'Id' -Value $componentId
New-XmlAttribute -XmlDoc $newFilesAssetXml -Element $newComponent -Name 'Guid' -Value "{$(New-Guid)}"
# Crete new File in Component
$newFile = New-XmlElement -XmlDoc $newFilesAssetXml -LocalName 'File' -Node $newComponent -PassThru -NamespaceUri 'http://schemas.microsoft.com/wix/2006/wi'
New-XmlAttribute -XmlDoc $newFilesAssetXml -Element $newFile -Name 'Id' -Value (New-WixId -Prefix 'fil')
New-XmlAttribute -XmlDoc $newFilesAssetXml -Element $newFile -Name 'KeyPath' -Value "yes"
New-XmlAttribute -XmlDoc $newFilesAssetXml -Element $newFile -Name 'Source' -Value $file
# Create new ComponentRef
$newComponentRef = New-XmlElement -XmlDoc $newFilesAssetXml -LocalName 'ComponentRef' -Node $compGroupNode -PassThru -NamespaceUri 'http://schemas.microsoft.com/wix/2006/wi'
New-XmlAttribute -XmlDoc $newFilesAssetXml -Element $newComponentRef -Name 'Id' -Value $componentId
Write-Warning "new file in {$folder} with name {$name} in a {$($filesNode.LocalName)} need to be added to {$FilesWxsPath}"
}
}
if(!$passed)
{
$newXmlFileName = Join-Path -Path $env:TEMP -ChildPath ([System.io.path]::GetRandomFileName() + '.xml')
$newFilesAssetXml.Save($newXmlFileName)
$newXml = Get-Content -raw $newXmlFileName
$newXml = $newXml -replace 'amd64', '$(var.FileArchitecture)'
$newXml = $newXml -replace 'x86', '$(var.FileArchitecture)'
$newXml | Out-File -FilePath $newXmlFileName -Encoding ascii
Write-Log -message "Update xml saved to $newXmlFileName"
if($env:appveyor)
{
try
{
Push-AppveyorArtifact $HeatFilesWxsPath
Push-AppveyorArtifact $newXmlFileName
}
catch
{
Write-Warning -Message "Pushing MSI File fragment failed."
}
}
elseif($env:TF_BUILD -and $env:BUILD_REASON -ne 'PullRequest')
{
Write-Host "##vso[artifact.upload containerfolder=wix;artifactname=wix]$newXmlFileName"
}
throw "Current files to not match {$FilesWxsPath}"
}
}
# Removes a ComponentRef node in the files.wxs Xml Doc
function Remove-ComponentRefNode
{
param(
[Parameter(Mandatory)]
[System.Xml.XmlDocument]
$XmlDoc,
[Parameter(Mandatory)]
[System.Xml.XmlNamespaceManager]
$XmlNsManager,
[Parameter(Mandatory)]
[string]
$Id
)
$compRefXPath = '//Wix:ComponentRef[@Id="{0}"]' -f $Id
$node = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath $compRefXPath
if($node)
{
Remove-XmlElement -element $node
}
else
{
Write-Warning "could not remove node"
}
}
# Get the ComponentGroup node in the files.wxs Xml Doc
function Get-ComponentGroupNode
{
param(
[Parameter(Mandatory)]
[System.Xml.XmlDocument]
$XmlDoc,
[Parameter(Mandatory)]
[System.Xml.XmlNamespaceManager]
$XmlNsManager
)
if(!$XmlNsManager.HasNamespace('Wix'))
{
throw 'Namespace manager must have "wix" defined.'
}
$compGroupXPath = '//Wix:ComponentGroup'
$node = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath $compGroupXPath
return $node
}
# Gets the Directory Node the files.wxs Xml Doc
# Creates it if it does not exist
function Get-DirectoryNode
{
param(
[Parameter(Mandatory)]
[System.Xml.XmlElement]
$Node,
[Parameter(Mandatory)]
[System.Xml.XmlDocument]
$XmlDoc,
[Parameter(Mandatory)]
[System.Xml.XmlNamespaceManager]
$XmlNsManager
)
if(!$XmlNsManager.HasNamespace('Wix'))
{
throw 'Namespace manager must have "wix" defined.'
}
$pathStack = [System.Collections.Stack]::new()
[System.Xml.XmlElement] $dirNode = $Node.ParentNode.ParentNode
$dirNodeType = $dirNode.LocalName
if($dirNodeType -eq 'DirectoryRef')
{
return Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath "//Wix:DirectoryRef"
}
if($dirNodeType -eq 'Directory')
{
while($dirNode.LocalName -eq 'Directory') {
$pathStack.Push($dirNode.Name)
$dirNode = $dirNode.ParentNode
}
$path = "//"
[System.Xml.XmlElement] $lastNode = $null
while($pathStack.Count -gt 0){
$dirName = $pathStack.Pop()
$path += 'Wix:Directory[@Name="{0}"]' -f $dirName
$node = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath $path
if(!$node)
{
if(!$lastNode)
{
# Inserting at the root
$lastNode = Get-XmlNodeByXPath -XmlDoc $XmlDoc -XmlNsManager $XmlNsManager -XPath "//Wix:DirectoryRef"
}
$newDirectory = New-XmlElement -XmlDoc $XmlDoc -LocalName 'Directory' -Node $lastNode -PassThru -NamespaceUri 'http://schemas.microsoft.com/wix/2006/wi'
New-XmlAttribute -XmlDoc $XmlDoc -Element $newDirectory -Name 'Name' -Value $dirName
New-XmlAttribute -XmlDoc $XmlDoc -Element $newDirectory -Name 'Id' -Value (New-WixId -Prefix 'dir')
$lastNode = $newDirectory
}
else
{
$lastNode = $node
}
if ($pathStack.Count -gt 0)
{
$path += '/'
}
}
return $lastNode
}
throw "unknown element type: $dirNodeType"
}
# Creates a new Wix Id in the proper format
function New-WixId
{
param(
[Parameter(Mandatory)]
[string]
$Prefix
)
$guidPortion = (New-Guid).Guid.ToUpperInvariant() -replace '\-' ,''
"$Prefix$guidPortion"
}
# Builds coming out of this project can have version number as 'a.b.c' OR 'a.b.c-d-f'
# This function converts the above version into major.minor[.build[.revision]] format
function Get-PackageVersionAsMajorMinorBuildRevision
+132 -9
View File
@@ -223,6 +223,9 @@ function Get-ChangeLog
#.PARAMETER Path
#The path to check for csproj files with packagse
#
#.PARAMETER IncludeAll
#Include packages that don't need to be updated
#
#.OUTPUTS
#Objects which represet the csproj package ref, with the current and new version
##############################
@@ -230,11 +233,13 @@ function Get-NewOfficalPackage
{
param(
[String]
$Path = (Join-path -Path $PSScriptRoot -ChildPath '..')
$Path = (Join-path -Path $PSScriptRoot -ChildPath '..\src'),
[Switch]
$IncludeAll
)
# Calculate the filter to find the CSProj files
$filter = Join-Path -Path $Path -ChildPath '*.csproj'
$csproj = Get-ChildItem $filter -Recurse
$csproj = Get-ChildItem $filter -Recurse -Exclude 'PSGalleryModules.csproj'
$csproj | ForEach-Object{
$file = $_
@@ -246,25 +251,58 @@ function Get-NewOfficalPackage
$packages=$csprojXml.Project.ItemGroup.PackageReference
# check to see if there is a newer package for each refernce
foreach($package in $packages)
foreach ($package in $packages)
{
# Get the name of the package
$name = $package.Include
# don't pull 'Microsoft.Management.Infrastructure' from nuget
if($name -and $name -ne 'Microsoft.Management.Infrastructure')
if ($name)
{
# Get the current package from nuget
$newPackage = find-package -Name $name -Source https://nuget.org/api/v2/ -ErrorAction SilentlyContinue
$versions = find-package -Name $name -Source https://nuget.org/api/v2/ -ErrorAction SilentlyContinue -AllVersions |
Add-Member -Type ScriptProperty -Name Published -Value { $this.Metadata['published']} -PassThru |
Where-Object { Test-IncludePackageVersion -NewVersion $_.Version -Version $package.version}
$revsionRegEx = Get-MatchingMajorMinorRegEx -Version $package.version
$newPackage = $versions |
Sort-Object -Descending |
Select-Object -First 1
# Get the newest matching revision
$newRevision = $versions |
Where-Object {$_.Version -match $revsionRegEx } |
Sort-Object -Descending |
Select-Object -First 1
# If the current package has a different version from the version in the csproj, print the details
if($newPackage -and $newPackage.Version.ToString() -ne $package.version)
if ($newRevision -and $newRevision.Version.ToString() -ne $package.version -or $newPackage -and $newPackage.Version.ToString() -ne $package.version -or $IncludeAll.IsPresent)
{
if ($newRevision)
{
$newRevisionString = $newRevision.Version
}
else
{
# We don't have a new Revision, report the current version
$newRevisionString = $package.Version
}
if ($newPackage)
{
$newVersionString = $newPackage.Version
}
else
{
# We don't have a new Version, report the current version
$newVersionString = $package.Version
}
[pscustomobject]@{
Csproj = $file
Csproj = (Split-Path -Path $file -Leaf)
PackageName = $name
CsProjVersion = $Package.Version
NuGetVersion = $newPackage.Version
NuGetRevision = $newRevisionString
NuGetVersion = $newVersionString
}
}
}
@@ -272,6 +310,91 @@ function Get-NewOfficalPackage
}
}
##############################
#.SYNOPSIS
# Returns True if NewVersion is newer than Version
# Pre release are ignored if the current version is not pre-release
# If the current version is pre-release, this function only determines if the version portion is NewReleaseTag
# The calling function is responsible for sorting prelease version by publish date (as find-package gives them to you)
# and returning the newest.
#
#.PARAMETER Version
# The current Version
#
#.PARAMETER NewVersion
# The potention replacement version
#
#.OUTPUTS
# True if NewVersion should be considere as a replacement
##############################
function Test-IncludePackageVersion
{
param(
[string]
$NewVersion,
[string]
$Version
)
$simpleCompare = $Version -notlike '*-*'
if($simpleCompare -and $NewVersion -like '*-*')
{
# We are using a stable and the new version is pre-release
return $false
}
elseif($simpleCompare -and [Version]$NewVersion -ge [Version] $Version)
{
# Simple comparison says the new version is newer
return $true
}
elseif($simpleCompare)
{
# Simple comparison was done, but it was not newer
return $false
}
elseif($NewVersion -notlike '*-*')
{
# Our current version is a pre-release but the new is not
# make sure the new version is newer than the version part of the current version
$versionOnly = ($Version -Split '\-')[0]
if([Version]$NewVersion -ge [Version] $versionOnly)
{
return $true
}
else
{
return $false
}
}
else
{
# Not sure, include it
return $true
}
}
##############################
#.SYNOPSIS
# Get a RegEx based on a version that will match the major and minor
#
#.PARAMETER Version
# The version to match
#
##############################
function Get-MatchingMajorMinorRegEx
{
param(
[Parameter(Mandatory)]
$Version
)
$parts = $Version -split '\.'
$regEx = "^$($parts[0])\.$($parts[1])\..*"
return $regEx
}
##############################
#.SYNOPSIS
# Update the version number in code