From d83a468fa9aa128157dc5134a4d993a2fdd490dd Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 00:45:47 +0000 Subject: [PATCH 01/32] More file moves --- Install.ps1 => CI/Install.ps1 | 11 +- CI/PS-CI.ps1 | 236 ++++++++++++++++++ .../PublishToGallery.ps1 | 0 CI/build.ps1 | 6 +- CI/pipeline.yml | 89 +++++++ ImportExcel.psd1 | 1 + ImportExcel.psm1 | 4 +- .../ArgumentCompletion.ps1 | 0 8 files changed, 338 insertions(+), 9 deletions(-) rename Install.ps1 => CI/Install.ps1 (93%) create mode 100644 CI/PS-CI.ps1 rename PublishToGallery.ps1 => CI/PublishToGallery.ps1 (100%) create mode 100644 CI/pipeline.yml rename ArgumentCompletion.ps1 => Private/ArgumentCompletion.ps1 (100%) diff --git a/Install.ps1 b/CI/Install.ps1 similarity index 93% rename from Install.ps1 rename to CI/Install.ps1 index e7be649..1e8d801 100644 --- a/Install.ps1 +++ b/CI/Install.ps1 @@ -81,8 +81,9 @@ function Invoke-MultiLike { } } +Push-Location "$PSScriptRoot\.." try { - Write-Verbose -Message 'Module installation started' + Write-Verbose -Message "Module installation started. Installing from $PWD" if (!$ModulePath) { if ($Scope -eq 'CurrentUser') { @@ -99,6 +100,7 @@ try { } $ModulePath = ($env:PSModulePath -split $ModulePathSeparator)[$ModulePathIndex] } + Write-Verbose -Message "Installing to $ModulePath" # Get $ModuleName, $TargetPath, [$Links] if ($FromGitHub) { @@ -115,8 +117,8 @@ try { $ModuleVersion = (. ([Scriptblock]::Create((Invoke-WebRequest -Uri ($Links | Where-Object { $_.name -eq "$ModuleName.psd1" }).download_url)))).ModuleVersion } else { - $ModuleName = [System.IO.Path]::GetFileNameWithoutExtension((Get-ChildItem -File -Filter *.psm1 -Name -Path $PSScriptRoot)) - $ModuleVersion = (. ([Scriptblock]::Create((Get-Content -Path (Join-Path $PSScriptRoot "$ModuleName.psd1") | Out-String)))).ModuleVersion + $ModuleName = [System.IO.Path]::GetFileNameWithoutExtension((Get-ChildItem -File -Filter *.psm1 -Name -Path $PWD)) + $ModuleVersion = (. ([Scriptblock]::Create((Get-Content -Path (Join-Path $PWD "$ModuleName.psd1") | Out-String)))).ModuleVersion } $TargetPath = Join-Path -Path $ModulePath -ChildPath $ModuleName $TargetPath = Join-Path -Path $TargetPath -ChildPath $ModuleVersion @@ -160,7 +162,7 @@ try { } } else { - Get-ChildItem -Path $PSScriptRoot -Exclude $ExcludeFiles | Where-Object { LikeAny $_.Name $IncludeFiles } | ForEach-Object { + Get-ChildItem -Path $PWD -Exclude $ExcludeFiles | Where-Object { LikeAny $_.Name $IncludeFiles } | ForEach-Object { if ($_.Attributes -ne 'Directory') { Copy-Item -Path $_ -Destination $TargetPath Write-Verbose -Message ('Installed module file "{0}"' -f $_) @@ -185,4 +187,5 @@ finally { # [Net.ServicePointManager]::SecurityProtocol = $SecurityProtocol #} Write-Verbose -Message 'Module installation end' + Pop-Location } \ No newline at end of file diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 new file mode 100644 index 0000000..8c498c3 --- /dev/null +++ b/CI/PS-CI.ps1 @@ -0,0 +1,236 @@ +[cmdletbinding(DefaultParameterSetName='Scope')] +Param( + [Parameter(Mandatory = $true, ParameterSetName = 'ModulePath')] + [ValidateNotNullOrEmpty()] + [String]$ModulePath, + + # Path to install the module to, PSModulePath "CurrentUser" or "AllUsers", if not provided "CurrentUser" used. + [Parameter(ParameterSetName = 'Scope')] + [ValidateSet('CurrentUser', 'AllUsers')] + [string] + $Scope = 'CurrentUser', + + [Parameter(Mandatory=$true, ParameterSetName = 'PreCheckOnly')] + [switch]$PreCheckOnly, + [Parameter(ParameterSetName = 'ModulePath')] + [Parameter(ParameterSetName = 'Scope')] + [switch]$SkipPreChecks, + [Parameter(ParameterSetName = 'ModulePath')] + [Parameter(ParameterSetName = 'Scope')] + [switch]$SkipPostChecks, + [Parameter(ParameterSetName = 'ModulePath')] + [Parameter(ParameterSetName = 'Scope')] + [switch]$SkipPesterTests, + [Parameter(ParameterSetName = 'ModulePath')] + [Parameter(ParameterSetName = 'Scope')] + [switch]$CleanModuleDir +) +if ($PSScriptRoot) { Push-Location "$PSScriptRoot\.."} +$psdpath = Get-Item "*.psd1" +if (-not $psdpath -or $psdpath.count -gt 1) { + if ($PSScriptRoot) { Pop-Location } + throw "Did not find a unique PSD file " +} +else { + $ModuleName = $psdpath.Name -replace '\.psd1$' , '' + $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) + $approvedVerbs = Get-Verb | Select-Object -ExpandProperty verb +} + +#pre-build checks - manifest found, files in it found, public functions and aliases loaded in it. Public functions correct. +if (-not $SkipPreChecks) { + + #Check files in the manifest are present + foreach ($file in $Settings.FileList) { + if (-not (Test-Path $file)) { + Write-host "##vso[task.logissue type=warning]File $file in the manifest file list is not present" -ForegroundColor yellow + } + } + + #Check files in public have Approved_verb-noun names and are 1 function using the file name as its name with + # its name and any alias names in the manifest; function should have a param block and help should be in an MD file + # We will want a regex which captures from "function verb-noun {" to its closing "}" + # need to match each { to a } - $reg is based on https://stackoverflow.com/questions/7898310/using-regex-to-balance-match-parenthesis + $reg = [Regex]::new(@" + function\s*[-\w]+\s*{ # The function name and opening '{' + (?: + [^{}]+ # Match all non-braces + | + (? { ) # Match '{', and capture into 'open' + | + (?<-open> } ) # Match '}', and delete the 'open' capture + )* + (?(open)(?!)) # Fails if 'open' stack isn't empty + } # Functions closing '}' +"@, 57) # 57 = compile,multi-line ignore case and white space. + $reg2 = [Regex]::new(@" + ^function\s*[-\w]+\s*{ # The function name and opening '{' + ( + \#.*?[\r\n]+ # single line comment + | # or + \s*<\#.*?\#> # <#comment block#> + | # or + \s*\[.*?\] # [attribute tags] + )* +"@, 57) # 57 = compile, multi-line, ignore case and white space. + foreach ($file in (Get-Item .\Public\*.ps1)) { + $name = $file.name -replace(".ps1","") + if ($name -notmatch ("(\w+)-\w+")) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$name in the public folder is not a verb-noun name"} + elseif ($Matches[1] -notin $approvedVerbs) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$name in the public folder does not start with an approved verb"} + if(-not ($Settings.FunctionsToExport -ceq $name)) { + Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]File $($file.name) in the public folder does not match an exported function in the manifest" + } + else { + $fileContent = Get-Content $file -Raw + $m = $reg.Matches($fileContent) + if ($m.Count -eq 0) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Could not find $name function in $($file.name)"; continue} + elseif ($m.Count -ge 2) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Multiple functions in $($item.name)"; Continue} + elseif ($m[0] -imatch "^\function\s" -and + $m[0] -cnotmatch "^\w+\s+$name") {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]function name does not match file name for $($file.name)"} + #$m[0] runs form the f of function to its final } -find the section up to param, check for aliases & comment-based help + $m2 = [regex]::Match($m[0],"^.*?param",17) # 17 = multi-line, ignnore case + if (-not $m2.Success) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]function $name has no param() block"} + else { + if ($m2.value -match "\[\s*Alias\(\s*.([\w-]+).\s*\)\s*\]") { + foreach ($a in ($Matches[1] -split '\s*,\s*')) { + $a = $a -replace "'","" -replace '"','' + if (-not ($Settings.AliasesToExport -eq $a)) { + Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Function $name has alias $a which is not in the manifest" + } + } + } + if ($m2.value -match "\.syopsis|\.Description|\.Example") { + Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Function $name appears to have comment based help." + } + } + } + } + + #Warn about functions which are exported but not found in public + $notFromPublic = $Settings.FunctionsToExport.where({-not (Test-Path ".\public\$_.ps1")}) + If ($notFromPublic) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Exported function(s) $($notFromPublic -join ', ') are not loaded from Public"} +} + +if ($PreCheckOnly) {return} + +#region build, determine module path if necessary, create target directory if necessary, copy files based on manifest, build help +try { + Write-verbose -verbose -Message 'Module build started' + + if (-not $ModulePath) { + if ($IsLinux -or $IsMacOS) {$ModulePathSeparator = ':' } + else {$ModulePathSeparator = ';' } + + if ($Scope -eq 'CurrentUser') { $dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile) } + else { $dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles) } + $ModulePath = ($env:PSModulePath -split $ModulePathSeparator).where({$_ -like "$dir*"},"First",1) + $ModulePath = Join-Path -Path $ModulePath -ChildPath $ModuleName + $ModulePath = Join-Path -Path $ModulePath -ChildPath $Settings.ModuleVersion + } + + # Clean-up / Create Directory + if (-not (Test-Path -Path $ModulePath)) { + $null = New-Item -Path $ModulePath -ItemType Directory -ErrorAction Stop + Write-verbose -verbose -Message ('Created module folder: "{0}"' -f $ModulePath) + } + elseif ($CleanModuleDir) { + Write-verbose -verbose "$ModulePath exists - cleaning before copy" + Get-ChildItem -Path $ModulePath | Remove-Item -Force -Recurse + } + Write-verbose -verbose -Message ('Copying files to "{0}"' -f $ModulePath) + $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru + foreach ($file in $Settings.FileList) { + if ($file -like '.\*') { + $dest = ($file -replace '\.\\',"$ModulePath\") + if (-not (Test-Path -PathType Container (Split-Path -Parent $dest))) { + $null = New-item -Type Directory -Path (Split-Path -Parent $dest) + } + } + else {$dest = $ModulePath } + Copy-Item -Path $file -Destination $dest -Force -Recurse + } + + if (Test-Path -PathType Container "mdHelp") { + if (-not (Get-Module -ListAvailable platyPS)) { + Write-Verbose -Verbose -Message ('Installing Platyps to build help files') + Install-Module -Name platyPS -Force -SkipPublisherCheck + } + Import-Module platyPS + Get-ChildItem .\mdHelp -Directory | ForEach-Object { + Write-verbose -verbose "Building help for language '$($_.Name)' " + $Null = New-ExternalHelp -Path $_.FullName -OutputPath (Join-Path $ModulePath $_.Name) -Force + } + } + #Leave module path for things which follow. + $env:PSNewBuildModule = $ModulePath +} +catch { + if ($PSScriptRoot) { Pop-Location } + throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) +} +finally { Write-verbose -verbose -Message 'Module installation end' + if (-not $outputFile -or -not (Test-Path $outputFile)) { + throw "Failed to create module" + } +} +#endregion + +Copy-Item -Path $ModulePath -Destination $env:Build_ArtifactStagingDirectory -Recurse + + +#Check valid command names, help, run script analyzer over the files in the module directory +if (-not $SkipPostChecks) { + try {$outputFile | Import-Module -Force -ErrorAction stop } + catch { + if ($PSScriptRoot) { Pop-Location } + throw "New module failed to load"} + $commands = Get-Command -Module $ModuleName -CommandType function,Cmdlet + $commands.where({$_.name -notmatch "(\w+)-\w+" -or $Matches[1] -notin $approvedVerbs}) | ForEach-Object { + Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$($_.name) does not meet the ApprovedVerb-Noun naming rules" + } + $helpless = $commands | Get-Help | Where-Object {$_.Synopsis -match "^\s+$($_.name)\s+\["} | Select-Object -ExpandProperty name + foreach ($command in $helpless ) { + Write-Warning -Message "On-line help is missing for $command" + } + if (-not (Get-Module -Name PSScriptAnalyzer -ListAvailable)) { + Install-Module -Name PSScriptAnalyzer -Force + } + Import-module -Name PSScriptAnalyzer + Write-Verbose -Verbose "Running script analyzer against '$ModulePath' " + $AnalyzerResults = Invoke-ScriptAnalyzer -Path $ModulePath -Recurse -ErrorAction SilentlyContinue + if ($AnalyzerResults) { + if (-not (Get-Module -Name ImportExcel -ListAvailable)) { + #ironically we use this to build import-excel Shouldn't need this there! + Write-Verbose -verbose 'Installing ImportExcel.' + Install-Module -Name ImportExcel -Force + } + $chartDef = New-ExcelChartDefinition -ChartType 'BarClustered' -Column 2 -Title "Script analysis" -LegendBold + $ExcelParams = @{ + Path = "$PWD\ScriptAnalyzer.xlsx" + WorksheetName = 'FullResults' + TableStyle = 'Medium6' + AutoSize = $true + Activate = $true + PivotTableDefinition = @{BreakDown = @{ + PivotData = @{RuleName = 'Count' } + PivotRows = 'Severity', 'RuleName' + PivotTotals = 'Rows' + PivotChartDefinition = $chartDef }} + } + Remove-Item -Path $ExcelParams['Path'] -ErrorAction SilentlyContinue + $AnalyzerResults | Export-Excel @ExcelParams + Write-Verbose -verbose "Analysis exported to '$($ExcelParams['Path'])'" + } +} + +#if there are test files, run pester (unless told not to) +if (-not $SkipPesterTests -and (Get-ChildItem -Recurse *.tests.ps1)) { + Import-Module -Force $outputFile + if (-not (Get-Module -ListAvailable pester | Where-Object -Property version -ge ([version]::new(4,4,1)))) { + Install-Module Pester -Force -SkipPublisherCheck + } + Import-Module Pester + if ($PSScriptRoot) { Pop-Location } + Invoke-Pester -OutputFile ("$PSScriptRoot\..\TestResultsPS{0}.xml" -f $PSVersionTable.PSVersion) +} +elseif ($PSScriptRoot) { Pop-Location } diff --git a/PublishToGallery.ps1 b/CI/PublishToGallery.ps1 similarity index 100% rename from PublishToGallery.ps1 rename to CI/PublishToGallery.ps1 diff --git a/CI/build.ps1 b/CI/build.ps1 index bff69de..dbfd870 100644 --- a/CI/build.ps1 +++ b/CI/build.ps1 @@ -10,11 +10,10 @@ param( [ValidateSet('CurrentUser', 'AllUsers')] [string] $Scope = 'CurrentUser', - [switch]$Passthru, - [switch]$Tests + [switch]$Passthru ) -if ($PSScriptRoot) { Push-Location $PSScriptRoot } +if ($PSScriptRoot) { Push-Location "$PSScriptRoot\.." } $psdpath = Get-Item "*.psd1" if (-not $psdpath -or $psdpath.count -gt 1) { @@ -76,5 +75,6 @@ catch { throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) } finally { + if ($PSScriptRoot) { Pop-Location } Write-Verbose -Message 'Module installation end' } \ No newline at end of file diff --git a/CI/pipeline.yml b/CI/pipeline.yml new file mode 100644 index 0000000..cdaa155 --- /dev/null +++ b/CI/pipeline.yml @@ -0,0 +1,89 @@ +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +trigger: + branches: + include: + - '*' + # - master + # - releases/* + paths: + exclude: + - README.md + - CHANGELOG.md + +jobs: + - job: Windows + pool: + vmImage: 'windows-latest' + + steps: + - powershell: 'Install-Module -Name Pester,PlatyPS,platyPS,PSScriptAnalyzer -Force -SkipPublisherCheck' + displayName: 'Update Modules' + - powershell: './CI/CI.ps1 -Test' + displayName: 'Check Build Check Pack Test' + + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true + + - powershell: './CI/CI.ps1 -Artifact' + displayName: 'Prepare Artifact' + - task: PublishPipelineArtifact@1 + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)' + artifact: 'Modules' + - powershell: './CI/CI.ps1 -Analyzer' + displayName: 'Invoke ScriptAnalyzer' + + - job: WindowsPSCore + pool: + vmImage: 'windows-latest' + + steps: + - pwsh: 'Install-Module -Name Pester -Force' + displayName: 'Update Pester' + - pwsh: './CI/CI.ps1 -Test' + displayName: 'Install and Test' + + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true + + - job: Ubuntu + pool: + vmImage: 'ubuntu-latest' + + steps: + - powershell: 'Install-Module -Name Pester -Force' + displayName: 'Update Pester' + - powershell: './CI/CI.ps1 -Test' + displayName: 'Install and Test' + + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true + + - job: macOS + pool: + vmImage: 'macOS-latest' + + steps: + - script: brew install mono-libgdiplus + displayName: 'Install mono-libgdiplus' + - powershell: 'Install-Module -Name Pester -Force' + displayName: 'Update Pester' + - powershell: './CI/CI.ps1 -Test' + displayName: 'Install and Test' + + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true \ No newline at end of file diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index c553e85..26f996f 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -153,6 +153,7 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5 '.\ImportExcel.psm1', '.\LICENSE.txt', '.\README.md', + '.\Plot.ps1' '.\en\ImportExcel-help.xml', '.\en\Strings.psd1', '.\Charting\Charting.ps1', diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index b070877..ec49780 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -7,11 +7,11 @@ if (-not $Strings) { try {[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")} catch {Write-Warning -Message $Strings.SystemDrawingAvaialable} -foreach ($directory in @('Public','Charting','InferData','Pivot')) { +foreach ($directory in @('Private', 'Public','Charting','InferData','Pivot')) { Get-ChildItem -Path "$PSScriptRoot\$directory\*.ps1" | ForEach-Object {. $_.FullName} } -. $PSScriptRoot\ArgumentCompletion.ps1 +# . $PSScriptRoot\ArgumentCompletion.ps1 if ($PSVersionTable.PSVersion.Major -ge 5) { . $PSScriptRoot\Plot.ps1 diff --git a/ArgumentCompletion.ps1 b/Private/ArgumentCompletion.ps1 similarity index 100% rename from ArgumentCompletion.ps1 rename to Private/ArgumentCompletion.ps1 From ce4270babde610d4d9d1229862527c45b236be39 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 00:53:56 +0000 Subject: [PATCH 02/32] experiment with new Azure pipeline --- CI/pipeline.yml | 58 +------------------------------------------------ 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index cdaa155..06b5a48 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -20,70 +20,14 @@ jobs: steps: - powershell: 'Install-Module -Name Pester,PlatyPS,platyPS,PSScriptAnalyzer -Force -SkipPublisherCheck' displayName: 'Update Modules' - - powershell: './CI/CI.ps1 -Test' + - powershell: './CI/PS-CI.ps1' displayName: 'Check Build Check Pack Test' - - - task: PublishTestResults@2 - inputs: - testResultsFormat: 'NUnit' - testResultsFiles: '**/TestResults*.xml' - failTaskOnFailedTests: true - - - powershell: './CI/CI.ps1 -Artifact' - displayName: 'Prepare Artifact' - task: PublishPipelineArtifact@1 inputs: targetPath: '$(Build.ArtifactStagingDirectory)' artifact: 'Modules' - - powershell: './CI/CI.ps1 -Analyzer' - displayName: 'Invoke ScriptAnalyzer' - - - job: WindowsPSCore - pool: - vmImage: 'windows-latest' - - steps: - - pwsh: 'Install-Module -Name Pester -Force' - displayName: 'Update Pester' - - pwsh: './CI/CI.ps1 -Test' - displayName: 'Install and Test' - - task: PublishTestResults@2 inputs: testResultsFormat: 'NUnit' testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - - - job: Ubuntu - pool: - vmImage: 'ubuntu-latest' - - steps: - - powershell: 'Install-Module -Name Pester -Force' - displayName: 'Update Pester' - - powershell: './CI/CI.ps1 -Test' - displayName: 'Install and Test' - - - task: PublishTestResults@2 - inputs: - testResultsFormat: 'NUnit' - testResultsFiles: '**/TestResults*.xml' - failTaskOnFailedTests: true - - - job: macOS - pool: - vmImage: 'macOS-latest' - - steps: - - script: brew install mono-libgdiplus - displayName: 'Install mono-libgdiplus' - - powershell: 'Install-Module -Name Pester -Force' - displayName: 'Update Pester' - - powershell: './CI/CI.ps1 -Test' - displayName: 'Install and Test' - - - task: PublishTestResults@2 - inputs: - testResultsFormat: 'NUnit' - testResultsFiles: '**/TestResults*.xml' - failTaskOnFailedTests: true \ No newline at end of file From dec579a966f45723efd27e0dbc77ffc1a8bbbf2b Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 00:58:22 +0000 Subject: [PATCH 03/32] update CI for moved install.ps1 --- CI/CI.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/CI.ps1 b/CI/CI.ps1 index 43b92ce..1170c5c 100644 --- a/CI/CI.ps1 +++ b/CI/CI.ps1 @@ -95,7 +95,7 @@ if (-not $VersionFilePath) { '[Info] Testing On:' Get-EnvironmentInfo '[Progress] Installing Module.' - . .\Install.ps1 + . .\ci\Install.ps1 '[Progress] Invoking Pester.' Invoke-Pester -OutputFile ('TestResultsPS{0}.xml' -f $PSVersionTable.PSVersion) } From a9b1a128cd58b653a83dedd586c20af18e09af1d Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 01:24:22 +0000 Subject: [PATCH 04/32] Refine Pipeline messages / script analysis --- CI/PS-CI.ps1 | 24 ++++++++++++------------ CI/pipeline.yml | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 8c498c3..8d5e480 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -131,13 +131,13 @@ try { # Clean-up / Create Directory if (-not (Test-Path -Path $ModulePath)) { $null = New-Item -Path $ModulePath -ItemType Directory -ErrorAction Stop - Write-verbose -verbose -Message ('Created module folder: "{0}"' -f $ModulePath) + 'Created module folder: "{0}"' -f $ModulePath } elseif ($CleanModuleDir) { - Write-verbose -verbose "$ModulePath exists - cleaning before copy" + '{0} exists - cleaning before copy' -f $ModulePath Get-ChildItem -Path $ModulePath | Remove-Item -Force -Recurse } - Write-verbose -verbose -Message ('Copying files to "{0}"' -f $ModulePath) + 'Copying files to: "{0}"' -f $ModulePath $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru foreach ($file in $Settings.FileList) { if ($file -like '.\*') { @@ -152,12 +152,12 @@ try { if (Test-Path -PathType Container "mdHelp") { if (-not (Get-Module -ListAvailable platyPS)) { - Write-Verbose -Verbose -Message ('Installing Platyps to build help files') + 'Installing Platyps to build help files' Install-Module -Name platyPS -Force -SkipPublisherCheck } Import-Module platyPS Get-ChildItem .\mdHelp -Directory | ForEach-Object { - Write-verbose -verbose "Building help for language '$($_.Name)' " + 'Building help for language ''{0}''.' -f $_.Name $Null = New-ExternalHelp -Path $_.FullName -OutputPath (Join-Path $ModulePath $_.Name) -Force } } @@ -168,14 +168,13 @@ catch { if ($PSScriptRoot) { Pop-Location } throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) } -finally { Write-verbose -verbose -Message 'Module installation end' - if (-not $outputFile -or -not (Test-Path $outputFile)) { +finally { if (-not $outputFile -or -not (Test-Path $outputFile)) { throw "Failed to create module" } } #endregion -Copy-Item -Path $ModulePath -Destination $env:Build_ArtifactStagingDirectory -Recurse +Copy-Item -Path (split-path -Parent $ModulePath) -Destination $env:Build_ArtifactStagingDirectory -Recurse #Check valid command names, help, run script analyzer over the files in the module directory @@ -183,14 +182,15 @@ if (-not $SkipPostChecks) { try {$outputFile | Import-Module -Force -ErrorAction stop } catch { if ($PSScriptRoot) { Pop-Location } - throw "New module failed to load"} + throw "New module failed to load" + } $commands = Get-Command -Module $ModuleName -CommandType function,Cmdlet $commands.where({$_.name -notmatch "(\w+)-\w+" -or $Matches[1] -notin $approvedVerbs}) | ForEach-Object { Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$($_.name) does not meet the ApprovedVerb-Noun naming rules" } $helpless = $commands | Get-Help | Where-Object {$_.Synopsis -match "^\s+$($_.name)\s+\["} | Select-Object -ExpandProperty name foreach ($command in $helpless ) { - Write-Warning -Message "On-line help is missing for $command" + '##vso[task.logissue type=Warning]On-line help is missing for {0}.' -f $command } if (-not (Get-Module -Name PSScriptAnalyzer -ListAvailable)) { Install-Module -Name PSScriptAnalyzer -Force @@ -206,7 +206,7 @@ if (-not $SkipPostChecks) { } $chartDef = New-ExcelChartDefinition -ChartType 'BarClustered' -Column 2 -Title "Script analysis" -LegendBold $ExcelParams = @{ - Path = "$PWD\ScriptAnalyzer.xlsx" + Path = "$env:Build_ArtifactStagingDirectory\ScriptAnalyzer.xlsx" WorksheetName = 'FullResults' TableStyle = 'Medium6' AutoSize = $true @@ -219,7 +219,7 @@ if (-not $SkipPostChecks) { } Remove-Item -Path $ExcelParams['Path'] -ErrorAction SilentlyContinue $AnalyzerResults | Export-Excel @ExcelParams - Write-Verbose -verbose "Analysis exported to '$($ExcelParams['Path'])'" + "##vso[task.uploadfile]$($ExcelParams['Path'])" } } diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 06b5a48..92170e1 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -22,12 +22,12 @@ jobs: displayName: 'Update Modules' - powershell: './CI/PS-CI.ps1' displayName: 'Check Build Check Pack Test' - - task: PublishPipelineArtifact@1 - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)' - artifact: 'Modules' - task: PublishTestResults@2 inputs: testResultsFormat: 'NUnit' testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true + - task: PublishPipelineArtifact@1 + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' + artifact: 'Module' \ No newline at end of file From 469586e94a4ba8db118730c0eba743e868268294 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 01:26:21 +0000 Subject: [PATCH 05/32] retry previous --- CI/pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 92170e1..06b5a48 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -22,12 +22,12 @@ jobs: displayName: 'Update Modules' - powershell: './CI/PS-CI.ps1' displayName: 'Check Build Check Pack Test' + - task: PublishPipelineArtifact@1 + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)' + artifact: 'Modules' - task: PublishTestResults@2 inputs: testResultsFormat: 'NUnit' testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - - task: PublishPipelineArtifact@1 - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' - artifact: 'Module' \ No newline at end of file From 2bcb332ec73b074b59ff68123276d2fc62e6daaa Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 01:35:13 +0000 Subject: [PATCH 06/32] try better script analysis publishing --- CI/PS-CI.ps1 | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 8d5e480..9c8f5ac 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -43,7 +43,7 @@ if (-not $SkipPreChecks) { #Check files in the manifest are present foreach ($file in $Settings.FileList) { if (-not (Test-Path $file)) { - Write-host "##vso[task.logissue type=warning]File $file in the manifest file list is not present" -ForegroundColor yellow + "##vso[task.logissue type=warning]File $file in the manifest file list is not present" } } @@ -63,44 +63,34 @@ if (-not $SkipPreChecks) { (?(open)(?!)) # Fails if 'open' stack isn't empty } # Functions closing '}' "@, 57) # 57 = compile,multi-line ignore case and white space. - $reg2 = [Regex]::new(@" - ^function\s*[-\w]+\s*{ # The function name and opening '{' - ( - \#.*?[\r\n]+ # single line comment - | # or - \s*<\#.*?\#> # <#comment block#> - | # or - \s*\[.*?\] # [attribute tags] - )* -"@, 57) # 57 = compile, multi-line, ignore case and white space. foreach ($file in (Get-Item .\Public\*.ps1)) { $name = $file.name -replace(".ps1","") - if ($name -notmatch ("(\w+)-\w+")) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$name in the public folder is not a verb-noun name"} - elseif ($Matches[1] -notin $approvedVerbs) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$name in the public folder does not start with an approved verb"} + if ($name -notmatch ("(\w+)-\w+")) {"##vso[task.logissue type=Warning]$name in the public folder is not a verb-noun name"} + elseif ($Matches[1] -notin $approvedVerbs) {"##vso[task.logissue type=Warning]$name in the public folder does not start with an approved verb"} if(-not ($Settings.FunctionsToExport -ceq $name)) { - Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]File $($file.name) in the public folder does not match an exported function in the manifest" + "##vso[task.logissue type=Warning]File $($file.name) in the public folder does not match an exported function in the manifest" } else { $fileContent = Get-Content $file -Raw $m = $reg.Matches($fileContent) - if ($m.Count -eq 0) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Could not find $name function in $($file.name)"; continue} - elseif ($m.Count -ge 2) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Multiple functions in $($item.name)"; Continue} + if ($m.Count -eq 0) {"##vso[task.logissue type=Warning]Could not find $name function in $($file.name)"; continue} + elseif ($m.Count -ge 2) {"##vso[task.logissue type=Warning]Multiple functions in $($item.name)"; Continue} elseif ($m[0] -imatch "^\function\s" -and - $m[0] -cnotmatch "^\w+\s+$name") {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]function name does not match file name for $($file.name)"} + $m[0] -cnotmatch "^\w+\s+$name") {"##vso[task.logissue type=Warning]function name does not match file name for $($file.name)"} #$m[0] runs form the f of function to its final } -find the section up to param, check for aliases & comment-based help $m2 = [regex]::Match($m[0],"^.*?param",17) # 17 = multi-line, ignnore case - if (-not $m2.Success) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]function $name has no param() block"} + if (-not $m2.Success) {"##vso[task.logissue type=Warning]function $name has no param() block"} else { if ($m2.value -match "\[\s*Alias\(\s*.([\w-]+).\s*\)\s*\]") { foreach ($a in ($Matches[1] -split '\s*,\s*')) { $a = $a -replace "'","" -replace '"','' if (-not ($Settings.AliasesToExport -eq $a)) { - Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Function $name has alias $a which is not in the manifest" + "##vso[task.logissue type=Warning]Function $name has alias $a which is not in the manifest" } } } if ($m2.value -match "\.syopsis|\.Description|\.Example") { - Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Function $name appears to have comment based help." + "##vso[task.logissue type=Warning]Function $name appears to have comment based help." } } } @@ -108,16 +98,14 @@ if (-not $SkipPreChecks) { #Warn about functions which are exported but not found in public $notFromPublic = $Settings.FunctionsToExport.where({-not (Test-Path ".\public\$_.ps1")}) - If ($notFromPublic) {Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]Exported function(s) $($notFromPublic -join ', ') are not loaded from Public"} + If ($notFromPublic) {"##vso[task.logissue type=Warning]Exported function(s) $($notFromPublic -join ', ') are not loaded from Public"} } if ($PreCheckOnly) {return} #region build, determine module path if necessary, create target directory if necessary, copy files based on manifest, build help try { - Write-verbose -verbose -Message 'Module build started' - - if (-not $ModulePath) { + if (-not $ModulePath) { if ($IsLinux -or $IsMacOS) {$ModulePathSeparator = ':' } else {$ModulePathSeparator = ';' } @@ -186,7 +174,7 @@ if (-not $SkipPostChecks) { } $commands = Get-Command -Module $ModuleName -CommandType function,Cmdlet $commands.where({$_.name -notmatch "(\w+)-\w+" -or $Matches[1] -notin $approvedVerbs}) | ForEach-Object { - Write-Host -ForegroundColor yellow "##vso[task.logissue type=Warning]$($_.name) does not meet the ApprovedVerb-Noun naming rules" + "##vso[task.logissue type=Warning]$($_.name) does not meet the ApprovedVerb-Noun naming rules" } $helpless = $commands | Get-Help | Where-Object {$_.Synopsis -match "^\s+$($_.name)\s+\["} | Select-Object -ExpandProperty name foreach ($command in $helpless ) { @@ -196,12 +184,12 @@ if (-not $SkipPostChecks) { Install-Module -Name PSScriptAnalyzer -Force } Import-module -Name PSScriptAnalyzer - Write-Verbose -Verbose "Running script analyzer against '$ModulePath' " + "Running script analyzer against '{0}' " -f $ModulePath $AnalyzerResults = Invoke-ScriptAnalyzer -Path $ModulePath -Recurse -ErrorAction SilentlyContinue if ($AnalyzerResults) { if (-not (Get-Module -Name ImportExcel -ListAvailable)) { #ironically we use this to build import-excel Shouldn't need this there! - Write-Verbose -verbose 'Installing ImportExcel.' + 'Installing ImportExcel.' Install-Module -Name ImportExcel -Force } $chartDef = New-ExcelChartDefinition -ChartType 'BarClustered' -Column 2 -Title "Script analysis" -LegendBold @@ -219,6 +207,7 @@ if (-not $SkipPostChecks) { } Remove-Item -Path $ExcelParams['Path'] -ErrorAction SilentlyContinue $AnalyzerResults | Export-Excel @ExcelParams + "Try to uploadfile$($ExcelParams['Path'])" "##vso[task.uploadfile]$($ExcelParams['Path'])" } } From 6be0961d17d40f1b009956aaee6272ce916af351 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 01:47:43 +0000 Subject: [PATCH 07/32] Improve Artifacts --- CI/PS-CI.ps1 | 4 ++-- CI/pipeline.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 9c8f5ac..ad6f3c4 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -207,8 +207,8 @@ if (-not $SkipPostChecks) { } Remove-Item -Path $ExcelParams['Path'] -ErrorAction SilentlyContinue $AnalyzerResults | Export-Excel @ExcelParams - "Try to uploadfile$($ExcelParams['Path'])" - "##vso[task.uploadfile]$($ExcelParams['Path'])" + "Try to uploadfile {0}" -f $ExcelParams['Path'] + "##vso[task.uploadfile]{0}" -f $ExcelParams['Path'] } } diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 06b5a48..76a80ca 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -22,12 +22,12 @@ jobs: displayName: 'Update Modules' - powershell: './CI/PS-CI.ps1' displayName: 'Check Build Check Pack Test' - - task: PublishPipelineArtifact@1 - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)' - artifact: 'Modules' - task: PublishTestResults@2 inputs: testResultsFormat: 'NUnit' testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true + - task: PublishPipelineArtifact@1 + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' + artifact: 'ImportExcel' From 4f17fc36bc172276609ff7bcfbc7875d11840829 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 01:50:54 +0000 Subject: [PATCH 08/32] retry --- CI/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 76a80ca..5fd00b6 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -29,5 +29,5 @@ jobs: failTaskOnFailedTests: true - task: PublishPipelineArtifact@1 inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' + targetPath: '$(Build.ArtifactStagingDirectory)' artifact: 'ImportExcel' From 2e56012408cd7a59964a09e6dfea86cc997a960d Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 01:52:25 +0000 Subject: [PATCH 09/32] retry 2 --- CI/pipeline.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 5fd00b6..23c0c76 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -28,6 +28,6 @@ jobs: testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - task: PublishPipelineArtifact@1 - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)' - artifact: 'ImportExcel' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)' + artifact: 'ImportExcel' From 300960fd9b5a4ed282fdc93652b581ab4d6a94f6 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 02:19:56 +0000 Subject: [PATCH 10/32] remove more aliases from examples --- CI/pipeline.yml | 2 +- Examples/Index - Music.ps1 | 2 +- Examples/SQL+FillColumns+Pivot/Example.ps1 | 2 +- Examples/SQL+FillColumns+Pivot/Example2.ps1 | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 23c0c76..340fcff 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -29,5 +29,5 @@ jobs: failTaskOnFailedTests: true - task: PublishPipelineArtifact@1 inputs: - targetPath: '$(Build.ArtifactStagingDirectory)' + targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' artifact: 'ImportExcel' diff --git a/Examples/Index - Music.ps1 b/Examples/Index - Music.ps1 index f895505..7d83c30 100644 --- a/Examples/Index - Music.ps1 +++ b/Examples/Index - Music.ps1 @@ -22,7 +22,7 @@ $ws = $excel.Music Set-ExcelRow -Worksheet $ws -Row 1 -Value {($worksheet.cells[$row,$column].value -replace '^SYSTEM\.','') -replace '^MEDIA\.|^AUDIO\.|^MUSIC\.','' } Set-ExcelColumn -Worksheet $ws -Column 5 -NumberFormat 'mm:ss.0' -StartRow 2 -Value {$worksheet.cells[$row,$column].value / 864000000000 } Write-Verbose -Message ("Cells Reset: " + $stopwatch.Elapsed.TotalSeconds) -Set-Column -Worksheet $ws -Column 6 -NumberFormat '#.#,,"MB"' +Set-ExcelColumn -Worksheet $ws -Column 6 -NumberFormat '#.#,,"MB"' Set-ExcelColumn -Worksheet $ws -Column 7 -NumberFormat '0.0,"KHz"' $ws.Cells[$ws.Dimension].AutoFitColumns() diff --git a/Examples/SQL+FillColumns+Pivot/Example.ps1 b/Examples/SQL+FillColumns+Pivot/Example.ps1 index 702674f..78a54d5 100644 --- a/Examples/SQL+FillColumns+Pivot/Example.ps1 +++ b/Examples/SQL+FillColumns+Pivot/Example.ps1 @@ -83,6 +83,6 @@ Set-ExcelColumn -ExcelPackage $Excel -WorkSheetname "Winners" -column 7 -Head 6..7 | ForEach-Object { Set-ExcelRange -Address $Excel.Workbook.Worksheets["Winners"].column($_) -NumberFormat "0.0%" -AutoFit } #Define a chart to show the relationship of lest on an XY Grid, create the ranges required in the, add the chart and show the file in Excel in excel after saving. -$chart = New-ExcelChart -NoLegend -ChartType XYScatter -XRange WinsToFast -YRange WinsToPoles -ShowCategory -Column 7 -Width 2000 -Height 700 +$chart = New-ExcelChartDefinition -NoLegend -ChartType XYScatter -XRange WinsToFast -YRange WinsToPoles -ShowCategory -Column 7 -Width 2000 -Height 700 Export-Excel -ExcelPackage $Excel -WorkSheetname "Winners" -AutoNameRange -ExcelChartDefinition $chart -Show diff --git a/Examples/SQL+FillColumns+Pivot/Example2.ps1 b/Examples/SQL+FillColumns+Pivot/Example2.ps1 index 548c668..65b8acc 100644 --- a/Examples/SQL+FillColumns+Pivot/Example2.ps1 +++ b/Examples/SQL+FillColumns+Pivot/Example2.ps1 @@ -15,10 +15,10 @@ $Excel = Send-SQLDataToExcel -SQL $sql -Session $session -path .\demo3.xlsx - $ws = $Excel.Workbook.Worksheets["Winners"] -Set-Row -Worksheet $ws -Heading "Average" -Value {"=Average($columnName`2:$columnName$endrow)"} -NumberFormat "0.0" -Bold -Set-Column -Worksheet $ws -Heading "WinsToPoles" -Value {"=D$row/C$row"} -Column 6 -AutoSize -AutoNameRange -Set-Column -Worksheet $ws -Heading "WinsToFast" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange +Set-ExcelRow -Worksheet $ws -Heading "Average" -Value {"=Average($columnName`2:$columnName$endrow)"} -NumberFormat "0.0" -Bold +Set-ExcelColumn -Worksheet $ws -Heading "WinsToPoles" -Value {"=D$row/C$row"} -Column 6 -AutoSize -AutoNameRange +Set-ExcelColumn -Worksheet $ws -Heading "WinsToFast" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange Set-ExcelRange -Worksheet $ws -Range "F2:G50" -NumberFormat "0.0%" -$chart = New-ExcelChart -NoLegend -ChartType XYScatter -XRange WinsToFast -YRange WinsToPoles -Column 7 -Width 2000 -Height 700 -Title "Poles vs fastlaps" +$chart = New-ExcelChartDefinition -NoLegend -ChartType XYScatter -XRange WinsToFast -YRange WinsToPoles -Column 7 -Width 2000 -Height 700 -Title "Poles vs fastlaps" Export-Excel -ExcelPackage $Excel -WorkSheetname "Winners" -ExcelChartDefinition $chart -Show \ No newline at end of file From fb4fb57751a2cf36e41c89794b3f3bf87e5ef6e6 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 10:12:33 +0000 Subject: [PATCH 11/32] refine the pipeline --- CI/PS-CI.ps1 | 83 +++++++++++++++++++++++++++++++++---------------- CI/pipeline.yml | 48 ++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 30 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index ad6f3c4..229ab89 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -23,18 +23,36 @@ Param( [switch]$SkipPesterTests, [Parameter(ParameterSetName = 'ModulePath')] [Parameter(ParameterSetName = 'Scope')] + [switch]$SkipHelp, + [Parameter(ParameterSetName = 'ModulePath')] + [Parameter(ParameterSetName = 'Scope')] [switch]$CleanModuleDir ) -if ($PSScriptRoot) { Push-Location "$PSScriptRoot\.."} +Function Show-warning { + param( + [Parameter(Position=0,ValueFromPipeline=$true)] + $message + ) + process { + write-output "##vso[task.logissue type=warning]File $message" + $message >> $script:warningfile + } +} + +if ($PSScriptRoot) { + $workingdir = Split-Path -Parent $PSScriptRoot + Push-Location $workingdir +} $psdpath = Get-Item "*.psd1" if (-not $psdpath -or $psdpath.count -gt 1) { if ($PSScriptRoot) { Pop-Location } throw "Did not find a unique PSD file " } else { - $ModuleName = $psdpath.Name -replace '\.psd1$' , '' - $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) - $approvedVerbs = Get-Verb | Select-Object -ExpandProperty verb + $ModuleName = $psdpath.Name -replace '\.psd1$' , '' + $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) + $approvedVerbs = Get-Verb | Select-Object -ExpandProperty verb + $script:warningfile = Join-Path -Path $pwd -ChildPath "warnings.txt" } #pre-build checks - manifest found, files in it found, public functions and aliases loaded in it. Public functions correct. @@ -43,7 +61,7 @@ if (-not $SkipPreChecks) { #Check files in the manifest are present foreach ($file in $Settings.FileList) { if (-not (Test-Path $file)) { - "##vso[task.logissue type=warning]File $file in the manifest file list is not present" + show-warning "File $file in the manifest file list is not present" } } @@ -65,32 +83,32 @@ if (-not $SkipPreChecks) { "@, 57) # 57 = compile,multi-line ignore case and white space. foreach ($file in (Get-Item .\Public\*.ps1)) { $name = $file.name -replace(".ps1","") - if ($name -notmatch ("(\w+)-\w+")) {"##vso[task.logissue type=Warning]$name in the public folder is not a verb-noun name"} - elseif ($Matches[1] -notin $approvedVerbs) {"##vso[task.logissue type=Warning]$name in the public folder does not start with an approved verb"} + if ($name -notmatch ("(\w+)-\w+")) {Show-Warning "$name in the public folder is not a verb-noun name"} + elseif ($Matches[1] -notin $approvedVerbs) {Show-Warning "$name in the public folder does not start with an approved verb"} if(-not ($Settings.FunctionsToExport -ceq $name)) { - "##vso[task.logissue type=Warning]File $($file.name) in the public folder does not match an exported function in the manifest" + Show-Warning ('File {0} in the public folder does not match an exported function in the manifest' -f $file.name) } else { $fileContent = Get-Content $file -Raw $m = $reg.Matches($fileContent) - if ($m.Count -eq 0) {"##vso[task.logissue type=Warning]Could not find $name function in $($file.name)"; continue} - elseif ($m.Count -ge 2) {"##vso[task.logissue type=Warning]Multiple functions in $($item.name)"; Continue} + if ($m.Count -eq 0) {Show-Warning ('Could not find {0} function in {1}' -f $name, $file.name); continue} + elseif ($m.Count -ge 2) {Show-Warning ('Multiple functions in {0}' -f $item.name) ; Continue} elseif ($m[0] -imatch "^\function\s" -and - $m[0] -cnotmatch "^\w+\s+$name") {"##vso[task.logissue type=Warning]function name does not match file name for $($file.name)"} + $m[0] -cnotmatch "^\w+\s+$name") {Show-Warning ('function name does not match file name for {0}' -f $file.name)} #$m[0] runs form the f of function to its final } -find the section up to param, check for aliases & comment-based help $m2 = [regex]::Match($m[0],"^.*?param",17) # 17 = multi-line, ignnore case - if (-not $m2.Success) {"##vso[task.logissue type=Warning]function $name has no param() block"} + if (-not $m2.Success) {Show-Warning "function $name has no param() block"} else { if ($m2.value -match "\[\s*Alias\(\s*.([\w-]+).\s*\)\s*\]") { foreach ($a in ($Matches[1] -split '\s*,\s*')) { $a = $a -replace "'","" -replace '"','' if (-not ($Settings.AliasesToExport -eq $a)) { - "##vso[task.logissue type=Warning]Function $name has alias $a which is not in the manifest" + Show-Warning "Function $name has alias $a which is not in the manifest" } } } if ($m2.value -match "\.syopsis|\.Description|\.Example") { - "##vso[task.logissue type=Warning]Function $name appears to have comment based help." + Show-Warning "Function $name appears to have comment based help." } } } @@ -98,7 +116,7 @@ if (-not $SkipPreChecks) { #Warn about functions which are exported but not found in public $notFromPublic = $Settings.FunctionsToExport.where({-not (Test-Path ".\public\$_.ps1")}) - If ($notFromPublic) {"##vso[task.logissue type=Warning]Exported function(s) $($notFromPublic -join ', ') are not loaded from Public"} + If ($notFromPublic) {Show-Warning ('Exported function(s) {0} are not loaded from the Public folder' -f ($notFromPublic -join ', '))} } if ($PreCheckOnly) {return} @@ -138,14 +156,14 @@ try { Copy-Item -Path $file -Destination $dest -Force -Recurse } - if (Test-Path -PathType Container "mdHelp") { + if (Test-Path -PathType Container "mdHelp" -and not $SkipHelp) { if (-not (Get-Module -ListAvailable platyPS)) { 'Installing Platyps to build help files' Install-Module -Name platyPS -Force -SkipPublisherCheck } - Import-Module platyPS + $platypsInfo = Import-Module platyPS -PassThru -force | Format-Table name,version -HideTableHeaders | Out-String Get-ChildItem .\mdHelp -Directory | ForEach-Object { - 'Building help for language ''{0}''.' -f $_.Name + 'Building help for language ''{0}'', using {1} .' -f $_.Name,$platypsInfo $Null = New-ExternalHelp -Path $_.FullName -OutputPath (Join-Path $ModulePath $_.Name) -Force } } @@ -164,7 +182,6 @@ finally { if (-not $outputFile -or -not (Test-Path $outputFile)) { Copy-Item -Path (split-path -Parent $ModulePath) -Destination $env:Build_ArtifactStagingDirectory -Recurse - #Check valid command names, help, run script analyzer over the files in the module directory if (-not $SkipPostChecks) { try {$outputFile | Import-Module -Force -ErrorAction stop } @@ -174,17 +191,17 @@ if (-not $SkipPostChecks) { } $commands = Get-Command -Module $ModuleName -CommandType function,Cmdlet $commands.where({$_.name -notmatch "(\w+)-\w+" -or $Matches[1] -notin $approvedVerbs}) | ForEach-Object { - "##vso[task.logissue type=Warning]$($_.name) does not meet the ApprovedVerb-Noun naming rules" + Show-Warning ('{0} does not meet the ApprovedVerb-Noun naming rules' -f $_.name) } $helpless = $commands | Get-Help | Where-Object {$_.Synopsis -match "^\s+$($_.name)\s+\["} | Select-Object -ExpandProperty name foreach ($command in $helpless ) { - '##vso[task.logissue type=Warning]On-line help is missing for {0}.' -f $command + Show-Warning ('On-line help is missing for {0}.' -f $command) } if (-not (Get-Module -Name PSScriptAnalyzer -ListAvailable)) { Install-Module -Name PSScriptAnalyzer -Force } - Import-module -Name PSScriptAnalyzer - "Running script analyzer against '{0}' " -f $ModulePath + $PSSAInfo = Import-module -Name PSScriptAnalyzer -PassThru -force | Format-Table name,version -HideTableHeaders | Out-String + "Running {1} against '{0}' " -f $ModulePath , $PSSAInfo $AnalyzerResults = Invoke-ScriptAnalyzer -Path $ModulePath -Recurse -ErrorAction SilentlyContinue if ($AnalyzerResults) { if (-not (Get-Module -Name ImportExcel -ListAvailable)) { @@ -194,7 +211,7 @@ if (-not $SkipPostChecks) { } $chartDef = New-ExcelChartDefinition -ChartType 'BarClustered' -Column 2 -Title "Script analysis" -LegendBold $ExcelParams = @{ - Path = "$env:Build_ArtifactStagingDirectory\ScriptAnalyzer.xlsx" + Path = (Join-Path $pwd 'ScriptAnalyzer.xlsx') WorksheetName = 'FullResults' TableStyle = 'Medium6' AutoSize = $true @@ -207,11 +224,22 @@ if (-not $SkipPostChecks) { } Remove-Item -Path $ExcelParams['Path'] -ErrorAction SilentlyContinue $AnalyzerResults | Export-Excel @ExcelParams - "Try to uploadfile {0}" -f $ExcelParams['Path'] - "##vso[task.uploadfile]{0}" -f $ExcelParams['Path'] + if (Test-Path $ExcelParams['Path']) { + "Try to uploadfile {0}" -f $ExcelParams['Path'] + "##vso[task.uploadfile]{0}" -f $ExcelParams['Path'] + } } } +if (Test-Path $ExcelParams['Path']) { + "Try to uploadfile {0}" -f $ExcelParams['Path'] + "##vso[task.uploadfile]{0}" -f $ExcelParams['Path'] +} +if (Test-Path $script:warningfile) { + "Try to uploadfile {0}" -f $script:warningfile + "##vso[task.uploadfile]{0}" -f $script:warningfile +} + #if there are test files, run pester (unless told not to) if (-not $SkipPesterTests -and (Get-ChildItem -Recurse *.tests.ps1)) { Import-Module -Force $outputFile @@ -219,7 +247,8 @@ if (-not $SkipPesterTests -and (Get-ChildItem -Recurse *.tests.ps1)) { Install-Module Pester -Force -SkipPublisherCheck } Import-Module Pester + $PesterOutputPath = Join-Path $pwd -ChildPath ('TestResultsPS{0}.xml' -f $PSVersionTable.PSVersion) if ($PSScriptRoot) { Pop-Location } - Invoke-Pester -OutputFile ("$PSScriptRoot\..\TestResultsPS{0}.xml" -f $PSVersionTable.PSVersion) + Invoke-Pester -OutputFile $PesterOutputPath } elseif ($PSScriptRoot) { Pop-Location } diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 340fcff..69de45b 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -1,4 +1,3 @@ -# Add steps that build, run tests, deploy, and more: # https://aka.ms/yaml trigger: @@ -16,9 +15,8 @@ jobs: - job: Windows pool: vmImage: 'windows-latest' - steps: - - powershell: 'Install-Module -Name Pester,PlatyPS,platyPS,PSScriptAnalyzer -Force -SkipPublisherCheck' + - powershell: 'Install-Module -Name Pester -Force -SkipPublisherCheck' displayName: 'Update Modules' - powershell: './CI/PS-CI.ps1' displayName: 'Check Build Check Pack Test' @@ -31,3 +29,47 @@ jobs: inputs: targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' artifact: 'ImportExcel' + + - job: WindowsPSCore + pool: + vmImage: 'windows-latest' + steps: + - pwsh: 'Install-Module -Name Pester -Force' + displayName: 'Update Pester' + - pwsh: .\ci\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' + displayName: 'Install and Test' + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true + + - job: Ubuntu + pool: + vmImage: 'ubuntu-latest' + steps: + - powershell: 'Install-Module -Name Pester -Force' + displayName: 'Update Pester' + - powershell: '.\ci\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks ' + displayName: 'Install and Test' + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true + + - job: macOS + pool: + vmImage: 'macOS-latest' + steps: + - script: brew install mono-libgdiplus + displayName: 'Install mono-libgdiplus' + - powershell: 'Install-Module -Name Pester -Force' + displayName: 'Update Pester' + - powershell: './CI/CI.ps1 -Test' + displayName: 'Install and Test' + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '**/TestResults*.xml' + failTaskOnFailedTests: true From f5a29b45df9ae97e9cce21b43efa5bcc49a1fcee Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 10:19:21 +0000 Subject: [PATCH 12/32] retry previous --- CI/PS-CI.ps1 | 2 +- CI/pipeline.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 229ab89..182fdf5 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -156,7 +156,7 @@ try { Copy-Item -Path $file -Destination $dest -Force -Recurse } - if (Test-Path -PathType Container "mdHelp" -and not $SkipHelp) { + if ((Test-Path -PathType Container "mdHelp") -and -not $SkipHelp) { if (-not (Get-Module -ListAvailable platyPS)) { 'Installing Platyps to build help files' Install-Module -Name platyPS -Force -SkipPublisherCheck diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 69de45b..21675c9 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -12,7 +12,7 @@ trigger: - CHANGELOG.md jobs: - - job: Windows + - job: 'Windows powershell all options' pool: vmImage: 'windows-latest' steps: @@ -30,7 +30,7 @@ jobs: targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' artifact: 'ImportExcel' - - job: WindowsPSCore + - job: 'PowerShell Core on Windows Build and Pester only' pool: vmImage: 'windows-latest' steps: @@ -44,7 +44,7 @@ jobs: testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - - job: Ubuntu + - job: 'Ubuntu Build and Pester Only' pool: vmImage: 'ubuntu-latest' steps: @@ -58,7 +58,7 @@ jobs: testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - - job: macOS + - job: 'macOS Build and Pester Only' pool: vmImage: 'macOS-latest' steps: From d45d90c8a12990d0a99bb90173759f349e21fc20 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 10:22:35 +0000 Subject: [PATCH 13/32] Retry previous --- CI/pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 21675c9..890e92b 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -12,7 +12,7 @@ trigger: - CHANGELOG.md jobs: - - job: 'Windows powershell all options' + - job: 'Windows_PowerShell_all_options' pool: vmImage: 'windows-latest' steps: @@ -30,7 +30,7 @@ jobs: targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' artifact: 'ImportExcel' - - job: 'PowerShell Core on Windows Build and Pester only' + - job: 'PowerShell_Core_on_Windows_Build_and_Pester_only' pool: vmImage: 'windows-latest' steps: @@ -44,7 +44,7 @@ jobs: testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - - job: 'Ubuntu Build and Pester Only' + - job: 'Ubuntu_Build_and_Pester_Only' pool: vmImage: 'ubuntu-latest' steps: @@ -58,7 +58,7 @@ jobs: testResultsFiles: '**/TestResults*.xml' failTaskOnFailedTests: true - - job: 'macOS Build and Pester Only' + - job: 'macOS_Build_and_Pester_Only' pool: vmImage: 'macOS-latest' steps: From 5220e7c188bccf606fde2885ba402400a418270a Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 10:54:19 +0000 Subject: [PATCH 14/32] retry previous --- CI/pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 890e92b..5aa6089 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -18,7 +18,7 @@ jobs: steps: - powershell: 'Install-Module -Name Pester -Force -SkipPublisherCheck' displayName: 'Update Modules' - - powershell: './CI/PS-CI.ps1' + - powershell: './CI/PS-CI.ps1 ' displayName: 'Check Build Check Pack Test' - task: PublishTestResults@2 inputs: @@ -36,7 +36,7 @@ jobs: steps: - pwsh: 'Install-Module -Name Pester -Force' displayName: 'Update Pester' - - pwsh: .\ci\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' + - pwsh: '.\CI\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' displayName: 'Install and Test' - task: PublishTestResults@2 inputs: @@ -50,7 +50,7 @@ jobs: steps: - powershell: 'Install-Module -Name Pester -Force' displayName: 'Update Pester' - - powershell: '.\ci\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks ' + - powershell: './CI/PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks ' displayName: 'Install and Test' - task: PublishTestResults@2 inputs: @@ -66,7 +66,7 @@ jobs: displayName: 'Install mono-libgdiplus' - powershell: 'Install-Module -Name Pester -Force' displayName: 'Update Pester' - - powershell: './CI/CI.ps1 -Test' + - powershell: './CI/PS-CI.ps1 -Test' displayName: 'Install and Test' - task: PublishTestResults@2 inputs: From 40a50ad78b0388f1442fd4b984bb389cd1cc3806 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 11:06:17 +0000 Subject: [PATCH 15/32] Refine CI Pipeline (retry) --- CI/PS-CI.ps1 | 4 ---- CI/pipeline.yml | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 182fdf5..bdd1229 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -231,10 +231,6 @@ if (-not $SkipPostChecks) { } } -if (Test-Path $ExcelParams['Path']) { - "Try to uploadfile {0}" -f $ExcelParams['Path'] - "##vso[task.uploadfile]{0}" -f $ExcelParams['Path'] -} if (Test-Path $script:warningfile) { "Try to uploadfile {0}" -f $script:warningfile "##vso[task.uploadfile]{0}" -f $script:warningfile diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 5aa6089..6810599 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -35,7 +35,8 @@ jobs: vmImage: 'windows-latest' steps: - pwsh: 'Install-Module -Name Pester -Force' - displayName: 'Update Pester' + display + Name: 'Update Pester' - pwsh: '.\CI\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' displayName: 'Install and Test' - task: PublishTestResults@2 @@ -66,7 +67,7 @@ jobs: displayName: 'Install mono-libgdiplus' - powershell: 'Install-Module -Name Pester -Force' displayName: 'Update Pester' - - powershell: './CI/PS-CI.ps1 -Test' + - powershell: './CI/PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' displayName: 'Install and Test' - task: PublishTestResults@2 inputs: From 86f13e1d6f3af889a19f0a867a4ad99dffac309d Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 11:08:43 +0000 Subject: [PATCH 16/32] refine pipeline (remove typo) --- CI/pipeline.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 6810599..4522521 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -35,8 +35,7 @@ jobs: vmImage: 'windows-latest' steps: - pwsh: 'Install-Module -Name Pester -Force' - display - Name: 'Update Pester' + displayName: 'Update Pester' - pwsh: '.\CI\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' displayName: 'Install and Test' - task: PublishTestResults@2 From 1be2f82456988c6af66ba519bf375e2d1b372754 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 12:04:33 +0000 Subject: [PATCH 17/32] fix check not detecting commented alias --- CI/PS-CI.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index bdd1229..a22ba97 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -99,7 +99,7 @@ if (-not $SkipPreChecks) { $m2 = [regex]::Match($m[0],"^.*?param",17) # 17 = multi-line, ignnore case if (-not $m2.Success) {Show-Warning "function $name has no param() block"} else { - if ($m2.value -match "\[\s*Alias\(\s*.([\w-]+).\s*\)\s*\]") { + if ($m2.value -match "(? Date: Sun, 8 Dec 2019 12:56:54 +0000 Subject: [PATCH 18/32] reinstate loading from private post-merge --- ImportExcel.psd1 | 6 ++++-- ImportExcel.psm1 | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index ba42b13..ec936b9 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -153,14 +153,16 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5 '.\ImportExcel.psm1', '.\LICENSE.txt', '.\README.md', - '.\Plot.ps1' + '.\Plot.ps1', + '.\Private', + '.\Public', '.\en\ImportExcel-help.xml', '.\en\Strings.psd1', '.\Charting\Charting.ps1', '.\InferData\InferData.ps1', '.\Pivot\Pivot.ps1', '.\spikes\ConvertFrom-ExcelColumnName.ps1', - '.\Examples', '.\images', '.\Testimonials' , '.\Public' + '.\Examples', '.\images', '.\Testimonials' ) diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index 2ae628a..cabd70c 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -11,8 +11,6 @@ foreach ($directory in @('Private', 'Public','Charting','InferData','Pivot')) { Get-ChildItem -Path "$PSScriptRoot\$directory\*.ps1" | ForEach-Object {. $_.FullName} } -# . $PSScriptRoot\ArgumentCompletion.ps1 - if ($PSVersionTable.PSVersion.Major -ge 5) { . $PSScriptRoot\Plot.ps1 From 88e0a449c75a0ea03937cbbcfbf2630e157c7f27 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 13:16:54 +0000 Subject: [PATCH 19/32] Fix case error for CI.ps1 on Linux --- CI/CI.ps1 | 2 +- CI/pipeline.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/CI.ps1 b/CI/CI.ps1 index 1170c5c..5f88bc2 100644 --- a/CI/CI.ps1 +++ b/CI/CI.ps1 @@ -95,7 +95,7 @@ if (-not $VersionFilePath) { '[Info] Testing On:' Get-EnvironmentInfo '[Progress] Installing Module.' - . .\ci\Install.ps1 + . .\CI\Install.ps1 '[Progress] Invoking Pester.' Invoke-Pester -OutputFile ('TestResultsPS{0}.xml' -f $PSVersionTable.PSVersion) } diff --git a/CI/pipeline.yml b/CI/pipeline.yml index 4522521..ca0324c 100644 --- a/CI/pipeline.yml +++ b/CI/pipeline.yml @@ -17,7 +17,7 @@ jobs: vmImage: 'windows-latest' steps: - powershell: 'Install-Module -Name Pester -Force -SkipPublisherCheck' - displayName: 'Update Modules' + displayName: 'Update Pester' - powershell: './CI/PS-CI.ps1 ' displayName: 'Check Build Check Pack Test' - task: PublishTestResults@2 From c9116c2d1840739d1794fc6ab9a14b71669665ac Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 8 Dec 2019 22:54:18 +0000 Subject: [PATCH 20/32] Add manifest check to tests --- CI/PS-CI.ps1 | 16 +++++++++------- __tests__/ImportExcelTests/Simple.tests.ps1 | 11 +++++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index a22ba97..09e82d1 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -28,7 +28,7 @@ Param( [Parameter(ParameterSetName = 'Scope')] [switch]$CleanModuleDir ) -Function Show-warning { +Function Show-Warning { param( [Parameter(Position=0,ValueFromPipeline=$true)] $message @@ -49,8 +49,10 @@ if (-not $psdpath -or $psdpath.count -gt 1) { throw "Did not find a unique PSD file " } else { + try {$settings = Test-ModuleManifest -Path $psdpath -ErrorAction stop} + catch {throw $_ ; return} $ModuleName = $psdpath.Name -replace '\.psd1$' , '' - $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) + # $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) $approvedVerbs = Get-Verb | Select-Object -ExpandProperty verb $script:warningfile = Join-Path -Path $pwd -ChildPath "warnings.txt" } @@ -61,7 +63,7 @@ if (-not $SkipPreChecks) { #Check files in the manifest are present foreach ($file in $Settings.FileList) { if (-not (Test-Path $file)) { - show-warning "File $file in the manifest file list is not present" + Show-Warning "File $file in the manifest file list is not present" } } @@ -85,7 +87,7 @@ if (-not $SkipPreChecks) { $name = $file.name -replace(".ps1","") if ($name -notmatch ("(\w+)-\w+")) {Show-Warning "$name in the public folder is not a verb-noun name"} elseif ($Matches[1] -notin $approvedVerbs) {Show-Warning "$name in the public folder does not start with an approved verb"} - if(-not ($Settings.FunctionsToExport -ceq $name)) { + if(-not ($Settings.ExportedFunctions.Keys -ceq $name)) { Show-Warning ('File {0} in the public folder does not match an exported function in the manifest' -f $file.name) } else { @@ -102,7 +104,7 @@ if (-not $SkipPreChecks) { if ($m2.value -match "(? Date: Sun, 8 Dec 2019 23:30:20 +0000 Subject: [PATCH 21/32] go back to reading settings directly --- CI/PS-CI.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 09e82d1..c3bbd4b 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -49,10 +49,10 @@ if (-not $psdpath -or $psdpath.count -gt 1) { throw "Did not find a unique PSD file " } else { - try {$settings = Test-ModuleManifest -Path $psdpath -ErrorAction stop} + try {$null = Test-ModuleManifest -Path $psdpath -ErrorAction stop} catch {throw $_ ; return} $ModuleName = $psdpath.Name -replace '\.psd1$' , '' - # $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) + $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) $approvedVerbs = Get-Verb | Select-Object -ExpandProperty verb $script:warningfile = Join-Path -Path $pwd -ChildPath "warnings.txt" } @@ -87,7 +87,7 @@ if (-not $SkipPreChecks) { $name = $file.name -replace(".ps1","") if ($name -notmatch ("(\w+)-\w+")) {Show-Warning "$name in the public folder is not a verb-noun name"} elseif ($Matches[1] -notin $approvedVerbs) {Show-Warning "$name in the public folder does not start with an approved verb"} - if(-not ($Settings.ExportedFunctions.Keys -ceq $name)) { + if(-not ($Settings.FunctionsToExport -ceq $name)) { Show-Warning ('File {0} in the public folder does not match an exported function in the manifest' -f $file.name) } else { @@ -104,7 +104,7 @@ if (-not $SkipPreChecks) { if ($m2.value -match "(? Date: Mon, 9 Dec 2019 00:07:36 +0000 Subject: [PATCH 22/32] fix CI copy issue --- CI/PS-CI.ps1 | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index c3bbd4b..79fb6bb 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -125,24 +125,25 @@ if ($PreCheckOnly) {return} #region build, determine module path if necessary, create target directory if necessary, copy files based on manifest, build help try { - if (-not $ModulePath) { - if ($IsLinux -or $IsMacOS) {$ModulePathSeparator = ':' } - else {$ModulePathSeparator = ';' } - - if ($Scope -eq 'CurrentUser') { $dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile) } - else { $dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles) } + if ($ModulePath) { + $ModulePath = $ModulePath -replace "\\$|/$","" + } + else { + if ($IsLinux -or $IsMacOS) {$ModulePathSeparator = ':' } + else {$ModulePathSeparator = ';' } + if ($Scope -eq 'CurrentUser') {$dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile) } + else {$dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles) } $ModulePath = ($env:PSModulePath -split $ModulePathSeparator).where({$_ -like "$dir*"},"First",1) $ModulePath = Join-Path -Path $ModulePath -ChildPath $ModuleName $ModulePath = Join-Path -Path $ModulePath -ChildPath $Settings..ModuleVersion } - # Clean-up / Create Directory if (-not (Test-Path -Path $ModulePath)) { $null = New-Item -Path $ModulePath -ItemType Directory -ErrorAction Stop 'Created module folder: "{0}"' -f $ModulePath } elseif ($CleanModuleDir) { - '{0} exists - cleaning before copy' -f $ModulePath + '{0} exists - cleaning before copy' -f $ModulePath Get-ChildItem -Path $ModulePath | Remove-Item -Force -Recurse } 'Copying files to: "{0}"' -f $ModulePath @@ -182,7 +183,9 @@ finally { if (-not $outputFile -or -not (Test-Path $outputFile)) { } #endregion -Copy-Item -Path (split-path -Parent $ModulePath) -Destination $env:Build_ArtifactStagingDirectory -Recurse +if ($env:Build_ArtifactStagingDirectory) { + Copy-Item -Path (split-path -Parent $ModulePath) -Destination $env:Build_ArtifactStagingDirectory -Recurse +} #Check valid command names, help, run script analyzer over the files in the module directory if (-not $SkipPostChecks) { From f695e7cc63a1f37ec9e24ef38d3c23fb10513250 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Mon, 9 Dec 2019 00:24:56 +0000 Subject: [PATCH 23/32] retry previous --- CI/PS-CI.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 79fb6bb..9554001 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -148,6 +148,7 @@ try { } 'Copying files to: "{0}"' -f $ModulePath $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru + $outputFile.fullname foreach ($file in $Settings.FileList) { if ($file -like '.\*') { $dest = ($file -replace '\.\\',"$ModulePath\") @@ -156,10 +157,10 @@ try { } } else {$dest = $ModulePath } - Copy-Item -Path $file -Destination $dest -Force -Recurse + Copy-Item -Path $file -Destination $dest -Force -Recurse -Verbose } - if ((Test-Path -PathType Container "mdHelp") -and -not $SkipHelp) { + if ((Test-Path -PathType Container "mdHelp") -and -not $true) { if (-not (Get-Module -ListAvailable platyPS)) { 'Installing Platyps to build help files' Install-Module -Name platyPS -Force -SkipPublisherCheck @@ -177,9 +178,10 @@ catch { if ($PSScriptRoot) { Pop-Location } throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) } -finally { if (-not $outputFile -or -not (Test-Path $outputFile)) { +finally { if (-not $outputFile ) { throw "Failed to create module" } + if (-not (Test-Path $outputFile)) { throw "$outputfile doesn't exist"} } #endregion From ed7f43bab36ef4b57d4dc8fa97a30d8551d56cc5 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Mon, 9 Dec 2019 00:30:01 +0000 Subject: [PATCH 24/32] retry --- CI/PS-CI.ps1 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 9554001..be13574 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -147,7 +147,7 @@ try { Get-ChildItem -Path $ModulePath | Remove-Item -Force -Recurse } 'Copying files to: "{0}"' -f $ModulePath - $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru + $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru -Verbose $outputFile.fullname foreach ($file in $Settings.FileList) { if ($file -like '.\*') { @@ -178,10 +178,7 @@ catch { if ($PSScriptRoot) { Pop-Location } throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) } -finally { if (-not $outputFile ) { - throw "Failed to create module" - } - if (-not (Test-Path $outputFile)) { throw "$outputfile doesn't exist"} +finally { } #endregion From 28e3fe1cd82177da12db41dc559e7194986a0111 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Mon, 9 Dec 2019 00:32:47 +0000 Subject: [PATCH 25/32] again --- CI/PS-CI.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index be13574..5994068 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -135,7 +135,7 @@ try { else {$dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles) } $ModulePath = ($env:PSModulePath -split $ModulePathSeparator).where({$_ -like "$dir*"},"First",1) $ModulePath = Join-Path -Path $ModulePath -ChildPath $ModuleName - $ModulePath = Join-Path -Path $ModulePath -ChildPath $Settings..ModuleVersion + $ModulePath = Join-Path -Path $ModulePath -ChildPath $Settings.ModuleVersion } # Clean-up / Create Directory if (-not (Test-Path -Path $ModulePath)) { From ed41c20f639dcdc9e06fcab1f429d1d6dd478c43 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Mon, 9 Dec 2019 00:42:44 +0000 Subject: [PATCH 26/32] Finalize Checks for failing manfiest --- CI/PS-CI.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index 5994068..a944158 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -160,7 +160,7 @@ try { Copy-Item -Path $file -Destination $dest -Force -Recurse -Verbose } - if ((Test-Path -PathType Container "mdHelp") -and -not $true) { + if ((Test-Path -PathType Container "mdHelp") -and -not $SkipHelp) { if (-not (Get-Module -ListAvailable platyPS)) { 'Installing Platyps to build help files' Install-Module -Name platyPS -Force -SkipPublisherCheck @@ -175,10 +175,11 @@ try { $env:PSNewBuildModule = $ModulePath } catch { - if ($PSScriptRoot) { Pop-Location } - throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) + if ($PSScriptRoot) { Pop-Location } + throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) } finally { + if (-not $outputFile -or -not (Test-Path $outputFile)) { throw "Failed to create module"} } #endregion From ce8a070673cbb58777658dab534b7eebde6c70c5 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Mon, 9 Dec 2019 00:45:34 +0000 Subject: [PATCH 27/32] Remove debugging messages from CI pipeline --- CI/PS-CI.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/PS-CI.ps1 b/CI/PS-CI.ps1 index a944158..b37a8a4 100644 --- a/CI/PS-CI.ps1 +++ b/CI/PS-CI.ps1 @@ -147,7 +147,7 @@ try { Get-ChildItem -Path $ModulePath | Remove-Item -Force -Recurse } 'Copying files to: "{0}"' -f $ModulePath - $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru -Verbose + $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru $outputFile.fullname foreach ($file in $Settings.FileList) { if ($file -like '.\*') { @@ -157,7 +157,7 @@ try { } } else {$dest = $ModulePath } - Copy-Item -Path $file -Destination $dest -Force -Recurse -Verbose + Copy-Item -Path $file -Destination $dest -Force -Recurse } if ((Test-Path -PathType Container "mdHelp") -and -not $SkipHelp) { From 84140d757a30a59eb0161465a4a5ba8c4ceb175b Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 15 Dec 2019 22:36:31 +0000 Subject: [PATCH 28/32] #737 #741, #746 & Tests --- ImportExcel.psd1 | 85 ++++---- Public/ConvertFrom-ExcelSheet.ps1 | 54 +++-- Public/Export-ExcelSheet.md | 138 ------------- Public/Export-ExcelSheet.ps1 | 39 ---- Public/New-ConditionalText.ps1 | 29 +-- __tests__/Compare-WorkSheet.tests.ps1 | 11 +- __tests__/ConvertFrom-ExcelSheet.Tests.ps1 | 33 +++ __tests__/First10Races.xlsx | Bin 0 -> 14833 bytes __tests__/ImportExcelTests/Simple.tests.ps1 | 6 +- mdHelp/en/ConvertFrom-ExcelSheet.md | 217 ++++++++++++++++++++ 10 files changed, 346 insertions(+), 266 deletions(-) delete mode 100644 Public/Export-ExcelSheet.md delete mode 100644 Public/Export-ExcelSheet.ps1 create mode 100644 __tests__/ConvertFrom-ExcelSheet.Tests.ps1 create mode 100644 __tests__/First10Races.xlsx create mode 100644 mdHelp/en/ConvertFrom-ExcelSheet.md diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index ec936b9..c42dd17 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -26,38 +26,7 @@ PowerShell module to import/export Excel spreadsheets, without Excel. Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq '@ - # Minimum version of the Windows PowerShell engine required by this module - # PowerShellVersion = '' - # Name of the Windows PowerShell host required by this module - # PowerShellHostName = '' - - # Minimum version of the Windows PowerShell host required by this module - # PowerShellHostVersion = '' - - # Minimum version of Microsoft .NET Framework required by this module - # DotNetFrameworkVersion = '' - - # Minimum version of the common language runtime (CLR) required by this module - # CLRVersion = '' - - # Processor architecture (None, X86, Amd64) required by this module - # ProcessorArchitecture = '' - - # Modules that must be imported into the global environment prior to importing this module - # RequiredModules = @() - - # Script files (.ps1) that are run in the caller's environment prior to importing this module. - # ScriptsToProcess = @() - - # Type files (.ps1xml) to be loaded when importing this module - # TypesToProcess = @() - - # Format files (.ps1xml) to be loaded when importing this module - # FormatsToProcess = @() - - # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = @() # Functions to export from this module FunctionsToExport = @( @@ -125,15 +94,10 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5 'Update-FirstObjectProperties' ) - # Cmdlets to export from this module - #CmdletsToExport = '*' - - # Variables to export from this module - #VariablesToExport = '*' - # Aliases to export from this module AliasesToExport = @( - 'Convert-XlRangeToImage' + 'Convert-XlRangeToImage', + 'Export-ExcelSheet', 'New-ExcelChart', 'Set-Column', 'Set-Format', @@ -141,10 +105,9 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5 'Use-ExcelData' ) - # List of all modules packaged with this module - # ModuleList = @() + # Cmdlets to export from this module + CmdletsToExport = @() - # List of all files packaged with this module FileList = @( '.\EPPlus.dll', '.\Export-charts.ps1', @@ -199,6 +162,46 @@ Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5 } } + # Minimum version of the Windows PowerShell engine required by this module + # PowerShellVersion = '' + + # Name of the Windows PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the Windows PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module + # CLRVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # Variables to export from this module + #VariablesToExport = '*' + # HelpInfo URI of this module # HelpInfoURI = '' diff --git a/Public/ConvertFrom-ExcelSheet.ps1 b/Public/ConvertFrom-ExcelSheet.ps1 index 2c997af..db054f4 100644 --- a/Public/ConvertFrom-ExcelSheet.ps1 +++ b/Public/ConvertFrom-ExcelSheet.ps1 @@ -1,48 +1,42 @@ function ConvertFrom-ExcelSheet { [CmdletBinding()] - param - ( - [Alias("FullName")] + [Alias("Export-ExcelSheet")] + param ( [Parameter(Mandatory = $true)] - [String] - $Path, - [String] - $OutputPath = '.\', - [String] - $SheetName = "*", - [ValidateSet('ASCII', 'BigEndianUniCode', 'Default', 'OEM', 'UniCode', 'UTF32', 'UTF7', 'UTF8')] - [string] - $Encoding = 'UTF8', - [ValidateSet('.txt', '.log', '.csv')] - [string] - $Extension = '.csv', + [String]$Path, + [String]$OutputPath = '.\', + [String]$SheetName = "*", + [ValidateSet('ASCII', 'BigEndianUniCode','Default','OEM','UniCode','UTF32','UTF7','UTF8')] + [string]$Encoding = 'UTF8', + [ValidateSet('.txt', '.log','.csv')] + [string]$Extension = '.csv', [ValidateSet(';', ',')] - [string] - $Delimiter = ';' + [string]$Delimiter , + $Property = "*", + $ExcludeProperty = @(), + [switch]$Append, + [string[]]$AsText = @() ) $Path = (Resolve-Path $Path).Path - $Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, "Open", "Read", "ReadWrite" - $xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream + $xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path $workbook = $xl.Workbook - $targetSheets = $workbook.Worksheets | Where-Object { $_.Name -like $SheetName } + $targetSheets = $workbook.Worksheets | Where-Object {$_.Name -Like $SheetName} - $params = @{ } + $PSBoundParameters - $params.Remove("OutputPath") - $params.Remove("SheetName") - $params.Remove('Extension') - $params.NoTypeInformation = $true + $csvParams = @{NoTypeInformation = $true} + $PSBoundParameters + foreach ($p in 'OutputPath', 'SheetName', 'Extension', 'Property','ExcludeProperty', 'AsText') { + $csvParams.Remove($p) + } Foreach ($sheet in $targetSheets) { Write-Verbose "Exporting sheet: $($sheet.Name)" - $params.Path = "$OutputPath\$($Sheet.Name)$Extension" + $csvParams.Path = "$OutputPath\$($Sheet.Name)$Extension" - Import-Excel $Path -Sheet $($sheet.Name) | Export-Csv @params - } + Import-Excel -ExcelPackage $xl -Sheet $($sheet.Name) -AsText:$AsText | + Select-Object -Property $Property | Export-Csv @csvparams + } - $Stream.Close() - $Stream.Dispose() $xl.Dispose() } diff --git a/Public/Export-ExcelSheet.md b/Public/Export-ExcelSheet.md deleted file mode 100644 index 765dea7..0000000 --- a/Public/Export-ExcelSheet.md +++ /dev/null @@ -1,138 +0,0 @@ ---- -external help file: ImportExcel-help.xml -Module Name: ImportExcel -online version: https://github.com/dfinke/ImportExcel -schema: 2.0.0 ---- - -# Export-ExcelSheet - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Export-ExcelSheet [-Path] [[-OutputPath] ] [[-SheetName] ] [[-Encoding] ] [[-Extension] ] [[-Delimiter] ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -Delimiter -{{ Fill Delimiter Description }} - -```yaml -Type: String -Parameter Sets: (All) -Aliases: -Accepted values: ;, , - -Required: False -Position: 5 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Encoding -{{ Fill Encoding Description }} - -```yaml -Type: String -Parameter Sets: (All) -Aliases: -Accepted values: ASCII, BigEndianUniCode, Default, OEM, UniCode, UTF32, UTF7, UTF8 - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Extension -{{ Fill Extension Description }} - -```yaml -Type: String -Parameter Sets: (All) -Aliases: -Accepted values: .txt, .log, .csv - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -OutputPath -{{ Fill OutputPath Description }} - -```yaml -Type: String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Path -{{ Fill Path Description }} - -```yaml -Type: String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -SheetName -{{ Fill SheetName Description }} - -```yaml -Type: String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/Public/Export-ExcelSheet.ps1 b/Public/Export-ExcelSheet.ps1 deleted file mode 100644 index 27e8de5..0000000 --- a/Public/Export-ExcelSheet.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -function Export-ExcelSheet { - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [String]$Path, - [String]$OutputPath = '.\', - [String]$SheetName, - [ValidateSet('ASCII', 'BigEndianUniCode','Default','OEM','UniCode','UTF32','UTF7','UTF8')] - [string]$Encoding = 'UTF8', - [ValidateSet('.txt', '.log','.csv')] - [string]$Extension = '.csv', - [ValidateSet(';', ',')] - [string]$Delimiter = ';' - ) - - $Path = (Resolve-Path $Path).Path - $xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path - $workbook = $xl.Workbook - - $targetSheets = $workbook.Worksheets | Where-Object {$_.Name -Match $SheetName} - - $params = @{} + $PSBoundParameters - $params.Remove("OutputPath") - $params.Remove("SheetName") - $params.Remove('Extension') - $params.NoTypeInformation = $true - - Foreach ($sheet in $targetSheets) - { - Write-Verbose "Exporting sheet: $($sheet.Name)" - - $params.Path = "$OutputPath\$($Sheet.Name)$Extension" - - Import-Excel $Path -Sheet $($sheet.Name) | Export-Csv @params - } - - $xl.Dispose() -} diff --git a/Public/New-ConditionalText.ps1 b/Public/New-ConditionalText.ps1 index e7e990c..402310c 100644 --- a/Public/New-ConditionalText.ps1 +++ b/Public/New-ConditionalText.ps1 @@ -3,27 +3,28 @@ function New-ConditionalText { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',Justification='Does not change system State')] param( #[Parameter(Mandatory=$true)] - [Alias("ConditionValue")] + [Alias('ConditionValue')] $Text, - [Alias("ForeGroundColor")] + [Alias('ForeGroundColor')] $ConditionalTextColor=[System.Drawing.Color]::DarkRed, $BackgroundColor=[System.Drawing.Color]::LightPink, [String]$Range, [OfficeOpenXml.Style.ExcelFillStyle]$PatternType=[OfficeOpenXml.Style.ExcelFillStyle]::Solid, [ValidateSet( - "LessThan", "LessThanOrEqual", "GreaterThan", "GreaterThanOrEqual", - "Equal", "NotEqual", - "Top", "TopPercent", "Bottom", "BottomPercent", - "ContainsText", "NotContainsText", "BeginsWith", "EndsWith", - "ContainsBlanks", "NotContainsBlanks", "ContainsErrors", "NotContainsErrors", - "DuplicateValues", "UniqueValues", - "Tomorrow", "Today", "Yesterday", "Last7Days", - "NextWeek", "ThisWeek", "LastWeek", - "NextMonth", "ThisMonth", "LastMonth", - "AboveAverage", "AboveOrEqualAverage", "BelowAverage", "BelowOrEqualAverage" + 'LessThan', 'LessThanOrEqual', 'GreaterThan', 'GreaterThanOrEqual', + 'Equal', 'NotEqual', + 'Top', 'TopPercent', 'Bottom', 'BottomPercent', + 'ContainsText', 'NotContainsText', 'BeginsWith', 'EndsWith', + 'ContainsBlanks', 'NotContainsBlanks', 'ContainsErrors', 'NotContainsErrors', + 'DuplicateValues', 'UniqueValues', + 'Tomorrow', 'Today', 'Yesterday', 'Last7Days', + 'NextWeek', 'ThisWeek', 'LastWeek', + 'NextMonth', 'ThisMonth', 'LastMonth', + 'AboveAverage', 'AboveOrEqualAverage', 'BelowAverage', 'BelowOrEqualAverage', + 'Expression' )] - [Alias("RuleType")] - $ConditionalType="ContainsText" + [Alias('RuleType')] + $ConditionalType='ContainsText' ) $obj = [PSCustomObject]@{ diff --git a/__tests__/Compare-WorkSheet.tests.ps1 b/__tests__/Compare-WorkSheet.tests.ps1 index e119576..c4154b2 100644 --- a/__tests__/Compare-WorkSheet.tests.ps1 +++ b/__tests__/Compare-WorkSheet.tests.ps1 @@ -59,7 +59,16 @@ Describe "Compare Worksheet" { Context "Setting the background to highlight different rows" { BeforeAll { - $null = Compare-Worksheet "TestDrive:\server1.xlsx" "TestDrive:\server2.xlsx" -BackgroundColor ([System.Drawing.Color]::LightGreen) + if ($PSVersionTable.PSVersion.Major -ne 5) { + $null = Compare-Worksheet "TestDrive:\server1.xlsx" "TestDrive:\server2.xlsx" -BackgroundColor ([System.Drawing.Color]::LightGreen) + } + else { + $cmdline = 'Import-Module {0}; $null = Compare-WorkSheet "{1}" "{2}" -BackgroundColor ([System.Drawing.Color]::LightGreen) -GridView; Start-Sleep -sec 5; exit' + $cmdline = $cmdline -f (Resolve-Path "$PSScriptRoot\..\importExcel.psd1" ) , + (Join-Path (Get-PSDrive TestDrive).root "server1.xlsx"), + (Join-Path (Get-PSDrive TestDrive).root "server2.xlsx") + powershell.exe -Command $cmdline + } $xl1 = Open-ExcelPackage -Path "TestDrive:\server1.xlsx" $xl2 = Open-ExcelPackage -Path "TestDrive:\server2.xlsx" $s1Sheet = $xl1.Workbook.Worksheets[1] diff --git a/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 b/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 new file mode 100644 index 0000000..f3021bc --- /dev/null +++ b/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 @@ -0,0 +1,33 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','',Justification='False Positives')] +$scriptPath = Split-Path -Path $MyInvocation.MyCommand.path -Parent +$dataPath = Join-Path -Path $scriptPath -ChildPath "First10Races.xlsx" +$Outpath = "TestDrive:\" + +Describe 'ConvertFrom-ExcelSheet / Export-ExcelSheet' { + BeforeAll { + ConvertFrom-ExcelSheet -Path $dataPath -OutputPath $Outpath + $firstText = Get-Content (Join-path -Path $Outpath -ChildPath "First10Races.csv") + ConvertFrom-ExcelSheet -Path $dataPath -OutputPath $Outpath -AsText GridPosition,date + $SecondText = Get-Content (Join-path -Path $Outpath -ChildPath "First10Races.csv") + ConvertFrom-ExcelSheet -Path $dataPath -OutputPath $Outpath -AsText "GridPosition" -Property driver, + @{n="date"; e={[datetime]::FromOADate($_.Date).tostring("#MM/dd/yyyy#")}} , FinishPosition, GridPosition + $ThirdText = Get-Content (Join-path -Path $Outpath -ChildPath "First10Races.csv") + } + Context "Exporting to CSV" { + it "Exported the expected columns to a CSV file " { + $firstText[0] | should be '"Race","Date","FinishPosition","Driver","GridPosition","Team","Points"' + $SecondText[0] | should be '"Race","Date","FinishPosition","Driver","GridPosition","Team","Points"' + $ThirdText[0] | should be '"Driver","date","FinishPosition","GridPosition"' + } + it "Applied ASText and Properties correctly " { + $firstText[1] | should match '^"\w+","\d{5}","\d{1,2}","\w+ \w+","[1-9]\d?","\w+","\d{1,2}"$' + $secondText[1] | should match '^"\w+","\d\d \w{3,} \d{4}","\d","\w+ \w+","0\d","\w+","\d{1,2}"$' + $ThirdText[1] | should match '^"\w+ \w+","#\d\d/\d\d/\d{4}#","\d","0\d"$' + } + } + Context "Export aliased to ConvertFrom" { + it "Applied ASText and Properties correctly " { + (Get-Alias Export-ExcelSheet).source | should be "ImportExcel" + } + } +} \ No newline at end of file diff --git a/__tests__/First10Races.xlsx b/__tests__/First10Races.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5fe17090bf6468f103c58bdf843a84a7dbceaa99 GIT binary patch literal 14833 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 literal 0 HcmV?d00001 diff --git a/__tests__/ImportExcelTests/Simple.tests.ps1 b/__tests__/ImportExcelTests/Simple.tests.ps1 index 47b6f1f..abb229c 100644 --- a/__tests__/ImportExcelTests/Simple.tests.ps1 +++ b/__tests__/ImportExcelTests/Simple.tests.ps1 @@ -1,8 +1,8 @@ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','',Justification='False Positives')] Param() -if (-not (Get-command Import-Excel -ErrorAction SilentlyContinue)) { - Import-Module $PSScriptRoot\..\..\ImportExcel.psd1 -} + +Import-Module $PSScriptRoot\..\..\ImportExcel.psd1 -Force + Describe "Tests" { BeforeAll { $data = $null diff --git a/mdHelp/en/ConvertFrom-ExcelSheet.md b/mdHelp/en/ConvertFrom-ExcelSheet.md new file mode 100644 index 0000000..ed25289 --- /dev/null +++ b/mdHelp/en/ConvertFrom-ExcelSheet.md @@ -0,0 +1,217 @@ +--- +external help file: ImportExcel-help.xml +Module Name: ImportExcel +online version: https://github.com/dfinke/ImportExcel +schema: 2.0.0 +--- + +# ConvertFrom-ExcelSheet + +## SYNOPSIS +Exports Sheets from Excel Workbooks to CSV files. + +## SYNTAX + +``` +ConvertFrom-ExcelSheet [-Path] [[-OutputPath] ] [[-SheetName] ] [[-Encoding] ] + [[-Extension] ] [[-Delimiter] ] [[-Property] ] [[-ExcludeProperty] ] [-Append] + [[-AsText] ] [] +``` + +## DESCRIPTION +This command provides a convenient way to run Import-Excel @ImportParameters | Select-Object @selectParameters | export-Csv @ ExportParameters +It can take the parameters -AsText , as used in Import-Excel, )Properties & -ExcludeProperties as used in Select-Object and +-Append, -Delimiter and -Encoding as used in Export-CSV + + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> ConvertFrom-ExcelSheet Path .\__tests__\First10Races.xlsx -OutputPath .. -AsText GridPosition,date +``` + +First10Races.xlsx contains information about Motor races. The race date and grid (starting) position are stored with custom formats. +The command specifies the path to the file, and the directory to create the output file, and specifies that the columns "GridPosition" and "Date" should be treated as text to preserve their formatting + +### Example 2 +```powershell +PS C:\> ConvertFrom-ExcelSheet Path .\__tests__\First10Races.xlsx -OutputPath .. -AsText "GridPosition" -Property driver, @{n="date"; e={[datetime]::FromOADate($_.Date).tostring("#MM/dd/yyyy#")}} , FinishPosition, GridPosition +``` + +This uses the same file as example 1. Because the race date has a custom format, it imports as a number, +The requirement is to create a CSV file with the Driver, a specially formatted Date, FinishPostion and GridPostion (keeping its custom formatting). +The command specifies the path to the file, and the directory to create the output file, specifies that the column "GridPosition" should be treated as text instead of a number, and the output properties should be Driver, a calculated "date" field, FinishPosition and GridPsition. FromOADate converts the dates used by Excel (Days since Jan 1 1900) to a datetime object. + + +## PARAMETERS + +### -Append +Use this parameter to have the export add output to the end of the file. Without this parameter, the command replaces the file contents without warning. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AsText +AsText allows selected columns to be returned as the text displayed in their cells, instead of their value. (* is supported as a wildcard.) + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: 8 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Delimiter +Selects , or ; as the delimeter for the exported data - if not specified , is used by default. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: ;, , + +Required: False +Position: 5 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Encoding +Sets the text encoding for the output data file; UTF8 bu default + + +```yaml +Type: Encoding +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExcludeProperty + Specifies the properties that to exclude from the export. Wildcards are permitted. This parameter is effective only when the command also includes the Property parameter. + + +```yaml +Type: Object +Parameter Sets: (All) +Aliases: + +Required: False +Position: 7 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Extension +Sets the file extension for the exported data, defaults to CSV + + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: .txt, .log, .csv + +Required: False +Position: 4 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OutputPath +The directory where the output file(s) will be created. The file name(s) will match the name of the workbook page which contained the data. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Path +The path to the .XLSX file which will be exported. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Property +Specifies the properties to select. Wildcards are permitted - the default is "*". +The value of the Property parameter can be a new calculated property, and follows the same pattern as Select-Item + +```yaml +Type: Object +Parameter Sets: (All) +Aliases: + +Required: False +Position: 6 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SheetName +The name of a sheet to export, or a regular expression which is used to identify sheets + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS From 7b51d2f340ea2c7620a6d624bc1249518f330cda Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 15 Dec 2019 22:54:31 +0000 Subject: [PATCH 30/32] retry --- __tests__/Get-ExcelColumnName.Test.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/__tests__/Get-ExcelColumnName.Test.ps1 b/__tests__/Get-ExcelColumnName.Test.ps1 index 295ab83..bb09628 100644 --- a/__tests__/Get-ExcelColumnName.Test.ps1 +++ b/__tests__/Get-ExcelColumnName.Test.ps1 @@ -17,11 +17,11 @@ $map = @{ 16384 = 'XFD' } -(Get-ExcelColumnName 26).columnName | Should be 'Z' -(Get-ExcelColumnName 27).columnName | Should be 'AA' +(Get-ExcelColumnName 26).columnName | Should be 'Z' +(Get-ExcelColumnName 27).columnName | Should be 'AA' (Get-ExcelColumnName 28).columnNamee | Should be 'AB' -(Get-ExcelColumnName 30).columnName | Should be 'AD' -(Get-ExcelColumnName 48).columnName | Should be 'AV' +(Get-ExcelColumnName 30).columnName | Should be 'AD' +(Get-ExcelColumnName 48).columnName | Should be 'AV' 1..16 | ForEach-Object { $number = $_ * 1024 From 08dd30696e99a34a203c11c45f305b0d4dc46d40 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Sun, 15 Dec 2019 23:34:55 +0000 Subject: [PATCH 31/32] fix convertFrom-ExcelSheet test - date format issue --- __tests__/ConvertFrom-ExcelSheet.Tests.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 b/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 index f3021bc..5065a33 100644 --- a/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 +++ b/__tests__/ConvertFrom-ExcelSheet.Tests.ps1 @@ -21,7 +21,10 @@ Describe 'ConvertFrom-ExcelSheet / Export-ExcelSheet' { } it "Applied ASText and Properties correctly " { $firstText[1] | should match '^"\w+","\d{5}","\d{1,2}","\w+ \w+","[1-9]\d?","\w+","\d{1,2}"$' - $secondText[1] | should match '^"\w+","\d\d \w{3,} \d{4}","\d","\w+ \w+","0\d","\w+","\d{1,2}"$' + $date = $firstText[1] -replace '^.*(\d{5}).*$', '$1' + $date = [datetime]::FromOADate($date).toString("D") + $secondText[1] | should belike "*$date*" + $secondText[1] | should match '"0\d","\w+","\d{1,2}"$' $ThirdText[1] | should match '^"\w+ \w+","#\d\d/\d\d/\d{4}#","\d","0\d"$' } } From 718bf951b76903afb8c0d945fc4e0c4bbc862672 Mon Sep 17 00:00:00 2001 From: jhoneill Date: Thu, 19 Dec 2019 18:37:41 +0000 Subject: [PATCH 32/32] Fix String typos. Fix missing space merge-multiple column names --- ImportExcel.psm1 | 2 +- Public/Merge-MultipleSheets.ps1 | 2 +- en/Strings.psd1 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index cabd70c..c5c96a5 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -5,7 +5,7 @@ if (-not $Strings) { Import-LocalizedData -UICulture "en" -BindingVariable Strings -FileName Strings -ErrorAction Ignore } try { [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") } -catch { Write-Warning -Message $Strings.SystemDrawingAvaialable } +catch { Write-Warning -Message $Strings.SystemDrawingAvailable } foreach ($directory in @('Private', 'Public','Charting','InferData','Pivot')) { Get-ChildItem -Path "$PSScriptRoot\$directory\*.ps1" | ForEach-Object {. $_.FullName} diff --git a/Public/Merge-MultipleSheets.ps1 b/Public/Merge-MultipleSheets.ps1 index 996bdf4..acffcb6 100644 --- a/Public/Merge-MultipleSheets.ps1 +++ b/Public/Merge-MultipleSheets.ps1 @@ -89,7 +89,7 @@ function Merge-MultipleSheets { if ($filesToProcess.Count -ge 2) { $refPrefix = (Split-Path -Path $filestoProcess[0] -Leaf) -replace "\.xlsx$"," " } - else {$refPrefix = $WorksheetName[0] } + else {$refPrefix = $WorksheetName[0] + " "} Write-Progress -Activity "Merging sheets" -CurrentOperation "applying formatting to sheet '$OutputSheetName' in $OutputFile" #Find the column headings which are in the form "diffFile is"; which will hold 'Same', 'Added' or 'Changed' foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "\sIS$"}) ) { diff --git a/en/Strings.psd1 b/en/Strings.psd1 index ad64b6e..e6dc199 100644 --- a/en/Strings.psd1 +++ b/en/Strings.psd1 @@ -1,5 +1,5 @@ ConvertFrom-StringData @' -SystemDrawingAvaialable=System.Drawing could not be loaded. Color and font look-ups may not be available. +SystemDrawingAvailable=System.Drawing could not be loaded. Color and font look-ups may not be available. PS5NeededForPlot=PowerShell 5 is required for plot.ps1 -ModuleReadyExceptPlot=The ImportExcel moudle is ready, except for that functionality +ModuleReadyExceptPlot=The ImportExcel module is ready, except for that functionality '@ \ No newline at end of file