From 2e791e983deabd2c0f9d6fda1d8935828b647240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Sat, 4 Nov 2017 01:16:01 +0100 Subject: [PATCH 1/7] Add support for common properties shared between all streams --- AU/Public/Update-Package.ps1 | 3 +++ tests/Update-Package.Streams.Tests.ps1 | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index 5512d33b..f008c69e 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -423,6 +423,9 @@ function Update-Package { $streams = $res.Streams } + $res.Keys | ? { $_ -ne 'Streams' } | % { $global:au_Latest.Remove($_) } + $global:au_Latest += $res + $streams.Keys | ? { !$Include -or $_ -in $Include } | sort { [AUVersion] $_ } | % { $stream = $streams[$_] diff --git a/tests/Update-Package.Streams.Tests.ps1 b/tests/Update-Package.Streams.Tests.ps1 index 0f294a18..e054c90e 100644 --- a/tests/Update-Package.Streams.Tests.ps1 +++ b/tests/Update-Package.Streams.Tests.ps1 @@ -20,7 +20,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { if ($Checksum32) { $s += @{ Checksum32 = $Checksum32 } } $streams.Add($stream, $s) } - $command = "function global:au_GetLatest { @{ Streams = @{`n" + $command = "function global:au_GetLatest { @{ Fake = 1; Streams = @{`n" foreach ($item in $streams.Keys) { $command += "'$item' = @{Version = '$($streams.$item.Version)'; URL32 = '$($streams.$item.URL32)'" if ($streams.$item.Checksum32) { $command += "; Checksum32 = '$($streams.$item.Checksum32)'" } @@ -290,6 +290,12 @@ Describe 'Update-Package using streams' -Tag updatestreams { $return_value = @() { update } | Should Throw "returned nothing" } + + It "supports properties defined outside streams" { + get_latest -Version 1.4.0 + function au_BeforeUpdate { $global:Latest.Fake | Should Be 1 } + update + } } Context 'Before and after update' { From 78ce40c173a983bd670e1cd78badcf8f718e1284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Sat, 4 Nov 2017 01:27:52 +0100 Subject: [PATCH 2/7] Minor refactoring on AUVersion --- AU/Private/AUVersion.ps1 | 2 ++ AU/Public/Update-Package.ps1 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AU/Private/AUVersion.ps1 b/AU/Private/AUVersion.ps1 index 7b33bf42..ba57070d 100644 --- a/AU/Private/AUVersion.ps1 +++ b/AU/Private/AUVersion.ps1 @@ -53,6 +53,8 @@ class AUVersion : System.IComparable { } } + [AUVersion] WithVersion([version] $version) { return [AUVersion]::new($version, $this.Prerelease, $this.BuildMetadata) } + [int] CompareTo($obj) { if ($obj -eq $null) { return 1 } if ($obj -isnot [AUVersion]) { throw "AUVersion expected: $($obj.GetType())" } diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index f008c69e..5a3a3766 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -284,7 +284,7 @@ function Update-Package { $build = if ($v.Build -eq -1) {0} else {$v.Build} $v = [version] ('{0}.{1}.{2}.{3}' -f $v.Major, $v.Minor, $build, $d) - $package.RemoteVersion = [AUVersion]::new($v, $nuspecVersion.Prerelease, $nuspecVersion.BuildMetadata).ToString() + $package.RemoteVersion = $nuspecVersion.WithVersion($v).ToString() $Latest.Version = $package.RemoteVersion -as $Latest.Version.GetType() } From 4cce4e90cb1d0036367af9b2bb12d33d58ff2b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Sat, 4 Nov 2017 16:36:34 +0100 Subject: [PATCH 3/7] Support alphabetical streams --- AU/Public/Update-Package.ps1 | 33 +++++++++++-------- tests/Update-Package.Streams.Tests.ps1 | 27 ++++++++++++--- .../test_package_with_streams.json | 5 ++- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index 5a3a3766..b2021306 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -401,33 +401,40 @@ function Update-Package { if ($res.ContainsKey('Streams')) { if (!$res.Streams) { throw "au_GetLatest's streams returned nothing" } - if ($res.Streams -isnot [HashTable]) { throw "au_GetLatest's streams don't return a HashTable result but $($res.Streams.GetType())" } + if ($res.Streams -isnot [System.Collections.Specialized.OrderedDictionary] -and $res.Streams -isnot [HashTable]) { + throw "au_GetLatest doesn't return an OrderedDictionary or HashTable result for streams but $($res.Streams.GetType())" + } + + # Streams are expected to be sorted starting with the most recent one + $streams = @($res.Streams.Keys) + # In case of HashTable (i.e. not sorted), let's sort streams alphabetically descending + if ($res.Streams -is [HashTable]) { $streams = $streams | sort -Descending } if ($Include) { if ($Include -isnot [string] -and $Include -isnot [double] -and $Include -isnot [Array]) { throw "`$Include must be either a String, a Double or an Array but is $($Include.GetType())" } if ($Include -is [double]) { $Include = $Include -as [string] } - if ($Include -is [string]) { [Array] $Include = $Include -split ',' | foreach { ,$_.Trim() } } + if ($Include -is [string]) { + # Forcing type in order to handle case when only one version is included + [Array] $Include = $Include -split ',' | % { $_.Trim() } + } } elseif ($Force) { - $Include = @($res.Streams.Keys | sort { [AUVersion] $_ } -Descending | select -First 1) + # When forcing update, a single stream is expected + # By default, we take the first one (i.e. the most recent one) + $Include = @($streams | select -First 1) } if ($Force -and (!$Include -or $Include.Length -ne 1)) { throw 'A single stream must be included when forcing package update' } - if ($Include) { - $streams = @{} - $res.Streams.Keys | ? { $_ -in $Include } | % { - $streams.Add($_, $res.Streams[$_]) - } - } else { - $streams = $res.Streams - } + if ($Include) { $streams = $streams | ? { $_ -in $Include } } + # Let's reverse the order in order to process streams starting with the oldest one + [Array]::Reverse($streams) $res.Keys | ? { $_ -ne 'Streams' } | % { $global:au_Latest.Remove($_) } $global:au_Latest += $res - $streams.Keys | ? { !$Include -or $_ -in $Include } | sort { [AUVersion] $_ } | % { - $stream = $streams[$_] + $streams | % { + $stream = $res.Streams[$_] '' | result "*** Stream: $_ ***" | result diff --git a/tests/Update-Package.Streams.Tests.ps1 b/tests/Update-Package.Streams.Tests.ps1 index e054c90e..5a2ad3be 100644 --- a/tests/Update-Package.Streams.Tests.ps1 +++ b/tests/Update-Package.Streams.Tests.ps1 @@ -7,8 +7,8 @@ Describe 'Update-Package using streams' -Tag updatestreams { function global:get_latest([string] $Version, [string] $URL32, [string] $Checksum32) { $streams = @{ '1.4' = @{ Version = '1.4-beta1'; URL32 = 'test.1.4-beta1' } - '1.2' = @{ Version = '1.2.4'; URL32 = 'test.1.2.4' } '1.3' = @{ Version = '1.3.1'; URL32 = 'test.1.3.1' } + '1.2' = @{ Version = '1.2.4'; URL32 = 'test.1.2.4' } } if ($Version) { $stream = (ConvertTo-AUVersion $Version).ToString(2) @@ -20,8 +20,8 @@ Describe 'Update-Package using streams' -Tag updatestreams { if ($Checksum32) { $s += @{ Checksum32 = $Checksum32 } } $streams.Add($stream, $s) } - $command = "function global:au_GetLatest { @{ Fake = 1; Streams = @{`n" - foreach ($item in $streams.Keys) { + $command = "function global:au_GetLatest { @{ Fake = 1; Streams = [ordered] @{`n" + foreach ($item in ($streams.Keys| sort { ConvertTo-AUVersion $_ } -Descending)) { $command += "'$item' = @{Version = '$($streams.$item.Version)'; URL32 = '$($streams.$item.URL32)'" if ($streams.$item.Checksum32) { $command += "; Checksum32 = '$($streams.$item.Checksum32)'" } $command += "}`n" @@ -283,10 +283,10 @@ Describe 'Update-Package using streams' -Tag updatestreams { Context 'au_GetLatest' { - It "throws if au_GetLatest doesn't return HashTable" { + It "throws if au_GetLatest doesn't return OrderedDictionary or HashTable for streams" { $return_value = @(1) function global:au_GetLatest { @{ Streams = $return_value } } - { update } | Should Throw "don't return a HashTable" + { update } | Should Throw "doesn't return an OrderedDictionary or HashTable" $return_value = @() { update } | Should Throw "returned nothing" } @@ -296,6 +296,23 @@ Describe 'Update-Package using streams' -Tag updatestreams { function au_BeforeUpdate { $global:Latest.Fake | Should Be 1 } update } + + It 'supports alphabetical streams' { + $return_value = @{ + dev = @{ Version = '1.4.0' } + beta = @{ Version = '1.3.1' } + stable = @{ Version = '1.2.4' } + } + function global:au_GetLatest { @{ Streams = $return_value } } + + $res = update + + $res.Updated | Should Be $true + $res.Result[-1] | Should Be 'Package updated' + (json_file).stable | Should Be 1.2.4 + (json_file).beta | Should Be 1.3.1 + (json_file).dev | Should Be 1.4.0 + } } Context 'Before and after update' { diff --git a/tests/test_package_with_streams/test_package_with_streams.json b/tests/test_package_with_streams/test_package_with_streams.json index 2bd739d0..b5ac88e4 100644 --- a/tests/test_package_with_streams/test_package_with_streams.json +++ b/tests/test_package_with_streams/test_package_with_streams.json @@ -1,5 +1,8 @@ { "1.2": "1.2.3", "1.3": "1.3.1", - "1.4": "1.4-beta1" + "1.4": "1.4-beta1", + "stable": "1.2.3", + "beta": "1.3.1", + "dev": "1.4-beta1" } From ce0403684a097b540c1247bff88c9486784d923a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Mon, 6 Nov 2017 02:23:43 +0100 Subject: [PATCH 4/7] Add more information on streams for plugins (+ adapt GitReleases) & add more pester tests --- AU/Plugins/GitReleases.ps1 | 23 ++----- AU/Private/AUPackage.ps1 | 26 +++++++ AU/Public/Update-AUPackages.ps1 | 4 +- AU/Public/Update-Package.ps1 | 6 ++ test.ps1 | 10 ++- tests/Update-AUPackages.Streams.Tests.ps1 | 80 +++++++--------------- tests/Update-AUPackages.Tests.ps1 | 62 +++++++++++++++++ tests/test_package_with_streams/update.ps1 | 4 +- 8 files changed, 137 insertions(+), 78 deletions(-) diff --git a/AU/Plugins/GitReleases.ps1 b/AU/Plugins/GitReleases.ps1 index 9a6cdccd..9fce303d 100644 --- a/AU/Plugins/GitReleases.ps1 +++ b/AU/Plugins/GitReleases.ps1 @@ -21,9 +21,6 @@ param( # The text that should be used in the description of the release. [string]$releaseDescription = $null, - # The text that should be used in the description when a release is created for a stream (by default it uses the latest commit message). - [string]$streamReleaseDescription = '', - # The formatting to use when replacing in release header/description and on date based releases. [string]$dateFormat = '{0:yyyy-MM-dd}', @@ -70,14 +67,12 @@ $packagesToRelease = New-Object 'System.Collections.Generic.List[hashtable]' $packages | % { if ($_.Streams) { - $pkg = $_ - $data = ConvertFrom-Json ($pkg.Streams -replace '@', '' -replace '\s*=\s*', '":"' -replace '{\s*', '{"' -replace '\s*}', '"}' -replace '\s*;\s*', '","') - ($data | Get-Member -MemberType NoteProperty).Name | % { - $value = $data.$_ + $_.StreamsDetails.Values | ? { $_.Updated } | % { $packagesToRelease.Add(@{ - Name = $pkg.Name - RemoteVersion = $value - NuFile = Resolve-Path ("$($pkg.Path)/*.$($value).nupkg") + Name = $_.Name + NuspecVersion = $_.NuspecVersion + RemoteVersion = $_.RemoteVersion + NuFile = Resolve-Path ("$($_.Path)/$($_.Name).$($_.RemoteVersion).nupkg") }) } } @@ -143,13 +138,7 @@ $packagesToRelease | % { if ($releaseType -eq 'package') { $releaseName = $releaseHeader -replace '', $_.Name -replace '', $_.RemoteVersion -replace '', $_.NuspecVersion -replace '', $date - if ($_.NuspecVersion) { - $packageDesc = $releaseDescription - } - else { - $packageDesc = $streamReleaseDescription - } - $packageDesc = $packageDesc -replace '', $_.Name -replace '', $_.RemoteVersion -replace '', $_.NuspecVersion -replace '', $date + $packageDesc = $releaseDescription -replace '', $_.Name -replace '', $_.RemoteVersion -replace '', $_.NuspecVersion -replace '', $date $release = GetOrCreateRelease ` -tagName "$($_.Name)-$($_.RemoteVersion)" ` diff --git a/AU/Private/AUPackage.ps1 b/AU/Private/AUPackage.ps1 index 7ea0a042..8605a54e 100644 --- a/AU/Private/AUPackage.ps1 +++ b/AU/Private/AUPackage.ps1 @@ -14,6 +14,7 @@ class AUPackage { [string] $StreamsPath [pscustomobject] $Streams + [hashtable] $StreamsDetails AUPackage([string] $Path ){ if ([String]::IsNullOrWhiteSpace( $Path )) { throw 'Package path can not be empty' } @@ -31,6 +32,16 @@ class AUPackage { $this.Streams = [AUPackage]::LoadStreams( $this.StreamsPath ) } + [PSCustomObject] GetStreamDetails() { + return [PSCustomObject] @{ + Path = $this.Path + Name = $this.Name + Updated = $this.Updated + RemoteVersion = $this.RemoteVersion + NuspecVersion = $this.NuspecVersion + } + } + static [xml] LoadNuspecFile( $NuspecPath ) { $nu = New-Object xml $nu.PSBase.PreserveWhitespace = $true @@ -77,4 +88,19 @@ class AUPackage { return "$d\_output" } + AUPackage( [hashtable] $obj ) { + if (!$obj) { throw 'Obj can not be empty' } + $obj.Keys | % { + $this.$_ = $obj[$_] + } + } + + [hashtable] Serialize() { + $res = @{} + $this | Get-Member -Type Properties | % { + $property = $_.Name + $res.Add($property, $this.$property) + } + return $res + } } diff --git a/AU/Public/Update-AUPackages.ps1 b/AU/Public/Update-AUPackages.ps1 index dcc7b82c..a045b3bf 100644 --- a/AU/Public/Update-AUPackages.ps1 +++ b/AU/Public/Update-AUPackages.ps1 @@ -129,6 +129,8 @@ function Update-AUPackages { } else { $pkg.Error = 'Job returned no object, Vector smash ?' } + } else { + $pkg = [AUPackage]::new($pkg) } @@ -228,7 +230,7 @@ function Update-AUPackages { . $s $using:package_name $Options } - $pkg + $pkg.Serialize() } | Out-Null } $result = $result | sort Name diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index b2021306..a42b7a2b 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -200,6 +200,8 @@ function Update-Package { } function process_stream() { + $package.Updated = $false + if (!(is_version $package.NuspecVersion)) { Write-Warning "Invalid nuspec file Version '$($package.NuspecVersion)' - using 0.0" $global:Latest.NuspecVersion = $package.NuspecVersion = '0.0' @@ -433,6 +435,7 @@ function Update-Package { $res.Keys | ? { $_ -ne 'Streams' } | % { $global:au_Latest.Remove($_) } $global:au_Latest += $res + $package.StreamsDetails = @{} $streams | % { $stream = $res.Streams[$_] @@ -450,7 +453,10 @@ function Update-Package { set_latest $stream $package.Streams.$_ $_ process_stream + + $package.StreamsDetails.Add($_, $package.GetStreamDetails()) } + $package.StreamsDetails.Values | ? { $_.updated } | % { $package.Updated = $true } } else { '' | result set_latest $res $package.NuspecVersion diff --git a/test.ps1 b/test.ps1 index f883b4ec..42580408 100644 --- a/test.ps1 +++ b/test.ps1 @@ -2,7 +2,8 @@ param( [switch]$Chocolatey, [switch]$Pester, - [string]$Tag + [string]$Tag, + [switch]$CodeCoverage ) if (!$Chocolatey -and !$Pester) { $Chocolatey = $Pester = $true } @@ -20,5 +21,10 @@ if ($Pester) { Write-Host "`n==| Running Pester tests" $testResultsFile = "$build_dir/TestResults.xml" - Invoke-Pester -Tag $Tag -OutputFormat NUnitXml -OutputFile $testResultsFile -PassThru + if ($CodeCoverage) { + $files = @(ls $PSScriptRoot/AU/* -Filter *.ps1 -Recurse | % FullName) + Invoke-Pester -Tag $Tag -OutputFormat NUnitXml -OutputFile $testResultsFile -PassThru -CodeCoverage $files + } else { + Invoke-Pester -Tag $Tag -OutputFormat NUnitXml -OutputFile $testResultsFile -PassThru + } } diff --git a/tests/Update-AUPackages.Streams.Tests.ps1 b/tests/Update-AUPackages.Streams.Tests.ps1 index c42550b8..d3ef1a34 100644 --- a/tests/Update-AUPackages.Streams.Tests.ps1 +++ b/tests/Update-AUPackages.Streams.Tests.ps1 @@ -33,65 +33,33 @@ Describe 'Update-AUPackages using streams' -Tag updateallstreams { Context 'Plugins' { - It 'should ignore the package that returns "ignore"' { + It 'should execute GitReleases plugin when there are updates' { gc $global:au_Root\test_package_with_streams_1\update.ps1 | set content - $content -replace 'update', "Write-Host 'test ignore'; 'ignore'" | set content + $content -replace '@\{.+1\.3.+\}', "@{ Version = '1.3.2' }" | set content + $content -replace '@\{.+1\.2.+\}', "@{ Version = '1.2.4' }" | set content $content | sc $global:au_Root\test_package_with_streams_1\update.ps1 - - $res = updateall -Options $Options -NoPlugins:$false 6>$null - - $res[0].Ignored | Should Be $true - $res[0].IgnoreMessage | Should Be 'test ignore' - } - - It 'should repeat and ignore on specific error' { - gc $global:au_Root\test_package_with_streams_1\update.ps1 | set content - $content -replace 'update', "1|Out-File -Append $TestDrive\tmp_test; throw 'test ignore'; update" | set content - $content | sc $global:au_Root\test_package_with_streams_1\update.ps1 - - $Options.RepeatOn = @('test ignore') - $Options.RepeatCount = 2 - $Options.IgnoreOn = @('test ignore') - - $res = updateall -Options $Options -NoPlugins:$false 6>$null - - $res[0].Ignored | Should Be $true - $res[0].IgnoreMessage | Should BeLike 'AU ignored on*test ignore' - - (gc $TestDrive\tmp_test).Count | Should be 3 - } - - It 'should execute Report plugin' { - $Options.Report = @{ - Type = 'markdown' - Path = "$global:au_Root\report.md" - Params = @{ Github_UserRepo = 'majkinetor/chocolatey' } - } - - updateall -NoPlugins:$false -Options $Options 6> $null - - Test-Path $Options.Report.Path | Should Be $true - - $report = gc $Options.Report.Path - ($report -match "test_package_with_streams_[1-$pkg_no]").Count | Should Be (3 * $pkg_no) - } - - It 'should execute RunInfo plugin' { - $Options.RunInfo = @{ - Path = "$global:au_Root\update_info.xml" - Exclude = 'password' + + $Options.GitReleases = @{ + ApiToken = 'apiToken' + ReleaseType = 'package' + Force = $true } - $Options.Test = @{ - MyPassword = 'password' - Parameter2 = 'p2'` - } - - $res = updateall -NoPlugins:$false -Options $Options 6> $null - Test-Path $Options.RunInfo.Path | Should Be $true - $info = Import-Clixml $Options.RunInfo.Path - $info.plugin_results.RunInfo -match 'Test.MyPassword' | Should Be $true - $info.Options.Test.MyPassword | Should Be '*****' + Mock Invoke-RestMethod { + return @{ + tag_name = 'test_package_with_streams_1-1.2.4' + assets = @( + @{ + url = 'https://api.github.com/test_package_with_streams_1.1.2.4.nupkg' + name = 'test_package_with_streams_1.1.2.4.nupkg' + } + ) + } + } -ModuleName AU + + updateall -NoPlugins:$false -Options $Options 6> $null + + Assert-MockCalled Invoke-RestMethod -Exactly 6 -ModuleName AU } } @@ -100,7 +68,7 @@ Describe 'Update-AUPackages using streams' -Tag updateallstreams { $choco_path = gcm choco.exe | % Source $choco_hash = Get-FileHash $choco_path -Algorithm SHA256 | % Hash gc $global:au_Root\test_package_with_streams_1\update.ps1 | set content - $content -replace '@\{.+\}', "@{ Version = '1.3'; ChecksumType32 = 'sha256'; Checksum32 = '$choco_hash'}" | set content + $content -replace '@\{.+1\.3.+\}', "@{ Version = '1.3.2'; ChecksumType32 = 'sha256'; Checksum32 = '$choco_hash'}" | set content $content -replace 'update', "update -ChecksumFor 32" | set content $content | sc $global:au_Root\test_package_with_streams_1\update.ps1 diff --git a/tests/Update-AUPackages.Tests.ps1 b/tests/Update-AUPackages.Tests.ps1 index 8c7acc2d..ddc0deb4 100644 --- a/tests/Update-AUPackages.Tests.ps1 +++ b/tests/Update-AUPackages.Tests.ps1 @@ -136,6 +136,68 @@ Describe 'Update-AUPackages' -Tag updateall { $info.plugin_results.RunInfo -match 'Test.MyPassword' | Should Be $true $info.Options.Test.MyPassword | Should Be '*****' } + + It 'should execute GitReleases plugin when there are no updates' { + $Options.GitReleases = @{ + ApiToken = 'apiToken' + ReleaseType = 'package' + Force = $true + } + + Mock Invoke-RestMethod {} -ModuleName AU + + updateall -NoPlugins:$false -Options $Options 6> $null + + Assert-MockCalled Invoke-RestMethod -Exactly 0 -ModuleName AU + } + + It 'should execute GitReleases plugin per package when there are updates' { + gc $global:au_Root\test_package_1\update.ps1 | set content + $content -replace '@\{.+\}', "@{ Version = '1.3' }" | set content + $content | sc $global:au_Root\test_package_1\update.ps1 + + $Options.GitReleases = @{ + ApiToken = 'apiToken' + ReleaseType = 'package' + Force = $true + } + + Mock Invoke-RestMethod { + return @{ + tag_name = 'test_package_1-1.3' + assets = @( + @{ + url = 'https://api.github.com/test_package_1.1.3.nupkg' + name = 'test_package_1.1.3.nupkg' + } + ) + } + } -ModuleName AU + + updateall -NoPlugins:$false -Options $Options 6> $null + + Assert-MockCalled Invoke-RestMethod -Exactly 3 -ModuleName AU + } + + It 'should execute GitReleases plugin per date when there are updates' { + gc $global:au_Root\test_package_1\update.ps1 | set content + $content -replace '@\{.+\}', "@{ Version = '1.3' }" | set content + $content | sc $global:au_Root\test_package_1\update.ps1 + + $Options.GitReleases = @{ + ApiToken = 'apiToken' + ReleaseType = 'date' + Force = $true + } + + Mock Get-Date { return '2017-11-05' } -ParameterFilter { $UFormat -eq '{0:yyyy-MM-dd}' } -ModuleName AU + Mock Invoke-RestMethod { return @{ tag_name = '2017-11-05' } } -ModuleName AU + + updateall -NoPlugins:$false -Options $Options 6> $null + + Assert-MockCalled Get-Date -Exactly 1 -ModuleName AU + Assert-MockCalled Invoke-RestMethod -Exactly 2 -ModuleName AU + } } It 'should update package with checksum verification mode' { diff --git a/tests/test_package_with_streams/update.ps1 b/tests/test_package_with_streams/update.ps1 index f59df342..225892a5 100644 --- a/tests/test_package_with_streams/update.ps1 +++ b/tests/test_package_with_streams/update.ps1 @@ -3,10 +3,10 @@ function global:au_SearchReplace() { } function global:au_GetLatest() { - @{ Streams = @{ + @{ Streams = [ordered] @{ '1.4' = @{ Version = '1.4-beta1' } - '1.2' = @{ Version = '1.2.3' } '1.3' = @{ Version = '1.3.1' } + '1.2' = @{ Version = '1.2.3' } } } } From 25ad3a41b6815512a04732b351fd2b111ba4a856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Thu, 9 Nov 2017 14:24:42 +0100 Subject: [PATCH 5/7] Merge Streams and StreamsDetails property in AUPackage + add tests on it --- AU/Plugins/GitReleases.ps1 | 2 +- AU/Private/AUPackage.ps1 | 51 ++++++++++++++--------- AU/Public/Update-Package.ps1 | 10 ++--- tests/AUPackage.Tests.ps1 | 57 ++++++++++++++++++++++++++ tests/Get-Version.Tests.ps1 | 12 ------ tests/Update-AUPackages.Tests.ps1 | 20 ++++----- tests/Update-Package.Streams.Tests.ps1 | 15 ++++++- 7 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 tests/AUPackage.Tests.ps1 diff --git a/AU/Plugins/GitReleases.ps1 b/AU/Plugins/GitReleases.ps1 index 9fce303d..fd76babf 100644 --- a/AU/Plugins/GitReleases.ps1 +++ b/AU/Plugins/GitReleases.ps1 @@ -67,7 +67,7 @@ $packagesToRelease = New-Object 'System.Collections.Generic.List[hashtable]' $packages | % { if ($_.Streams) { - $_.StreamsDetails.Values | ? { $_.Updated } | % { + $_.Streams.Values | ? { $_.Updated } | % { $packagesToRelease.Add(@{ Name = $_.Name NuspecVersion = $_.NuspecVersion diff --git a/AU/Private/AUPackage.ps1 b/AU/Private/AUPackage.ps1 index 8605a54e..83dd3f64 100644 --- a/AU/Private/AUPackage.ps1 +++ b/AU/Private/AUPackage.ps1 @@ -11,10 +11,8 @@ class AUPackage { [xml] $NuspecXml [bool] $Ignored [string] $IgnoreMessage - - [string] $StreamsPath - [pscustomobject] $Streams - [hashtable] $StreamsDetails + [string] $StreamsPath + [System.Collections.Specialized.OrderedDictionary] $Streams AUPackage([string] $Path ){ if ([String]::IsNullOrWhiteSpace( $Path )) { throw 'Package path can not be empty' } @@ -32,13 +30,12 @@ class AUPackage { $this.Streams = [AUPackage]::LoadStreams( $this.StreamsPath ) } - [PSCustomObject] GetStreamDetails() { - return [PSCustomObject] @{ + [hashtable] GetStreamDetails() { + return @{ Path = $this.Path Name = $this.Name Updated = $this.Updated RemoteVersion = $this.RemoteVersion - NuspecVersion = $this.NuspecVersion } } @@ -54,21 +51,28 @@ class AUPackage { [System.IO.File]::WriteAllText($this.NuspecPath, $this.NuspecXml.InnerXml, $Utf8NoBomEncoding) } - static [pscustomobject] LoadStreams( $StreamsPath ) { - if (!(Test-Path $StreamsPath)) { return $null } - return Get-Content $StreamsPath | ConvertFrom-Json + static [System.Collections.Specialized.OrderedDictionary] LoadStreams( $streamsPath ) { + if (!(Test-Path $streamsPath)) { return $null } + $res = [ordered] @{} + $versions = Get-Content $streamsPath | ConvertFrom-Json + $versions.psobject.Properties | % { + $stream = $_.Name + $res.Add($stream, @{ NuspecVersion = $versions.$stream }) + } + return $res } UpdateStream( $stream, $version ){ - if (!$this.Streams) { $this.Streams = [pscustomobject] @{} } $s = $stream.ToString() $v = $version.ToString() - if ($this.Streams | Get-Member $s) { - if ($this.Streams.$s -ne 'ignore') { $this.Streams.$s = $v } - } else { - $this.Streams | Add-Member $s $v + if (!$this.Streams) { $this.Streams = [ordered] @{} } + if (!$this.Streams.Contains($s)) { $this.Streams.$s = @{} } + if ($this.Streams.$s -ne 'ignore') { $this.Streams.$s.NuspecVersion = $v } + $versions = [ordered] @{} + $this.Streams.Keys | % { + $versions.Add($_, $this.Streams.$_.NuspecVersion) } - $this.Streams | ConvertTo-Json | Set-Content $this.StreamsPath -Encoding UTF8 + $versions | ConvertTo-Json | Set-Content $this.StreamsPath -Encoding UTF8 } Backup() { @@ -90,17 +94,26 @@ class AUPackage { AUPackage( [hashtable] $obj ) { if (!$obj) { throw 'Obj can not be empty' } - $obj.Keys | % { - $this.$_ = $obj[$_] + $obj.Keys | ? { $_ -ne 'Streams' } | % { + $this.$_ = $obj.$_ + } + if ($obj.Streams) { + $this.Streams = [ordered] @{} + $obj.Streams.psobject.Properties | % { + $this.Streams.Add($_.Name, $_.Value) + } } } [hashtable] Serialize() { $res = @{} - $this | Get-Member -Type Properties | % { + $this | Get-Member -Type Properties | ? { $_.Name -ne 'Streams' } | % { $property = $_.Name $res.Add($property, $this.$property) } + if ($this.Streams) { + $res.Add('Streams', [PSCustomObject] $this.Streams) + } return $res } } diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index a42b7a2b..490c9e7c 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -435,7 +435,6 @@ function Update-Package { $res.Keys | ? { $_ -ne 'Streams' } | % { $global:au_Latest.Remove($_) } $global:au_Latest += $res - $package.StreamsDetails = @{} $streams | % { $stream = $res.Streams[$_] @@ -446,17 +445,18 @@ function Update-Package { if ($stream -eq 'ignore') { return } if ($stream -isnot [HashTable]) { throw "au_GetLatest's $_ stream doesn't return a HashTable result but $($stream.GetType())" } - if ($package.Streams.$_ -eq 'ignore') { + if ($package.Streams.$_.NuspecVersion -eq 'ignore') { 'Ignored' | result return } - set_latest $stream $package.Streams.$_ $_ + set_latest $stream $package.Streams.$_.NuspecVersion $_ process_stream - $package.StreamsDetails.Add($_, $package.GetStreamDetails()) + $package.Streams.$_ += $package.GetStreamDetails() } - $package.StreamsDetails.Values | ? { $_.updated } | % { $package.Updated = $true } + $package.Updated = $false + $package.Streams.Values | ? { $_.Updated } | % { $package.Updated = $true } } else { '' | result set_latest $res $package.NuspecVersion diff --git a/tests/AUPackage.Tests.ps1 b/tests/AUPackage.Tests.ps1 new file mode 100644 index 00000000..0c99c922 --- /dev/null +++ b/tests/AUPackage.Tests.ps1 @@ -0,0 +1,57 @@ +remove-module AU -ea ignore +import-module $PSScriptRoot\..\AU + +Describe 'AUPackage' -Tag aupackage { + InModuleScope AU { + It 'throws an error when intanciating without a path' { + { [AUPackage]::new('') } | Should Throw 'empty' + } + + It 'throws an error when intanciating without a hashtable' { + { [AUPackage]::new([hashtable] $null) } | Should Throw 'empty' + } + + It 'can serialize and deserialize' { + $expected = @{ + Path = 'path' + Name = 'name' + Updated = $true + Pushed = $true + RemoteVersion = '1.2.3' + NuspecVersion = '0.1.2' + Result = 'result1,result2,result3' -split ',' + Error = 'error' + NuspecPath = 'nuspecPath' + Ignored = $true + IgnoreMessage = 'ignoreMessage' + StreamsPath = 'streamsPath' + Streams = [PSCustomObject] @{ + '0.1' = @{ + NuspecVersion = '0.1.2' + Path = 'path' + Name = 'name' + Updated = $true + RemoteVersion = '1.2.3' + } + '0.2' = @{ + NuspecVersion = '0.2.2' + Path = 'path' + Name = 'name' + Updated = $true + RemoteVersion = '1.2.3' + } + } + } + + $package = [AUPackage]::new($expected) + $actual = $package.Serialize() + + $expected.Keys | ? { $_ -ne 'Streams' } | % { + $actual.$_ | Should Be $expected.$_ + } + $expected.Streams.psobject.Properties | % { + $actual.Streams.$_ | Should Be $expected.Streams.$_ + } + } + } +} diff --git a/tests/Get-Version.Tests.ps1 b/tests/Get-Version.Tests.ps1 index 93a94da6..39551622 100644 --- a/tests/Get-Version.Tests.ps1 +++ b/tests/Get-Version.Tests.ps1 @@ -2,16 +2,7 @@ remove-module AU -ea ignore import-module $PSScriptRoot\..\AU Describe 'Get-Version' -Tag getversion { - $saved_pwd = $pwd - - BeforeEach { - cd TestDrive:\ - rm -Recurse -Force TestDrive:\test_package -ea ignore - cp -Recurse -Force $PSScriptRoot\test_package TestDrive:\test_package - } - InModuleScope AU { - It 'should convert a strict version' { $expectedVersionStart = '1.2' $expectedVersion = "$expectedVersionStart.3.4" @@ -116,8 +107,5 @@ Describe 'Get-Version' -Tag getversion { $version = [AUVersion] $Value $version | Should Not BeNullOrEmpty } - } - - cd $saved_pwd } diff --git a/tests/Update-AUPackages.Tests.ps1 b/tests/Update-AUPackages.Tests.ps1 index ddc0deb4..62e7b391 100644 --- a/tests/Update-AUPackages.Tests.ps1 +++ b/tests/Update-AUPackages.Tests.ps1 @@ -137,18 +137,18 @@ Describe 'Update-AUPackages' -Tag updateall { $info.Options.Test.MyPassword | Should Be '*****' } - It 'should execute GitReleases plugin when there are no updates' { + It 'should not execute GitReleases plugin when there are no updates' { $Options.GitReleases = @{ ApiToken = 'apiToken' ReleaseType = 'package' Force = $true } - Mock Invoke-RestMethod {} -ModuleName AU + Mock -ModuleName AU Invoke-RestMethod {} updateall -NoPlugins:$false -Options $Options 6> $null - Assert-MockCalled Invoke-RestMethod -Exactly 0 -ModuleName AU + Assert-MockCalled -ModuleName AU Invoke-RestMethod -Exactly 0 -Scope It } It 'should execute GitReleases plugin per package when there are updates' { @@ -162,7 +162,7 @@ Describe 'Update-AUPackages' -Tag updateall { Force = $true } - Mock Invoke-RestMethod { + Mock -ModuleName AU Invoke-RestMethod { return @{ tag_name = 'test_package_1-1.3' assets = @( @@ -172,11 +172,11 @@ Describe 'Update-AUPackages' -Tag updateall { } ) } - } -ModuleName AU + } updateall -NoPlugins:$false -Options $Options 6> $null - Assert-MockCalled Invoke-RestMethod -Exactly 3 -ModuleName AU + Assert-MockCalled -ModuleName AU Invoke-RestMethod -Exactly 3 -Scope It } It 'should execute GitReleases plugin per date when there are updates' { @@ -190,13 +190,13 @@ Describe 'Update-AUPackages' -Tag updateall { Force = $true } - Mock Get-Date { return '2017-11-05' } -ParameterFilter { $UFormat -eq '{0:yyyy-MM-dd}' } -ModuleName AU - Mock Invoke-RestMethod { return @{ tag_name = '2017-11-05' } } -ModuleName AU + Mock -ModuleName AU Get-Date { return '2017-11-05' } -ParameterFilter { $UFormat -eq '{0:yyyy-MM-dd}' } + Mock -ModuleName AU Invoke-RestMethod { return @{ tag_name = '2017-11-05' } } updateall -NoPlugins:$false -Options $Options 6> $null - Assert-MockCalled Get-Date -Exactly 1 -ModuleName AU - Assert-MockCalled Invoke-RestMethod -Exactly 2 -ModuleName AU + Assert-MockCalled -ModuleName AU Get-Date -Exactly 1 -Scope It + Assert-MockCalled -ModuleName AU Invoke-RestMethod -Exactly 2 -Scope It } } diff --git a/tests/Update-Package.Streams.Tests.ps1 b/tests/Update-Package.Streams.Tests.ps1 index 5a2ad3be..3ea793ae 100644 --- a/tests/Update-Package.Streams.Tests.ps1 +++ b/tests/Update-Package.Streams.Tests.ps1 @@ -164,6 +164,9 @@ Describe 'Update-Package using streams' -Tag updatestreams { $res = update $res.Updated | Should Be $true + $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 + $res.Streams.'1.3'.RemoteVersion | Should Be 1.3.1 + $res.Streams.'1.4'.RemoteVersion | Should Be 1.4-beta1 $res.Result[-1] | Should Be 'Package updated' (nuspec_file).package.metadata.version | Should Be 1.2.4 (json_file).'1.2' | Should Be 1.2.4 @@ -177,6 +180,9 @@ Describe 'Update-Package using streams' -Tag updatestreams { $res = update $res.Updated | Should Be $true + $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 + $res.Streams.'1.3'.RemoteVersion | Should Be 1.3.1 + $res.Streams.'1.4'.RemoteVersion | Should Be 1.4.0 $res.Result[-1] | Should Be 'Package updated' (json_file).'1.2' | Should Be 1.2.4 (json_file).'1.3' | Should Be 1.3.1 @@ -189,6 +195,9 @@ Describe 'Update-Package using streams' -Tag updatestreams { $res = update $res.Updated | Should Be $false + $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.3 + $res.Streams.'1.3'.RemoteVersion | Should Be 1.3.1 + $res.Streams.'1.4'.RemoteVersion | Should Be 1.4-beta1 (nuspec_file).package.metadata.version | Should Be 1.2.3 (json_file).'1.2' | Should Be 1.2.3 (json_file).'1.3' | Should Be 1.3.1 @@ -219,6 +228,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { $res = update -Force -Include 1.2 $res.Updated | Should Be $true + $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 (nuspec_file).package.metadata.version | Should Be 1.2.4 (json_file).'1.2' | Should Be 1.2.4 (json_file).'1.3' | Should Be 1.3.1 @@ -229,7 +239,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { function global:au_SearchReplace { @{ 'test_package_with_streams.nuspec' = @{ - '()(.*)()' = "`$1test.$($Latest.Version)`$3" + '()(.*)()' = "`$1test_package_with_streams.$($Latest.Version)`$3" } } } @@ -237,7 +247,8 @@ Describe 'Update-Package using streams' -Tag updatestreams { update $nu = (nuspec_file).package.metadata - $nu.releaseNotes | Should Be 'test.1.2.4' + $nu.releaseNotes | Should Be 'test_package_with_streams.1.2.4' + $nu.id | Should Be 'test_package_with_streams' $nu.version | Should Be 1.2.4 } } From 61bb5073efb56290701f5dbe043bba8c4dcb0a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Thu, 9 Nov 2017 14:52:49 +0100 Subject: [PATCH 6/7] Rename Include to IncludeStream --- AU/Public/Update-Package.ps1 | 22 +++++++++++----------- tests/Update-Package.Streams.Tests.ps1 | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index 490c9e7c..1d6382ce 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -84,7 +84,7 @@ function Update-Package { #Streams to process, either a string or an array. If ommitted, all streams are processed. #Single stream required when Force is specified. - $Include, + $IncludeStream, #Force package update even if no new version is found. #For multi streams packages, most recent stream is checked by default when Force is specified. @@ -395,7 +395,7 @@ function Update-Package { if ($global:au_Force) { $Force = $true - if ($global:au_Include) { $Include = $global:au_Include } + if ($global:au_IncludeStream) { $IncludeStream = $global:au_IncludeStream } } } catch { throw "au_GetLatest failed`n$_" @@ -412,23 +412,23 @@ function Update-Package { # In case of HashTable (i.e. not sorted), let's sort streams alphabetically descending if ($res.Streams -is [HashTable]) { $streams = $streams | sort -Descending } - if ($Include) { - if ($Include -isnot [string] -and $Include -isnot [double] -and $Include -isnot [Array]) { - throw "`$Include must be either a String, a Double or an Array but is $($Include.GetType())" + if ($IncludeStream) { + if ($IncludeStream -isnot [string] -and $IncludeStream -isnot [double] -and $IncludeStream -isnot [Array]) { + throw "`$IncludeStream must be either a String, a Double or an Array but is $($IncludeStream.GetType())" } - if ($Include -is [double]) { $Include = $Include -as [string] } - if ($Include -is [string]) { + if ($IncludeStream -is [double]) { $IncludeStream = $IncludeStream -as [string] } + if ($IncludeStream -is [string]) { # Forcing type in order to handle case when only one version is included - [Array] $Include = $Include -split ',' | % { $_.Trim() } + [Array] $IncludeStream = $IncludeStream -split ',' | % { $_.Trim() } } } elseif ($Force) { # When forcing update, a single stream is expected # By default, we take the first one (i.e. the most recent one) - $Include = @($streams | select -First 1) + $IncludeStream = @($streams | select -First 1) } - if ($Force -and (!$Include -or $Include.Length -ne 1)) { throw 'A single stream must be included when forcing package update' } + if ($Force -and (!$IncludeStream -or $IncludeStream.Length -ne 1)) { throw 'A single stream must be included when forcing package update' } - if ($Include) { $streams = $streams | ? { $_ -in $Include } } + if ($IncludeStream) { $streams = $streams | ? { $_ -in $IncludeStream } } # Let's reverse the order in order to process streams starting with the oldest one [Array]::Reverse($streams) diff --git a/tests/Update-Package.Streams.Tests.ps1 b/tests/Update-Package.Streams.Tests.ps1 index 3ea793ae..66a92ad6 100644 --- a/tests/Update-Package.Streams.Tests.ps1 +++ b/tests/Update-Package.Streams.Tests.ps1 @@ -46,7 +46,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { $global:au_Timeout = 100 $global:au_Force = $false - $global:au_Include = '' + $global:au_IncludeStream = '' $global:au_NoHostOutput = $true $global:au_NoCheckUrl = $true $global:au_NoCheckChocoVersion = $true @@ -129,7 +129,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { It 'can let user override the version of a specific stream' { get_latest -Version 1.2.3 $global:au_Force = $true - $global:au_Include = '1.2' + $global:au_IncludeStream = '1.2' $global:au_Version = '1.0' $res = update -ChecksumFor 32 6> $null @@ -152,7 +152,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { } It 'automatically calculates the checksum' { - update -ChecksumFor 32 -Include 1.2 6> $null + update -ChecksumFor 32 -IncludeStream 1.2 6> $null $global:Latest.Checksum32 | Should Not BeNullOrEmpty $global:Latest.ChecksumType32 | Should Be 'sha256' @@ -206,13 +206,13 @@ Describe 'Update-Package using streams' -Tag updatestreams { It "throws an error when forcing update whithout specifying a stream" { get_latest -Version 1.2.3 - { update -Force -Include 1.2,1.4 } | Should Throw 'A single stream must be included when forcing package update' + { update -Force -IncludeStream 1.2,1.4 } | Should Throw 'A single stream must be included when forcing package update' } It "updates the package when forced using choco fix notation" { get_latest -Version 1.2.3 - $res = update -Force -Include 1.2 + $res = update -Force -IncludeStream 1.2 $d = (get-date).ToString('yyyyMMdd') $res.Updated | Should Be $true @@ -225,7 +225,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { } It "does not use choco fix notation if the package remote version is higher" { - $res = update -Force -Include 1.2 + $res = update -Force -IncludeStream 1.2 $res.Updated | Should Be $true $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 @@ -270,7 +270,7 @@ Describe 'Update-Package using streams' -Tag updatestreams { $streams.'1.2' = '{{PackageVersion}}' $streams | ConvertTo-Json | Set-Content "$TestDrive\test_package_with_streams\test_package_with_streams.json" -Encoding UTF8 - update -Include 1.2 *> $null + update -IncludeStream 1.2 *> $null $global:Latest.NuspecVersion | Should Be '0.0' } @@ -329,20 +329,20 @@ Describe 'Update-Package using streams' -Tag updatestreams { Context 'Before and after update' { It 'calls au_BeforeUpdate if package is updated' { function au_BeforeUpdate { $global:Latest.test = 1 } - update -Include 1.2 + update -IncludeStream 1.2 $global:Latest.test | Should Be 1 } It 'calls au_AfterUpdate if package is updated' { function au_AfterUpdate { $global:Latest.test = 1 } - update -Include 1.2 + update -IncludeStream 1.2 $global:Latest.test | Should Be 1 } It 'doesnt call au_BeforeUpdate if package is not updated' { get_latest -Version 1.2.3 function au_BeforeUpdate { $global:Latest.test = 1 } - update -Include 1.2 + update -IncludeStream 1.2 $global:Latest.test | Should BeNullOrEmpty } From daf04b55c1b2d5a2061358dd6d46ac3d93dbc03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Thu, 9 Nov 2017 20:23:47 +0100 Subject: [PATCH 7/7] Fix IncludeStream argument when only one is passed --- AU/Public/Update-Package.ps1 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/AU/Public/Update-Package.ps1 b/AU/Public/Update-Package.ps1 index 1d6382ce..102df7a2 100644 --- a/AU/Public/Update-Package.ps1 +++ b/AU/Public/Update-Package.ps1 @@ -393,10 +393,8 @@ function Update-Package { $res_type = $res.GetType() if ($res_type -ne [HashTable]) { throw "au_GetLatest doesn't return a HashTable result but $res_type" } - if ($global:au_Force) { - $Force = $true - if ($global:au_IncludeStream) { $IncludeStream = $global:au_IncludeStream } - } + if ($global:au_Force) { $Force = $true } + if ($global:au_IncludeStream) { $IncludeStream = $global:au_IncludeStream } } catch { throw "au_GetLatest failed`n$_" } @@ -428,7 +426,7 @@ function Update-Package { } if ($Force -and (!$IncludeStream -or $IncludeStream.Length -ne 1)) { throw 'A single stream must be included when forcing package update' } - if ($IncludeStream) { $streams = $streams | ? { $_ -in $IncludeStream } } + if ($IncludeStream) { $streams = @($streams | ? { $_ -in $IncludeStream }) } # Let's reverse the order in order to process streams starting with the oldest one [Array]::Reverse($streams)