diff --git a/.build/custom/Setup-Container.ps1 b/.build/custom/Invoke-ContainerSetup.ps1 similarity index 100% rename from .build/custom/Setup-Container.ps1 rename to .build/custom/Invoke-ContainerSetup.ps1 diff --git a/.build/modules/PSDepend/0.3.8/PSDepend.Config b/.build/modules/PSDepend/0.3.8/PSDepend.Config new file mode 100644 index 0000000..972f0b0 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDepend.Config @@ -0,0 +1,7 @@ +# You can use the following: +# Absolute paths +# Relative paths +# UNC paths +# $ModuleRoot as "the path to the PSDepend module folder" +NuGetPath=$ModuleRoot\nuget.exe +GitPath=$ModuleRoot\git\git.exe \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDepend.Format.ps1xml b/.build/modules/PSDepend/0.3.8/PSDepend.Format.ps1xml new file mode 100644 index 0000000..2ba92bf --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDepend.Format.ps1xml @@ -0,0 +1,32 @@ + + + + + Default + + PSDepend.Dependency + + + + + + + DependencyName + + + DependencyType + + + Version + + + + Try { ($_.Tags | Out-String ).trimend("`n")} Catch {$_.Tags} + + + + + + + + diff --git a/.build/modules/PSDepend/0.3.8/PSDepend.psd1 b/.build/modules/PSDepend/0.3.8/PSDepend.psd1 new file mode 100644 index 0000000..1d89853 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDepend.psd1 @@ -0,0 +1,111 @@ +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'PSDepend.psm1' + +# Version number of this module. +ModuleVersion = '0.3.8' + +# ID used to uniquely identify this module +GUID = '63ea9e2a-320d-43ff-a11a-4930ca03cce6' + +# Author of this module +Author = 'Warren Frame' + +# Company or vendor of this module +#CompanyName = 'Unknown' + +# Copyright statement for this module +Copyright = '(c) 2016 Warren F. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'PowerShell Dependency Handler' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '3.0' + +# 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 = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# 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 = 'PSDepend.Format.ps1xml' + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module +FunctionsToExport = @('Get-Dependency','Get-PSDependScript','Get-PSDependType','Import-Dependency','Install-Dependency','Invoke-DependencyScript','Invoke-PSDepend','Test-Dependency') + +# Cmdlets to export from this module +CmdletsToExport = '*' + +# Variables to export from this module +# VariablesToExport = '*' + +# Aliases to export from this module +AliasesToExport = '*' + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('requirements', 'dependencies', 'dependency', 'manager', 'bundle', 'package') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/RamblingCookieMonster/PSDepend/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/RamblingCookieMonster/PSDepend/' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = 'Added various PowerShell Core fixes thanks to @lipkau!' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} diff --git a/.build/modules/PSDepend/0.3.8/PSDepend.psm1 b/.build/modules/PSDepend/0.3.8/PSDepend.psm1 new file mode 100644 index 0000000..a4cf20f --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDepend.psm1 @@ -0,0 +1,34 @@ +#Get public and private function definition files. + $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) + $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) + $ModuleRoot = $PSScriptRoot + +#Dot source the files + Foreach($import in @($Public + $Private)) + { + Try + { + . $import.fullname + } + Catch + { + Write-Error -Message "Failed to import function $($import.fullname): $_" + } + } + +#Get nuget dependecy file if we don't have it + Get-Content $ModuleRoot\PSDepend.Config | + Where-Object {$_ -and $_ -notmatch "^\s*#"} | + Foreach-Object { + $Name = ( $_ -split '=')[0].trim() + $Value = ( $_ -split '=')[1].trim() + # Revisit later and only apply these for '*path', if we have other types of variables... + $Value = $Value -replace '\$ModuleRoot', $ModuleRoot + $Value = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Value) + Set-Variable -Name $Name -Value $Value + } + if(Test-PlatformSupport -Support 'windows') { + BootStrap-Nuget -NugetPath $NuGetPath + } + +Export-ModuleMember -Function $Public.Basename diff --git a/.build/modules/PSDepend/0.3.8/PSDependMap.psd1 b/.build/modules/PSDepend/0.3.8/PSDependMap.psd1 new file mode 100644 index 0000000..d150c72 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependMap.psd1 @@ -0,0 +1,92 @@ +# This defines dependencies. Feel free to create your own +# Top level node is the dependency name +# Script is the script to run. These are stored in \PSDepend\PSDependScripts +# Description is a quick description of the dependency script +# Supports is a way to filter supported platforms: core, windows, macos, linux + +# In some cases, it may be beneficial to include 'aliases'. Just add nodes for these. +@{ + Chocolatey = @{ + Script = 'Chocolatey.ps1' + Description = 'Install a Chocolatey package from a Chocolatey feed' + Supports = 'windows' + } + + Command = @{ + Script = 'Command.ps1' + Description = 'Invoke a command in PowerShell' + Supports = 'windows', 'core', 'macos', 'linux' + } + + DotnetSdk = @{ + Script = 'DotnetSdk.ps1' + Description = "Installs the .NET Core SDK" + Supports = 'windows', 'core', 'macos', 'linux' + } + + FileDownload = @{ + Script = 'FileDownload.ps1' + Description = 'Download a file' + Supports = 'windows' + } + + FileSystem = @{ + Script = 'FileSystem.ps1' + Description = 'Copy a file or folder' + Supports = 'windows' + } + + Git = @{ + Script = 'Git.ps1' + Description = 'Clone a git repository' + Supports = 'windows', 'core', 'macos', 'linux' + } + + GitHub = @{ + Script = 'GitHub.ps1' + Description = 'Download and extract a GitHub repo' + Supports = 'windows', 'core', 'macos', 'linux' + } + + Npm = @{ + Script = 'Npm.ps1' + Description = 'Install a node package' + Supports = 'windows', 'core', 'macos', 'linux' + } + + Noop = @{ + Script = 'Noop.ps1' + Description = 'Display parameters that a depends script would receive. Use for testing and validation' + Supports = 'windows', 'core', 'macos', 'linux' + } + + Nuget = @{ + Script = 'Nuget.ps1' + Description = 'Install a Nuget package from a Nuget feed' + Supports = 'windows', 'core', 'macos', 'linux' + } + + Package = @{ + Script = 'Package.ps1' + Description = 'EXPERIMENTAL: Install a package via PackageManagement Install-Package' + Supports = 'windows', 'core', 'macos', 'linux' + } + + PSGalleryModule = @{ + Script= 'PSGalleryModule.ps1' + Description = 'Install a PowerShell module from the PowerShell Gallery' + Supports = 'windows', 'core', 'macos', 'linux' + } + + PSGalleryNuget = @{ + Script = 'PSGalleryNuget.ps1' + Description = 'Install a PowerShell module from the PowerShell Gallery without the PowerShellGet dependency' + Supports = 'windows', 'core', 'macos', 'linux' + } + + Task = @{ + Script = 'Task.ps1' + Description = 'Support dependencies by handling simple tasks' + Supports = 'windows', 'core', 'macos', 'linux' + } +} diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Chocolatey.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Chocolatey.ps1 new file mode 100644 index 0000000..7bb3beb --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Chocolatey.ps1 @@ -0,0 +1,299 @@ +<# + .SYNOPSIS + Installs a Chocolatey package a repository. + + .DESCRIPTION + Installs a package from a Chocolatey repository like Chocolatey.org. + + Relevant Dependency metadata: + Name: The name of the package + Version: Used to identify existing installs meeting this criteria. Defaults to 'latest' + Source: Source Uri. Defaults to https://chocolatey.org/api/v2/ + + .PARAMETER Force + If specified and the package is already installed, force the install again. + + .PARAMETER PSDependAction + Test, or Install the package. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + + .EXAMPLE + + @{ + 'git' = @{ + DependencyType = 'Chocolatey' + Version = '2.0.2' + } + } + + # Install version 2.0.2 of git via Chocolatey.org + + .EXAMPLE + + @{ + 'git' = @{ + DependencyType = 'Chocolatey' + Source = 'https://feed.mycompany.com' + } + } + + # Install the latest version of git from the Chocolatey feed at https://feed.mycompany.com + + .EXAMPLE + + @{ + PSDependOptions = @{ + DependencyType = 'Chocolatey' + } + 'git.portable' = @{ + Version = 'latest' + Parameters = @{ + Force = $true + } + } + 'lessmsi' = 'latest' + 'putty' = 'latest' + } + + # Installs the list of Chocolatey packages from Chocolatey.org using the Global PSDependOptions to limit repetition. + +#> +[CmdletBinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [switch]$Force, + + [string]$ChocoInstallScriptUrl = 'https://chocolatey.org/install.ps1', + + [ValidateSet('Test', 'Install')] + [string[]]$PSDependAction = @('Install') +) + +function Get-ChocoInstalledPackage +{ + [CmdletBinding()] + param ( + [string]$Name + ) + + $chocoParams = @('list', "$Name", '--limit-output', '--exact', '--local-only') + Invoke-ExternalCommand -Command 'choco.exe' -Arguments $chocoParams -PassThru | ConvertFrom-Csv -Header 'Name', 'Version' -Delimiter "|" +} + +function Get-ChocoLatestPackage +{ + [CmdletBinding()] + param ( + [string]$Name, + + [string]$Source, + + [Management.Automation.PSCredential]$Credential + ) + + $chocoParams = @('list', "$Name", '--limit-output', '--exact') + if ($Source) + { + $chocoParams += "--source='$Source'" + } + + if ($Credential) + { + $username = $credential.UserName + $password = $credential.GetNetworkCredential().Password + $chocoParams += "--username='$username'" + $chocoParams += "--password='$password'" + } + + Invoke-ExternalCommand -Command 'choco.exe' -Arguments $chocoParams -PassThru | ConvertFrom-Csv -Header 'Name', 'Version' -Delimiter "|" +} + +function Invoke-ChocoInstallPackage +{ + [CmdletBinding()] + param ( + [string]$Name, + + [string]$Version, + + [string]$Source, + + [switch]$Force, + + [Management.Automation.PSCredential]$Credential + ) + + $chocoParams = @('upgrade', "$Name", '--limit-output', '--exact', '--no-progress', '--allow-downgrade') + if ($Force.IsPresent) + { + $chocoParams += "--force" + } + + if ($Source) + { + $chocoParams += "--source='$Source'" + } + + if ($Version -and $Version -ne 'latest' -and $Version -ne '') + { + $chocoParams += "--version='$Version'" + } + + if ($Credential) + { + $username = $credential.UserName + $password = $credential.GetNetworkCredential().Password + $chocoParams += "--username='$username'" + $chocoParams += "--password='$password'" + } + + Invoke-ExternalCommand -Command 'choco.exe' -Arguments $chocoParams +} + +# Extract data from Dependency +$Name = $Dependency.Name +if (-not $Name) +{ + $Name = $Dependency.DependencyName +} + +$Version = $Dependency.Version +if (-not $Dependency.Version -or $Version -eq '') +{ + $Version = 'latest' +} + +$Source = $Dependency.Source +if (-not $Dependency.Source -or $Source -eq '') +{ + $Source = 'https://chocolatey.org/api/v2/' +} + +$Credential = $Dependency.Credential + +if (-not (Get-Command -Name 'choco.exe' -ErrorAction SilentlyContinue)) { + Write-Verbose "Chocolatey is not installed. Installing from [$ChocoInstallScriptUrl]" + # download and run the Chocolatey script + # Add TLS 1.2 support + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + + do + { + $scriptPath = Join-Path -Path $env:TEMP -ChildPath ("{0}.ps1" -f [GUID]::NewGuid().ToString()) + } while (Test-Path -Path $scriptPath) + + try + { + Invoke-WebRequest -UseBasicParsing -Uri $ChocoInstallScriptUrl -OutFile $scriptPath + & $scriptPath + } + catch + { + throw "Unable to install Chocolatey from '$scriptUrl'." + } +} + +# if this is a forced install we don't need to check anything, just install the package version requested +if ($Force.IsPresent -and $PSDependAction -contains 'Install') +{ + $params = @{ + Name = $Name + Version = $Version + Source = $Source + Force = $Force.IsPresent + } + + if ($Credential) + { + $params.Credential = $Credential + } + + Write-Verbose "Forced install of Chocolatey package [$Name] from Chocolatey source [$Source] with Version [$Version]" + Invoke-ChocoInstallPackage @params + + return +} + +# get the package if it is installed +Write-Verbose "Getting package [$Name] version, if it is installed." +$existingVersion = (Get-ChocoInstalledPackage -Name $Name).Version +if ($existingVersion) +{ + Write-Verbose "Found package [$Name] installed with version [$Version]." +} +else { + Write-Verbose "Package [$Name] not installed." +} + +# Version latest requested, and equal to current +if ($Version -ne 'latest' -and $Version -eq $existingVersion) +{ + Write-Verbose "You have the requested version [$Version] of [$Name]" + if($PSDependAction -contains 'Test') + { + return $true + } + + return +} + +# get the latest version from the source +$repoParams = @{ + Name = $Name + Source = $Source +} +if ($Credential) +{ + $repoParams.Credential = Credential +} + +Write-verbose "Getting latest package [$Name] version from source [$Source]." +$repositoryVersion = (Get-ChocoLatestPackage @repoParams).Version +if ($repositoryVersion) +{ + Write-Verbose "Found package [$Name] version [$Version] on source [$Source]." +} +else +{ + Write-Verbose "Package [$Name] not found on source [$Source]. Nothing more can be done." + return # cannot continue +} + +# If the version in the remote repository is less than or equal to the version installed, then we have the latest already +if ($Version -eq 'latest' -and ([System.Version]$repositoryVersion -le [System.Version]$existingVersion)) +{ + Write-Verbose "You have the latest version of [$Name], with installed version [$existingVersion] and Source version [$repositoryVersion]" + if($PSDependAction -contains 'Test') + { + return $true + } + + return +} + +# if we get here then we do not have the latest version installed and that is what has been requested +Write-Verbose "You do not have the version requested of [$Name]: Requested version [$Version], existing version [$existingVersion], available version [$repositoryVersion]." +if ($PSDependAction -contains 'Install') +{ + $params = @{ + Name = $Name + Version = $Version + Source = $Source + Force = $Force.IsPresent + } + + if ($Credential) + { + $params.Credential = $Credential + } + + Invoke-ChocoInstallPackage @params +} +elseif ($PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) +{ + return $false +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Command.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Command.ps1 new file mode 100644 index 0000000..ee735ee --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Command.ps1 @@ -0,0 +1,72 @@ +<# + .SYNOPSIS + Invoke a PowerShell command + + .DESCRIPTION + Invoke a PowerShell command + + Converts the provided string into a scriptblock, invokes it in the current session. Beware quoting rules + + If a terminating error occurs, we write it and continue processing. Use FailOnError to change this. + + Relevant Dependency metadata: + Source: The code to run + Parameters: + FailOnError: If specified, throw a terminating error if the command errors out. + + .PARAMETER PSDependAction + Only option is to install the module. Defaults to Install + + Install: Install the dependency + + .PARAMETER Dependency + Dependency to process + + .EXAMPLE + @{ + ExampleCommand = @{ + DependencyType = 'Command' + Source = '$x = hostname; "Running a command on $x"' + } + } + + # Run some aribtrary PowerShell code that assigns a variable and uses it in a string + # Output: Running a command on WJ-LAB +#> +[cmdletbinding()] +param ( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [switch]$FailOnError, + + [ValidateSet('Install')] + [string[]]$PSDependAction = @('Install') # No logic for this +) + +Write-Verbose "Executing $($Dependency.count) commands" + +foreach($Depend in $Dependency) +{ + foreach($Command in $Depend.Source) + { + Write-Verbose "Invoking command [$($Dependency.DependencyName)]:`n$Command" + $ScriptBlock = [ScriptBlock]::Create($Command) + Try + { + . $ScriptBlock + } + Catch + { + if($FailOnError) + { + Write-Error $_ + continue + } + else + { + throw $_ + } + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/DotnetSdk.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/DotnetSdk.ps1 new file mode 100644 index 0000000..454c4f7 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/DotnetSdk.ps1 @@ -0,0 +1,107 @@ +<# + .SYNOPSIS + Installs the .NET Core SDK. + + .DESCRIPTION + Installs the .NET Core SDK. + + Relevant Dependency metadata: + DependencyName (Key): The .NET Core SDK download channel - ex. release, LTS, etc. + Version: Minimum version you need on your system. + Target: Path to place the .dotnet folder, which contains the .NET Core SDK. + You can specify a full path, a UNC path, or a relative path from the + current directory. You can also specify the special keyword, 'Global', + which will cause the node package to be installed globally for the + user who runs PSDepend against this dependency. + + .PARAMETER Dependency + Dependency to process + + .PARAMETER PSDependAction + Test, Install, or Import the dependency. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + Import: Adds the .NET Core SDK to $env:PATH + + .EXAMPLE + @{ + 'release' = @{ + DependencyType = 'DotnetSdk' + Version = '2.1.0' + Target = './.dotnet/' + } + } + + # Full syntax + # DependencyName (key) uses (unique) channel name 'release' + # Specify a version to install + # Ensure the package is installed locally in a .dotnet folder. + + .EXAMPLE + @{ + 'DotnetSdk::LTS' = 'latest' + } + + # Simple syntax + # The .NET SDK will be installed with the latest verion from the LTS channel, globally. + +#> +[CmdletBinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]] + $Dependency, + + [ValidateSet('Test', 'Install', 'Import')] + [string[]] + $PSDependAction = @('Install') +) + +# Users can specify 'Global which will use the default global path of +# "$env:LocalAppData\Microsoft\dotnet" on Windows or "$env:HOME/.dotnet" elsewhere +# Since Global is the default behavior, we ingore the fact that the Target was set. +$InstallDir = if ($Dependency.Target -and $Dependency.Target -ne 'Global') { $Dependency.Target } +$Version = $Dependency.Version +$Channel = if ($Dependency.DependencyName) { $Dependency.DependencyName } else { "release" } + +# The 'global' install location is different per platform +$IsWindowsEnv = !$PSVersionTable.Platform -or $PSVersionTable.Platform -eq "Win32NT" +$globalDotnetSdkLocation = if ($IsWindowsEnv) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" } + +# Handle 'Test' +if ($PSDependAction -contains 'Test') { + # Returns true if the .NET Core SDK can be found + return Test-Dotnet -Version $Version -InstallDir $InstallDir +} + +# Handle 'Install' +if ($PSDependAction -contains 'Install') { + if (!(Test-Dotnet -Version $Version -InstallDir $InstallDir)) { + # If the InstallDir is not set, set it to the 'global' path + if (!$InstallDir) { + $InstallTo = $globalDotnetSdkLocation + } else { + $InstallTo = $InstallDir + } + Install-Dotnet -Channel $Channel -Version $Version -InstallDir $InstallTo + } +} + +# Handle 'Import' +if ($PSDependAction -contains 'Import') { + # If the InstallDir was specified and the .NET Core SDK exists in it, add it to the path + if ($InstallDir -and (Test-Dotnet -Version $Version -InstallDir $InstallDir)) { + $env:PATH = "$InstallDir$([IO.Path]::PathSeparator)$env:PATH" + } else { + # Test if it's in the path already and if it's not check if it's in the 'global' location + $dotnetInPath = Get-Command 'dotnet' -ErrorAction SilentlyContinue + if (!$dotnetInPath) { + if (!(Test-Dotnet -Version $Version -InstallDir $globalDotnetSdkLocation)) { + throw ".NET SDK cannot be located. Try installing using PSDepend." + } else { + $env:PATH = "$globalDotnetSdkLocation$([IO.Path]::PathSeparator)$env:PATH" + } + } + } +} diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/FileDownload.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/FileDownload.ps1 new file mode 100644 index 0000000..2dad3cb --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/FileDownload.ps1 @@ -0,0 +1,161 @@ +<# + .SYNOPSIS + Download a file + + .DESCRIPTION + Download a file + + Relevant Dependency metadata: + DependencyName (Key): The key for this dependency is used as the URL. This can be overridden by 'Source' + Name: Optional file name for the downloaded file. Defaults to parsing filename from the URL + Target: The folder to download this file to. If a full path to a new file is used, this overrides any other file name. + Source: Optional override for URL + AddToPath: If specified, prepend the target's parent container to PATH + + .PARAMETER PSDependAction + Test or Install the module. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + + .EXAMPLE + sqllite_dll = @{ + DependencyType = 'FileDownload' + Source = 'https://github.com/RamblingCookieMonster/PSSQLite/blob/master/PSSQLite/x64/System.Data.SQLite.dll?raw=true' + Target = 'C:\temp' + } + + # Downloads System.Data.SQLite.dll to C:\temp + + .EXAMPLE + 'https://github.com/RamblingCookieMonster/PSSQLite/blob/master/PSSQLite/x64/System.Data.SQLite.dll?raw=true' = @{ + DependencyType = 'FileDownload' + Target = 'C:\temp\sqlite.dll' + } + + # Downloads System.Data.SQLite.dll to C:\temp\sqlite.dll +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]] + $Dependency, + + [ValidateSet('Test', 'Install')] + [string[]]$PSDependAction = @('Install') +) + +function Parse-URLForFile { +[cmdletbinding()] +param($URL) + # This will need work. Assume leaf is file. If CGI exists in leaf, assume it is after the file + $FileName = $URL.split('/')[-1] + if($FileName -match '\?') + { + $FileName.split('?')[0] + } + else + { + $FileName + } + Write-Verbose "Parsed file name [$FileName] from `$URL" +} + +# Extract data from Dependency + $DependencyName = $Dependency.DependencyName + $Name = $Dependency.Name + $Target = $Dependency.Target + $Source = $Dependency.Source + + # Pick the URL + if($Source) + { + $URL = $Source + } + else + { + $URL = $DependencyName + } + Write-Verbose "Using URL: $URL" + +# Act on target path.... + $ToInstall = $False # Anti pattern + $TargetParent = Split-Path $Target -Parent + $PathToAdd = $Target + if( (Test-Path $TargetParent) -and -not (Test-Path $Target)) + { + # They gave us a full path, don't parse the file name, use this! + $Path = $Target + $ToInstall = $True + Write-Verbose "Found parent [$TargetParent], not target [$Target], assuming this is target file path" + } + elseif(Test-Path $Target -PathType Leaf) + { + # File exists. We should download to temp spot, compare hashes, take action as appropriate. + # For now, skip the file. + Write-Verbose "Skipping existing file [$Target]" + if($PSDependAction -contains 'Test') + { + return $True + } + $PathToAdd = Split-Path $Target -Parent + } + elseif(-not (Test-Path $Target)) + { + # They gave us something that doesn't look like a new container for a new or existing file. Wat? + Write-Error "Could not find target path [$Target]" + if($PSDependAction -contains 'Test') + { + return $False + } + } + else + { + Write-Verbose "[$Target] is a container, creating path to file" + # We have a target container, now find the name + If($Name) + { + # explicit name + $FileName = $Name + } + else + { + $FileName = Parse-URLForFile -URL $URL + } + $Path = Join-Path $Target $FileName + + if(Test-Path $Path -PathType Leaf) + { + Write-Verbose "Skipping existing file [$Path]" + if($PSDependAction -contains 'Test') + { + return $True + } + } + else + { + $ToInstall = $True + } + } + Write-Verbose "Using [$Path] as `$Target" + + #No dependency found, return false if we're testing alone... + if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) + { + return $False + } + Write-Verbose "Downloading [$URL] to [$Path]" + +if($PSDependAction -contains 'Install' -and $ToInstall) +{ + # Future considerations: + # Should we check for existing? And if we find it, still download file, and compare sha256 hash, replace if it does not match? + # We should consider credentials at some point, but PSD1 does not lend itself to securely storing passwords + Get-WebFile -URL $URL -Path $Path +} + +if($Dependency.AddToPath) +{ + Write-Verbose "Setting PATH to`n$($PathToAdd, $env:PATH -join ';' | Out-String)" + Add-ToItemCollection -Reference Env:\Path -Item $PathToAdd +} diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/FileSystem.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/FileSystem.ps1 new file mode 100644 index 0000000..5edaf50 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/FileSystem.ps1 @@ -0,0 +1,199 @@ +<# + .SYNOPSIS + EXPERIMENTAL: Use Robocopy or Copy-Item for folder and file dependencies, respectively. + + .DESCRIPTION + EXPERIMENTAL: Use Robocopy or Copy-Item for folder and file dependencies, respectively. + + Runs in the current session (i.e. as the current user) + + Relevant Dependency metadata: + DependencyName (Key): The key for this dependency is used as the URL. This can be overridden by 'Source' + Name: Optional file name for the downloaded file. Defaults to parsing filename from the URL + Target: The folder to copy the source to. If the source is a file, we adjust this in Robocopy to append the source folder name. + Source: The source folder or file to copy + + .PARAMETER Dependency + Dependency to run + + .PARAMETER PSDependAction + Test, Install, or Import the module. Defaults to Install + + Test: Return true or false on whether the dependency is in place + IMPORTANT: If a folder exists, return $True, whether the contents are the same or not + If a file exists, we check the hash + Install: Install the dependency + Import: Import the dependency 'Target'. Override with ImportPath + + .PARAMETER ImportPath + If specified with PSDependAction Import, we import this path, instead of the target (or target parent if target is a file) + + .PARAMETER Force + If specified, and target is a folder, overwrite the target + + .PARAMETER Mirror + If specified and the target is a folder, we effectively call robocopy /MIR (Can remove folders/files...) + + .EXAMPLE + + @{ + 'notepad' = @{ + DependencyType = 'FileSystem' + Source = 'C:\windows\notepad.exe' + Target = 'C:\PSDependPesterTest' + } + } + + # Copy C:\Windows\Notepad.exe to C:\PSDependPesterTest\notepad.exe + + .EXAMPLE + + @{ + 'psams' = @{ + DependencyType = 'FileSystem' + Source = '\\FileServer\powershell\modules\psams' + Target = 'C:\ProjectX' + } + } + + # Copy psams module to C:\ProjectX\psams + +#> +[cmdletbinding()] +param ( + [PSTypeName('PSDepend.Dependency')] + [psobject[]] + $Dependency, + + [ValidateSet('Test', 'Install', 'Import')] + [string[]]$PSDependAction = @('Install'), + + [string]$ImportPath +) + +# Extract data from Dependency + $DependencyName = $Dependency.DependencyName + $Name = $Dependency.Name + $Target = $Dependency.Target + $Sources = @($Dependency.Source) + +$TestOutput = @() +foreach($Source in @($Sources)) +{ + if(-not (Test-Path $Source)) + { + if(-not $PSDependAction -like 'Test') + { + Write-Error "Skipping $DependencyName, could not find source [$Sources] due to error:" + } + continue + } + $IsContainer = ( Get-Item $Source ).PSIsContainer + + # Resolve PSDrives. + $Target = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Target) + $Source = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Source) + + if($IsContainer) + { + $Folder = Split-Path $Source -Leaf + $Target = Join-Path $Target $Folder + if(-not $ImportPath) {$ImportPath = $Target} + # We don't test equality for containers yet + if(Test-Path $Target) + { + $TestOutput += $true + } + else + { + $TestOutput += $false + } + if($PSDependAction -like 'Install') + { + # TODO: Add non Windows equivalent... + [string[]]$Arguments = "/XO" + $Arguments += "/E" + if($Dependency.Parameters.Mirror -eq $True -or $Mirror) + { + $Arguments += "/PURGE" + } + + Write-Verbose "Invoking ROBOCOPY.exe $Source $Target $Arguments" + ROBOCOPY.exe $Source $Target @Arguments + } + } + else + { + $SourceFolderPath = Split-Path $Source -Parent + $SourceFileName = Split-Path $Source -Leaf + $TargetFile = Join-Path $Target $SourceFileName + $SourceHash = ( Get-Hash $Source ).SHA256 + $TargetHash = $null + if(Test-Path $Target -PathType Leaf) + { + $TargetHash = ( Get-Hash $Target -ErrorAction SilentlyContinue -WarningAction SilentlyContinue ).SHA256 + $TargetPath = $Target + } + elseif(Test-Path $TargetFile -PathType Leaf) + { + $TargetHash = ( Get-Hash $TargetFile -ErrorAction SilentlyContinue -WarningAction SilentlyContinue ).SHA256 + $TargetPath = $TargetFile + } + Write-Verbose "Source [$Source] hash [$SourceHash]`n`tTarget [$TargetPath] hash [$TargetHash]" + + if($TargetHash -ne $SourceHash) + { + Write-Verbose "Hashes do not match" + if($PSDependAction -like 'Install') + { + Write-Verbose "Copying file [$Source] to [$Target]" + Copy-Item -Path $Source -Destination $Target -Force + } + if($PSDependAction -like 'Test' -and $PSDependAction.count -eq 1) + { + $TestOutput += $false + } + } + else + { + Write-Verbose "Matching hash: [$Source] = [$TargetFile]" + if($PSDependAction -like 'Test' -and $PSDependAction.count -eq 1) + { + $TestOutput += $True + } + } + } +} + +if($PSDependAction -like 'Test' -and $PSDependAction.count -eq 1) +{ + if(@($TestOutput) -contains $false -or @($TestOutput) -notcontains $true) + { + return $false + } + else + { + return $True + } +} + +if($PSDependAction -like 'Import') +{ + if(-not $ImportPath) + { + if(Test-Path $Target -PathType Leaf) + { + $ImportPath = Split-Path $Target -Parent + } + elseif(Test-Path $Target -PathType Container) + { + $ImportPath = $Target + } + else + { + Write-Error "Could not import target [$Target], path not found" + return + } + } + Import-PSDependModule $ImportPath +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Git.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Git.ps1 new file mode 100644 index 0000000..0483898 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Git.ps1 @@ -0,0 +1,225 @@ +<# + .SYNOPSIS + Clone a git repository + + .DESCRIPTION + Clone a git repository + + Note: We require git in your path + + Relevant Dependency metadata: + DependencyName (Key): Git URL + You can override this with the 'Name'. + If you specify only an Account/Repository, we assume GitHub is the source + Name: Optional override for the Git URL, same rules as DependencyName (key) + Version: Used with git checkout. Specify a branch name, commit hash, or tags/, for example. Defaults to master + Target: Path to clone this repository. e.g C:\Temp would result in C:\Temp\RepoName. Defaults to nothing (current path/repo name) + AddToPath: Prepend the Target to ENV:PATH and ENV:PSModulePath + + .PARAMETER Force + If specified and target does not exist, create directory tree up to the target folder + + .PARAMETER PSDependAction + Test, Install, or Import the module. Defaults to Install + + Test: Return true or false on whether the dependency is in place (Note: Currently only checks if path exists) + Install: Install the dependency + Import: Import the dependency 'Target'. Override with ImportPath + + .PARAMETER ImportPath + If specified with PSDependAction Import, we import this path, instead of Target, the default + + .EXAMPLE + @{ + 'buildhelpers' = @{ + Name = 'https://github.com/RamblingCookieMonster/BuildHelpers.git' + Version = 'd32a9495c39046c851ceccfb7b1a85b17d5be051' + Target = 'C:\git' + } + } + + # Full syntax + # DependencyName (key) uses (unique) name 'buildhelpers' + # Override DependencyName as URL the name https://github.com/RamblingCookieMonster/BuildHelpers.git + # Specify a commit to checkout (version) + # Clone in C:\git + + .EXAMPLE + + @{ + 'https://github.com/RamblingCookieMonster/PSDeploy.git' = 'master' + 'https://internal.gitlab.fqdn/jdoe/BuildHelpers.git' = 'd32a9495c39046c851ceccfb7b1a85b17d5be051' + } + + # Simple syntax + # First example shows cloning PSDeploy from ramblingcookiemonster's GitHub repo + # Second example shows clonging BuildHelpers from jdoe's internal GitLab account and checking out a specific commit + # Both are cloned to the current path (e.g. .\) + # This syntax assumes git as a source. The right hand side is the version (branch, commit, tags/, etc.) +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [switch]$Force, + + [ValidateSet('Test', 'Install')] + [string[]]$PSDependAction = @('Install'), + + [string]$ImportPath, + + [bool]$ExtractProject = $False +) + +# Extract data from Dependency +$DependencyName = $Dependency.DependencyName +$Name = $Dependency.Name +if(-not $Name) +{ + $Name = $DependencyName +} + +#Name is in account/repo format, default to GitHub as source +#This likely needs work, and will need to change if GitHub changes valid characters for usernames +if($Name -match "^[a-zA-Z0-9]+/[a-zA-Z0-9_-]+$") +{ + $Name = "https://github.com/$Name.git" +} +$GitName = $Name.trimend('/').split('/')[-1] -replace "\.git$", '' +if($Dependency.Target -and ($Target = (Get-Item $Dependency.Target -ErrorAction SilentlyContinue).FullName)) +{ + Write-Debug "Target resolved to $Target" +} +else +{ + $Target = $PWD.Path + Write-Debug "Target defaulted to current dir: $Target" +} +$RepoPath = Join-Path $Target $GitName +$GottaInstall = $True + +if(-not (Test-Path $Target) -and $PSDependAction -contains 'Install') +{ + Write-Verbose "Creating folder [$Target] for git dependency [$Name]" + $null = New-Item $Target -ItemType Directory -Force +} + +if(-not (Test-Path $RepoPath)) +{ + # Nothing found, return test output + if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) + { + return $False + } +} +else # Target exists +{ + $GottaTest = $True +} + +if(-not (Get-Command git -ErrorAction SilentlyContinue)) +{ + Write-Error "Git dependency type requires git. Ensure this is in your path, or explicitly specified in $ModuleRoot\PSDepend.Config's GitPath. Skipping [$DependencyName]" +} + +$Version = $Dependency.Version +if(-not $Version) +{ + $Version = 'master' +} + +if($GottaTest) +{ + Push-Location + Set-Location $RepoPath + $Branch = Invoke-ExternalCommand git -Arguments (Write-Output rev-parse --abbrev-ref HEAD) -Passthru + $Commit = Invoke-ExternalCommand git -Arguments (Write-Output rev-parse HEAD) -Passthru + Pop-Location + if($Version -eq $Branch -or $Version -eq $Commit) + { + Write-Verbose "[$RepoPath] exists and is already at version [$Version]" + if($PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) + { + return $true + } + $GottaInstall = $False + } + elseif($PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) + { + Write-Verbose "[$RepoPath] exists and is at branch [$Branch], commit [$Commit].`nWe don't currently support moving to the requested version [$Version]" + return $false + } + else + { + Write-Verbose "[$RepoPath] exists and is at branch [$Branch], commit [$Commit].`nWe don't currently support moving to the requested version [$Version]" + $GottaInstall = $False + } +} + +if($PSDependAction -notcontains 'Install') +{ + return +} + +if($GottaInstall -and !$ExtractProject) +{ + Push-Location + Set-Location $Target + Write-Verbose -Message "Cloning dependency [$Name] with git from [$($Target)]" + Invoke-ExternalCommand git 'clone', $Name + + #TODO: Should we do a fetch, once existing repo is found? + Set-Location $RepoPath + Write-Verbose -Message "Checking out [$Version] of [$Name] from [$RepoPath]" + Invoke-ExternalCommand git 'checkout', $Version + Pop-Location +} +elseif($GottaInstall -and $ExtractProject) { + $OutPath = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid().guid) + $RepoFolder = Join-Path -Path $OutPath -ChildPath $GitName + + $null = New-Item -ItemType Directory -Path $OutPath -Force + Push-Location $OutPath + + Write-Verbose -Message "Cloning dependency [$GitName] with git from [$($Target)]" + Invoke-ExternalCommand git 'clone', $Name + + Push-Location $GitName + Write-Verbose -Message "Checking out [$Version] of [$GitName] from [$RepoFolder]" + Invoke-ExternalCommand git 'checkout', $Version + Pop-Location + + + $ProjectDetails = Get-ProjectDetail -Path $RepoFolder + [string[]]$ToCopy = $ProjectDetails.Path + Pop-Location + + #TODO: Implement test and import PSDependActions. + if(-not (Test-Path $Target)) + { + $null = New-Item -ItemType Directory -Path $Target -Force + } + foreach($Item in $ToCopy) + { + Write-Verbose "Copy From: $ToCopy To: $Target" + Copy-Item -Path $Item -Destination $Target -Force -Confirm:$False -Recurse + } + Remove-Item $OutPath -Force -Recurse +} + +if($Dependency.AddToPath) +{ + Write-Verbose "Setting PSModulePath to`n$($Target, $env:PSModulePath -join ';' | Out-String)" + Add-ToItemCollection -Reference Env:\PSModulePath -Item (Get-Item $Target).FullName + + Write-Verbose "Setting PATH to`n$($RepoPath, $env:PATH -join ';' | Out-String)" + Add-ToItemCollection -Reference Env:\PATH -Item (Get-Item $Target).FullName +} + +$ToImport = $Target +if($ImportPath) +{ + $ToImport = $ImportPath +} +Import-PSDependModule $ToImport diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/GitHub.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/GitHub.ps1 new file mode 100644 index 0000000..014cf32 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/GitHub.ps1 @@ -0,0 +1,572 @@ +<# + .SYNOPSIS + Installs a module from a GitHub repository. + + .DESCRIPTION + Installs a module from a GitHub repository. + + Relevant Dependency metadata: + DependencyName (Key): The key for this dependency is used as Name, if none is specified + Name: Used to specify the GitHub repository name to download + Version: Used to identify existing installs meeting this criteria, and as RequiredVersion for installation. Defaults to 'latest' + Target: The folder to download repo to. Created if it doesn't exist. + "AllUsers" resolves to: + Windows: the system's program files folder. + Other: the platform's SHARED_MODULES folder. + "CurrentUser" resolves to: + Windows: the user's (My)Documents folder. + Other: the platform's USER_MODULES folder. + It defaults to "AllUsers" on Windows in an elevated session and to "CurrentUser" otherwise. + PowerShell uses the "WindowsPowerShell\Modules" folder hierarchy while PowerShell Core uses "PowerShell\Modules". + + + .NOTES + Initial idea and some code by Doug Finke. Rewrite by Jonas Thelemann for tag support and PowerShell Core compatibility. + A huge thanks to both! + https://github.com/dfinke/InstallModuleFromGitHub + https://github.com/dargmuesli + + .PARAMETER PSDependAction + Test, install or import the module. Defaults to "Install". + + Test: Return true or false on whether the dependency is in place. + Install: Install the dependency. + Import: Import the dependency. + + .PARAMETER ExtractPath + Limit extraction of file(s) and folder(s). + + .PARAMETER ExtractProject + Parse the GitHub repository for a common PowerShell project hierarchy and extract only the project folder. + + Example: ramblingcookiemonster/psslack looks like this: + PSSlack/ Repo root + PSSlack/ Module root + PSSlack.psd1 Module manifest + Tests/ + + In this case, we would extract PSSlack/PSSlack only. + + Example: bundyfx/vamp looks like this: + vamp/ Repo root (also, module root) + vamp.psd1 Module manifest + + In this case, we would extract the whole root vamp folder. + + .PARAMETER TargetType + How the target is interpreted: + Standard: Extract to "target\name" [Default]. + Exact: Extract to "target\". + Parallel: Extract to "target\name\version" or "target\name\branch\name" depending on the version specified. + + .PARAMETER Force + If specified, delete an already existing target folder (as defined by TargetType). + Default: False / not specified. Files are copied to the target folder without any file or folder removal. + + .EXAMPLE + Imagine a GitHub repository containing a PowerShell module with git tags named "1.0.0" and "0.1.0". + + @{ + 'Dargmuesli/powershell-lib' = '1.0.0' + } + @{ + 'Dargmuesli/powershell-lib' = 'latest' + } + @{ + 'Dargmuesli/powershell-lib' = '' + } + These download version 1.0.0 to "powershell-lib\1.0.0" + + @{ + 'Dargmuesli/powershell-lib' = '0.1.0' + } + This downloads version 0.1.0 to "powershell-lib\0.1.0" + + @{ + 'Dargmuesli/powershell-lib' = 'master' + } + This downloads branch "master" (most recent commit version) to "powershell-lib" + + .EXAMPLE + Imagine a GitHub repository containing a PowerShell module with no git tags. + + @{ + 'Dargmuesli/powershell-lib' = 'latest' + } + @{ + 'Dargmuesli/powershell-lib' = '' + } + @{ + 'Dargmuesli/powershell-lib' = 'master' + } + These download branch "master" (most recent commit version) to "powershell-lib" + + @{ + 'Dargmuesli/powershell-lib' = @{ + Version = 'latest' + Parameters @{ + TargetType = 'Parallel' + } + } + } + @{ + 'Dargmuesli/powershell-lib' = @{ + Parameters @{ + TargetType = 'Parallel' + } + } + } + @{ + 'Dargmuesli/powershell-lib' = @{ + Version = 'master' + Parameters @{ + TargetType = 'Parallel' + } + } + } + These download branch "master" (most recent commit version) to "powershell-lib\master\powershell-lib" + + @{ + 'Dargmuesli/powershell-lib' = @{ + Version = 'feature' + Parameters @{ + TargetType = 'Parallel' + } + } + } + This downloads branch "feature" (most recent commit version) to "powershell-lib\feature\powershell-lib" + + .EXAMPLE + @{ + 'powershell/demo_ci' = @{ + Version = 'latest' + DependencyType = 'GitHub' + Target = 'C:\T' + Parameters = @{ + ExtractPath = 'Assets/DscPipelineTools', + 'InfraDNS/Configs/DNSServer.ps1' + TargetType = 'Exact' + } + } + } + + # This downloads the latest version of demo_ci by powershell from GitHub. + # Then it extracts "repo-root/Assets/DscPipelineTools" and "repo-root/InfraDNS/Configs/DNSServer.ps1" to the target. +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [ValidateSet('Test', 'Install', 'Import')] + [string[]]$PSDependAction = @('Install'), + + [string[]]$ExtractPath, + + [bool]$ExtractProject = $true, + + [ValidateSet('Parallel', 'Standard', 'Exact')] + [string]$TargetType = 'Standard', + + [switch]$Force +) + +$script:IsWindows = (-not (Get-Variable -Name "IsWindows" -ErrorAction "Ignore")) -or $IsWindows +$script:IsCoreCLR = $PSVersionTable.ContainsKey("PSEdition") -and $PSVersionTable.PSEdition -eq "Core" + +Write-Verbose -Message "Am I on Windows? [$script:IsWindows]! Am I PS Core? [$script:IsCoreCLR]!" + +# Extract data from dependency +$DependencyID = $Dependency.DependencyName +$DependencyVersion = $Dependency.Version +$DependencyTarget = $Dependency.Target +$DependencyName = $DependencyID.Split("/")[1] + +# Translate "" to "latest" +if($DependencyVersion -eq "") +{ + $DependencyVersion = "latest" +} + +# Check if the version that should be used is a version number +if($DependencyVersion -match "^\d+(?:\.\d+)+$") +{ + $DependencyVersion = New-Object "System.Version" $DependencyVersion +} + +if ($script:IsCoreCLR) { + $ModuleChildPath = "PowerShell\Modules" +} +else +{ + $ModuleChildPath = "WindowsPowerShell\Modules" +} + +# Get system installation path +if($script:IsWindows) +{ + $AllUsersPath = Join-Path -Path $env:ProgramFiles -ChildPath $ModuleChildPath +} +else +{ + $AllUsersPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory('SHARED_MODULES') +} + +# Check if the MyDocuments folder path is accessible +try +{ + $MyDocumentsFolderPath = [Environment]::GetFolderPath("MyDocuments") +} +catch +{ + $MyDocumentsFolderPath = $null +} + +# Get user installation path +if($script:IsWindows) +{ + if($MyDocumentsFolderPath) + { + $CurrentUserPath = Join-Path -Path $MyDocumentsFolderPath -ChildPath $ModuleChildPath + } + else + { + $CurrentUserPath = Join-Path -Path $HOME -ChildPath "Documents\$ModuleChildPath" + } +} +else +{ + $CurrentUserPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory('USER_MODULES') +} + +# Set target path +if($DependencyTarget) +{ + # Resolve scope keywords + if($DependencyTarget -Eq "CurrentUser") + { + $TargetPath = $CurrentUserPath + } + elseif($DependencyTarget -Eq "AllUsers") + { + $TargetPath = $AllUsersPath + } + else + { + $TargetPath = $DependencyTarget + } +} +else +{ + # Set default target depending on admin permissions + if(($script:IsWindows) -And (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))) + { + $TargetPath = $AllUsersPath + } + else + { + $TargetPath = $CurrentUserPath + } +} + +Write-Verbose -Message "Dependency id: [$DependencyID]" +Write-Verbose -Message "Dependency version: [$DependencyVersion]" +Write-Verbose -Message "Dependency target: [$DependencyTarget]" +Write-Verbose -Message "Dependency name: [$DependencyName]" +Write-Verbose -Message "Target Path: [$TargetPath]" + +# Search for an already existing version of the dependency +$Module = Get-Module -ListAvailable -Name $DependencyName -ErrorAction SilentlyContinue +$ModuleExisting = $null +$ModuleExistingMatches = $false +$ExistingVersions = $null +$ShouldInstall = $false +$RemoteAvailable = $false +$URL = $null + +if($Module) +{ + $ModuleExisting = $true +} +else +{ + $ModuleExisting = $false +} + +if($ModuleExisting) +{ + Write-Verbose "Found existing module [$DependencyName]" + $ExistingVersions = $Module | Select-Object -ExpandProperty "Version" + + # Check if the version that is should be used is a version number + if($DependencyVersion -match "^\d+(?:\.\d+)+$") + { + :versionslocal foreach($ExistingVersion in $ExistingVersions) + { + switch($ExistingVersion.CompareTo($DependencyVersion)) + { + {@(-1, 1) -contains $_} { + Write-Verbose "For [$DependencyName], the version you specified [$DependencyVersion] does not match the already existing version [$ExistingVersion]" + $ShouldInstall = $true + break + } + 0 { + Write-Verbose "For [$DependencyName], the version you specified [$DependencyVersion] matches the already existing version [$ExistingVersion]" + $ShouldInstall = $false + $ModuleExistingMatches = $true + break versionslocal + } + } + } + } + else + { + # The version that is to be used is probably a GitHub branch name + $ShouldInstall = $true + } +} +else +{ + Write-Verbose "Did not find existing module [$DependencyName]" + $ShouldInstall = $true +} + +# Skip the case when the version that is to be used already exists +if($ShouldInstall) +{ + # API-fetch the tags on GitHub + $GitHubVersion = $null + $GitHubTag = $null + $Page = 0 + + try + { + :nullcheck while($GitHubVersion -Eq $null) + { + $Page++ + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + $GitHubTags = Invoke-RestMethod -Uri "https://api.github.com/repos/$DependencyID/tags?per_page=100&page=$Page" + + if($GitHubTags) + { + foreach($GitHubTag in $GitHubTags) + { + if($GitHubTag.name -match "^\d+(?:\.\d+)+$" -and ($DependencyVersion -match "^\d+(?:\.\d+)+$" -or $DependencyVersion -eq "latest")) + { + $GitHubVersion = New-Object "System.Version" $GitHubTag.name + + if($DependencyVersion -Eq "latest") + { + $DependencyVersion = $GitHubVersion + } + + switch($DependencyVersion.CompareTo($GitHubVersion)) + { + -1 { + # Version is older compared to the GitHub version, continue searching + break + } + 0 { + Write-Verbose "For [$DependencyName], a matching version [$DependencyVersion] has been found in the GitHub tags" + $RemoteAvailable = $true + break nullcheck + } + 1 { + # Version is newer compared to the GitHub version, which means we can stop searching (given version history is reasonable) + break nullcheck + } + } + } + } + } + else + { + break nullcheck + } + } + } + catch + { + # Repository does not seem to exist or a branch is the target + $ShouldInstall = $false + Write-Warning "Could not find module on GitHub: $_" + } + + if($RemoteAvailable) + { + # Use the tag's link + $URL = $GitHubTag.zipball_url + if($ExistingVersions) + { + :versionsremote foreach($ExistingVersion in $ExistingVersions) + { + # Because a remote and a local version exist + # Prevent a module from getting installed twice + switch($ExistingVersion.CompareTo($GitHubVersion)) + { + {@(-1, 1) -contains $_} { + Write-Verbose "For [$DependencyName], you have a different version [$ExistingVersion] compared to the version available on GitHub [$GitHubVersion]" + break + } + 0 { + Write-Verbose "For [$DependencyName], you already have the version [$ExistingVersion]" + $ModuleExistingMatches = $true + $ShouldInstall = $false + break versionsremote + } + } + } + } + } + else + { + Write-Verbose "[$DependencyID] has no tags on GitHub or [$DependencyVersion] is a branchname" + # Translate version "latest" to "master" + if($DependencyVersion -eq "latest") + { + $DependencyVersion = "master" + } + + # Link for a .zip archive of the repository's branch + $URL = "https://api.github.com/repos/$DependencyID/zipball/$DependencyVersion" + $ShouldInstall = $true + } +} + +if ($TargetType -ne 'Exact') +{ + $TargetPath = Join-Path $TargetPath $DependencyName +} + +# Install action needs to be wanted and logical +if(($PSDependAction -contains 'Install') -and $ShouldInstall) +{ + # Create a temporary directory and download the repository to it + $OutPath = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid().guid) + New-Item -ItemType Directory -Path $OutPath -Force | Out-Null + $OutFile = Join-Path $OutPath "$DependencyVersion.zip" + Invoke-RestMethod -Uri $URL -OutFile $OutFile + + if(-not (Test-Path $OutFile)) + { + Write-Error "Could not download [$URL] to [$OutFile]. See error details and verbose output for more information" + return + } + + # Extract the zip file + if($script:IsWindows) + { + $ZipFile = (New-Object -com shell.application).NameSpace($OutFile) + $ZipDestination = (New-Object -com shell.application).NameSpace($OutPath) + $ZipDestination.CopyHere($ZipFile.Items()) + } + else + { + # If not on Windows "Expand-Archive" should be available as PS version 6 is considered minimum. + Expand-Archive $OutFile -DestinationPath $OutPath + } + + # Remove the zip file + Remove-Item $OutFile -Force -Confirm:$false + + $OutPath = (Get-ChildItem -Path $OutPath)[0].FullName + $OutPath = (Rename-Item -Path $OutPath -NewName $DependencyName -PassThru).FullName + + if($ExtractPath) + { + # Filter only the contents wanted + [string[]]$ToCopy = foreach($RelativePath in $ExtractPath) + { + $AbsolutePath = Join-Path $OutPath $RelativePath + if(-not (Test-Path $AbsolutePath)) + { + Write-Warning "Expected ExtractPath [$RelativePath], did not find at [$AbsolutePath]" + } + else + { + $AbsolutePath + } + } + } + elseif($ExtractProject) + { + # Filter only the project contents + $ProjectDetails = Get-ProjectDetail -Path $OutPath + [string[]]$ToCopy = $ProjectDetails.Path + } + else + { + # Use the standard download path + [string[]]$ToCopy = $OutPath + } + + Write-Verbose "Contents that will be copied: $ToCopy" + + # Copy the contents to their target + if(-not (Test-Path $TargetPath)) + { + New-Item $TargetPath -ItemType "directory" -Force + } + + $Destination = $null + + if($TargetType -eq 'Exact') + { + $Destination = $TargetPath + } + elseif($DependencyVersion -match "^\d+(?:\.\d+)+$" -and $PSVersionTable.PSVersion -ge '5.0' ) + { + # For versioned GitHub tags + $Destination = Join-Path $TargetPath $DependencyVersion + } + elseif(($DependencyVersion -eq "latest") -and ($RemoteAvailable) -and $PSVersionTable.PSVersion -ge '5.0' ) + { + # For latest GitHub tags + $Destination = Join-Path $TargetPath $GitHubVersion + } + elseif($PSVersionTable.PSVersion -ge '5.0' -and $TargetType -eq 'Parallel') + { + # For GitHub branches + $Destination = Join-Path $TargetPath $DependencyVersion + $Destination = Join-Path $Destination $DependencyName + } + else + { + $Destination = $TargetPath + } + + if($Force -and (Test-Path -Path $Destination)) + { + Remove-Item -Path $Destination -Force -Recurse + } + + Write-Verbose "Copying [$($ToCopy.Count)] items to destination [$Destination] with`nTarget [$TargetPath]`nName [$DependencyName]`nVersion [$DependencyVersion]`nGitHubVersion [$GitHubVersion]" + + foreach($Item in $ToCopy) + { + Copy-Item -Path $Item -Destination $Destination -Force -Recurse + } + + # Delete the temporary folder + Remove-Item (Get-Item $OutPath).parent.FullName -Force -Recurse + $ModuleExisting = $true +} + +# Conditional import +if($ModuleExisting) +{ + Import-PSDependModule -Name $TargetPath -Action $PSDependAction +} +elseif($PSDependAction -contains 'Import') +{ + Write-Warning "[$DependencyName] at [$TargetPath] should be imported, but does not exist" +} + +# Return true or false if Test action is wanted +if($PSDependAction -contains 'Test') +{ + return $ModuleExistingMatches +} + +# Otherwise return null +return $null diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Noop.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Noop.ps1 new file mode 100644 index 0000000..570c7a5 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Noop.ps1 @@ -0,0 +1,34 @@ +<# + .SYNOPSIS + Display variables that a dependency script would receive. + + Used for testing and validation. + + .DESCRIPTION + Display variables that a dependency script would receive. + + Used for testing and validation. + + .PARAMETER Dependency + Dependency to process + + .PARAMETER StringParameter + An example parameter that does nothing +#> +[cmdletbinding()] +param ( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [string[]]$StringParameter +) + +Write-Verbose "Starting noop run with $($Dependency.count) sources" + +[pscustomobject]@{ + PSBoundParameters = $PSBoundParameters + Dependency= $Dependency + DependencyParameters = $Dependency.Parameters + GetVariable = (Get-Variable) + ENV = Get-Childitem ENV: +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Npm.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Npm.ps1 new file mode 100644 index 0000000..8f8e51b --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Npm.ps1 @@ -0,0 +1,118 @@ +<# + .SYNOPSIS + Install a node package from NPM. + + .DESCRIPTION + Install a node package from NPM. + + Note: We require npm in your path. + + Relevant Dependency metadata: + DependencyName (Key): Node Package Name + Version: Version of the node package to install; defaults to latest. + Target: Path to place the node_modules folder, and all relevant packages, in. + You can specify a full path, a UNC path, or a relative path from the + current directory. You can also specify the special keyword, 'Global', + which will cause the node package to be installed globally for the + user who runs PSDepend against this dependency. + + .PARAMETER Dependency + Dependency to process + + .PARAMETER Global + If specified, the node package will be installed globally. + + .PARAMETER PSDependAction + Test or Install the dependency. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + + .EXAMPLE + @{ + 'gitbook-cli' = @{ + DependencyType = 'Npm' + Version = '0.1.0' + Target = 'Global' + } + } + + # Full syntax + # DependencyName (key) uses (unique) name 'gitbook-cli' + # Specify a version to install + # Ensure the package is installed globally. + + .EXAMPLE + @{ + 'gitbook-cli' = @{ + DependencyType = 'Npm' + } + } + + # Simple syntax + # The example package, 'gitbook-cli' will be installed + at the latest verion from NPM to the current directory. + +#> +[cmdletbinding()] +param ( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [ValidateSet('Test', 'Install')] + [string[]]$PSDependAction = @('Install'), + [switch]$Force, + [switch]$Global +) +#region Extract Dependency Data + $Name = $Dependency.DependencyName + $Version = $Dependency.Version + $Target = $Dependency.Target + If (-not [string]::IsNullOrEmpty($Target) -and $Target -ne 'global') { + # If the target matches a full path or UNC path, don't modify it; + # Otherwise, assume that its a folder _in the current directory_. + # If no target is specified, it will install to the current directory. + If ($Target -notmatch '(^/|:|\\\\)') { + $Target = "$PWD\$Target" + } + If (-not (Test-Path $Target) -and $PSDependAction -contains 'Install') { + Write-Verbose "Creating folder [$Target] for node module dependency [$Name]" + $null = New-Item -ItemType directory -Path $Target -Force + } + } +#endregion Extract Dependency Data +#region Test Action + If ($PSDependAction -contains 'Test') { + $PackageListArguments = 'ls --json --silent' + If ([string]::IsNullOrEmpty($Target)) { + $InstalledNodeModules = Get-NodeModule + } ElseIf ($Target -eq 'global') { + $InstalledNodeModules = Get-NodeModule -Global + } Else { + Push-Location $Target + $InstalledNodeModules = Get-NodeModule + Pop-Location + } + $InstalledModule = $InstalledNodeModules.$Name + If ($InstalledModule -eq $null) { + return $false + } ElseIf ($Version -ne $null -and $InstalledModule.Version -ne $Version) { + return $false + } Else { + return $true + } + } +#endregion Test Action +#region Install Action + If ($PSDependAction -contains 'Install') { + If ([string]::IsNullOrEmpty($Target)) { + $null = Install-NodeModule -PackageName $Name -Version $Version + } ElseIf ($Target -eq 'global') { + $null = Install-NodeModule -PackageName $Name -Version $Version -Global + } Else { + Push-Location $Target + $null = Install-NodeModule -PackageName $Name -Version $Version + Pop-Location + } + } +#endregion Install Action \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Nuget.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Nuget.ps1 new file mode 100644 index 0000000..68aff50 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Nuget.ps1 @@ -0,0 +1,211 @@ +<# + .SYNOPSIS + Installs a package from a Nuget repository like Nuget.org using nuget.exe + + .DESCRIPTION + Installs a package from a Nuget repository like Nuget.org using nuget.exe + + Relevant Dependency metadata: + Name: The name of the package + Version: Used to identify existing installs meeting this criteria. Defaults to 'latest' + Source: Source Uri for Nuget. Defaults to https://www.nuget.org/api/v2/ + Target: Required path to save this module. No Default + Example: To install PSDeploy to C:\temp\PSDeploy, I would specify C:\temp + + .PARAMETER Force + If specified and Target already exists, remove existing item before saving + + .PARAMETER PSDependAction + Test, or Install the package. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + + .EXAMPLE + + @{ + 'Newtonsoft.Json' = @{ + DependencyType = 'Nuget' + Target = 'C:\Temp' + Version = '12.0.2' + } + } + + # Install Newtonsoft.Json via Nuget.org, to C:\temp, at version 12.0.2 + + .EXAMPLE + + @{ + 'MyCompany.Models' = @{ + DependencyType = 'Nuget' + Source = 'https://nuget.int.feed/' + Target = 'C:\Temp' + } + } + + # Install the latest version of MyCompany.Models on an internal nuget feed, to C:\temp + + .EXAMPLE + + @{ + PSDependOptions = @{ + DependencyType = 'Nuget' + Target = ".\Staging" + } + 'Portable.BouncyCastle' = @{ + Version = 'latest' + Parameters = @{ + Name = 'BouncyCastle.Crypto' + } + } + 'MimeKit' = 'latest' + 'Newtonsoft.Json' = 'latest' + } + + # Installs the list of Nuget packages from Nuget.org using the Global PSDependOptions to limit repetition. Packages will be downloaded to the Staging directory in the current working directory. Since the DLL included with Portable.BouncyCastle is actually named 'BouncyCastle.Crypto', we specify that in the parameters. + +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [switch]$Force, + + [ValidateSet('Test', 'Install')] + [string[]]$PSDependAction = @('Install'), + + [Alias('DLLName')] + [string]$Name +) +# Extract data from Dependency + $DependencyName = $Dependency.DependencyName + if ($null -ne $Dependency.Name) + { + $DependencyName = $Dependency.Name + } + + $Version = $Dependency.Version + if(-not $Version) + { + $Version = 'latest' + } + + $Source = $Dependency.Source + if(-not $Dependency.Source) + { + $Source = 'https://www.nuget.org/api/v2/' + } + + # We use target as a proxy for Scope + $Target = $Dependency.Target + if(-not $Dependency.Target) + { + Write-Error "Nuget requires a Dependency Target. Skipping [$DependencyName]" + return + } + + $Credential = $Dependency.Credential + +if(-not (Get-Command Nuget -ErrorAction SilentlyContinue)) +{ + Write-Error "Nuget requires Nuget.exe. Ensure this is in your path, or explicitly specified in $ModuleRoot\PSDepend.Config's NugetPath. Skipping [$DependencyName]" +} + +Write-Verbose -Message "Getting dependency [$DependencyName] from Nuget source [$Source]" + +# This code works for both install and save scenarios. +$PackagePath = Join-Path $Target $DependencyName + +$NameIs = if ($PSBoundParameters.ContainsKey('Name')) { + $Name +} +else { + $DependencyName +} + +if(Test-Path $PackagePath) +{ + if($null -eq (Get-ChildItem $PackagePath -Filter "$NameIs*" -Include '*.exe', '*.dll' -Recurse)) + { + # For now, skip if we don't find a DLL matching the expected name + Write-Error "Could not find existing DLL for dependency [$DependencyName] in package path [$PackagePath]" + return + } + + Write-Verbose "Found existing package [$DependencyName]" + + # Thanks to Brandon Padgett! + $Path = (Get-ChildItem $PackagePath -Filter "$NameIs*" -Include '*.exe', '*.dll' -Recurse | Select-Object -First 1).FullName + $ExistingVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($Path).FileVersion + $GetGalleryVersion = { (Find-NugetPackage -Name $DependencyName -PackageSourceUrl $Source -Credential $Credential -IsLatest).Version } + + # Version string, and equal to current + if( $Version -and $Version -ne 'latest' -and $Version -eq $ExistingVersion) + { + Write-Verbose "You have the requested version [$Version] of [$DependencyName]" + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + + # latest, and we have latest + if( $Version -and + ($Version -eq 'latest' -or $Version -like '') -and + ($GalleryVersion = [System.Version](& $GetGalleryVersion)) -le [System.Version]$ExistingVersion + ) + { + Write-Verbose "You have the latest version of [$DependencyName], with installed version [$ExistingVersion] and Source version [$GalleryVersion]" + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + + Write-Verbose "Removing existing [$PackagePath]`nContinuing to install [$DependencyName]: Requested version [$version], existing version [$ExistingVersion]" + if($PSDependAction -contains 'Install') + { + if($Force) + { + Remove-Item $PackagePath -Force -Recurse + } + else + { + Write-Verbose "Use -Force to remove existing [$PackagePath]`nSkipping install of [$DependencyName]: Requested version [$version], existing version [$ExistingVersion]" + if( $PSDependAction -contains 'Test') + { + return $false + } + return $null + } + } +} + +#No dependency found, return false if we're testing alone... +if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) +{ + return $False +} + +if($PSDependAction -contains 'Install') +{ + $TargetExists = Test-Path $Target -PathType Container + + Write-Verbose "Saving [$DependencyName] with path [$Target]" + $NugetParams = '-Source', $Source, '-ExcludeVersion', '-NonInteractive', '-OutputDirectory', $Target + if(-not $TargetExists) + { + Write-Verbose "Creating directory path to [$Target]" + $Null = New-Item -ItemType Directory -Path $Target -Force -ErrorAction SilentlyContinue + } + if($Version -and $Version -notlike 'latest') + { + $NugetParams += '-version', $Version + } + $NugetParams = 'install', $DependencyName + $NugetParams + + Invoke-ExternalCommand Nuget -Arguments $NugetParams +} diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/PSGalleryModule.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/PSGalleryModule.ps1 new file mode 100644 index 0000000..427f164 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/PSGalleryModule.ps1 @@ -0,0 +1,318 @@ +<# + .SYNOPSIS + Installs a module from a PowerShell repository like the PowerShell Gallery. + + .DESCRIPTION + Installs a module from a PowerShell repository like the PowerShell Gallery. + + Relevant Dependency metadata: + Name: The name for this module + Version: Used to identify existing installs meeting this criteria, and as RequiredVersion for installation. Defaults to 'latest' + Target: Used as 'Scope' for Install-Module. If this is a path, we use Save-Module with this path. Defaults to 'AllUsers' + AddToPath: If target is used as a path, prepend that path to ENV:PSModulePath + Credential: The username and password used to authenticate against the private repository + + If you don't have the Nuget package provider, we install it for you + + .PARAMETER Repository + PSRepository to download from. Defaults to PSGallery + + .PARAMETER SkipPublisherCheck + Bypass the catalog signing check. Defaults to $false + + .PARAMETER AllowClobber + Allow installation of modules that clobber existing commands. Defaults to $True + + .PARAMETER AcceptLicense + Accepts the license agreement during installation. + + .PARAMETER AllowPrerelease + If specified, allow for prerelease. + + If specified along with version 'latest', a prerelease will be selected if it is the latest version + + Sorting assumes you name prereleases appropriately (i.e. alpha < beta < gamma) + + .PARAMETER Import + If specified, import the module in the global scope + + Deprecated. Moving to PSDependAction + + .PARAMETER PSDependAction + Test, Install, or Import the module. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + Import: Import the dependency + + .EXAMPLE + @{ + BuildHelpers = 'latest' + PSDeploy = '' + InvokeBuild = '3.2.1' + } + + # From the PSGallery repository (PowerShellGallery.com)... + # Install the latest BuildHelpers and PSDeploy ('latest' and '' both evaluate to latest) + # Install version 3.2.1 + + .EXAMPLE + @{ + BuildHelpers = @{ + Target = 'C:\Build' + } + } + + # Install the latest BuildHelpers module from PSGallery to C:\Build (i.e. C:\Build\BuildHelpers will be the module folder) + # No version is specified - we assume latest in this case. + + .EXAMPLE + @{ + BuildHelpers = @{ + Parameters @{ + Repository = 'PSPrivateGallery' + SkipPublisherCheck = $true + } + } + } + + # Install the latest BuildHelpers module from a custom gallery* that you registered, and bypass the catalog signing check + # No version is specified - we assume latest in this case. + + # * Perhaps you use this https://github.com/PowerShell/PSPrivateGallery, or Artifactory, ProGet, etc. + + .EXAMPLE + @{ + 'vmware.powercli' = @{ + Parameters = @{ + AllowPrerelease = $True + } + } + } + # Install the latest version of PowerCLI, allowing for prerelease +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [AllowNull()] + [string]$Repository = 'PSGallery', # From Parameters... + + [bool]$SkipPublisherCheck, # From Parameters... + + [bool]$AllowClobber = $True, + + [bool]$AcceptLicense, + + [bool]$AllowPrerelease, + + [switch]$Import, + + [ValidateSet('Test', 'Install', 'Import')] + [string[]]$PSDependAction = @('Install') +) + +# Extract data from Dependency + $DependencyName = $Dependency.DependencyName + $Name = $Dependency.Name + if(-not $Name) + { + $Name = $DependencyName + } + + $Version = $Dependency.Version + if(-not $Version) + { + $Version = 'latest' + } + + # We use target as a proxy for Scope + if(-not $Dependency.Target) + { + $Scope = 'AllUsers' + } + else + { + $Scope = $Dependency.Target + } + + $Credential = $Dependency.Credential + + if('AllUsers', 'CurrentUser' -notcontains $Scope) + { + $command = 'save' + } + else + { + $command = 'install' + } + +if(-not (Get-PackageProvider -Name Nuget)) +{ + # Grab nuget bits. + $null = Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null +} + +Write-Verbose -Message "Getting dependency [$name] from PowerShell repository [$Repository]" + +# Validate that $target has been setup as a valid PowerShell repository, +# but allow to rely on all PS repos registered. +if($Repository) { + $validRepo = Get-PSRepository -Name $Repository -Verbose:$false -ErrorAction SilentlyContinue + if (-not $validRepo) { + Write-Error "[$Repository] has not been setup as a valid PowerShell repository." + return + } +} + +$params = @{ + Name = $Name + SkipPublisherCheck = $SkipPublisherCheck + AllowClobber = $AllowClobber + Verbose = $VerbosePreference + Force = $True +} + +if($PSBoundParameters.ContainsKey('AllowPrerelease')){ + $params.Add('AllowPrerelease', $AllowPrerelease) +} + +if($PSBoundParameters.ContainsKey('AcceptLicense')){ + $params.Add('AcceptLicense', $AcceptLicense) +} + +if($Repository) { + $params.Add('Repository',$Repository) +} + +if($Version -and $Version -ne 'latest') +{ + $Params.add('RequiredVersion', $Version) +} + +if($Credential) +{ + $Params.add('Credential', $Credential) +} + +# This code works for both install and save scenarios. +if($command -eq 'Save') +{ + $ModuleName = Join-Path $Scope $Name + $Params.Remove('AllowClobber') + $Params.Remove('SkipPublisherCheck') +} +elseif ($Command -eq 'Install') +{ + $ModuleName = $Name +} + +# Only use "SkipPublisherCheck" (and other) parameter if "Install-Module" supports it +$availableParameters = (Get-Command "Install-Module").Parameters +$tempParams = $Params.Clone() +foreach($thisParameter in $Params.Keys) +{ + if(-Not ($availableParameters.ContainsKey($thisParameter))) + { + Write-Verbose -Message "Removing parameter [$thisParameter] from [Install-Module] as it is not available" + $tempParams.Remove($thisParameter) + } +} +$Params = $tempParams.Clone() + +Add-ToPsModulePathIfRequired -Dependency $Dependency -Action $PSDependAction + +$Existing = $null +$Existing = Get-Module -ListAvailable -Name $ModuleName -ErrorAction SilentlyContinue + +if($Existing) +{ + Write-Verbose "Found existing module [$Name]" + # Thanks to Brandon Padgett! + $ExistingVersion = $Existing | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum + $FindModuleParams = @{Name = $Name} + if($Repository) { + $FindModuleParams.Add('Repository', $Repository) + } + if($Credential) + { + $FindModuleParams.Add('Credential', $Credential) + } + if($AllowPrerelease) + { + $FindModuleParams.Add('AllowPrerelease', $AllowPrerelease) + } + + # Version string, and equal to current + if($Version -and $Version -ne 'latest' -and $Version -eq $ExistingVersion) + { + Write-Verbose "You have the requested version [$Version] of [$Name]" + # Conditional import + Import-PSDependModule -Name $ModuleName -Action $PSDependAction -Version $ExistingVersion + + if($PSDependAction -contains 'Test') + { + return $true + } + return $null + } + + $GalleryVersion = Find-Module @FindModuleParams | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum + [System.Version]$parsedVersion = $null + [System.Management.Automation.SemanticVersion]$parsedSemanticVersion = $null + [System.Management.Automation.SemanticVersion]$parsedTempSemanticVersion = $null + $isGalleryVersionLessEquals = if ( + [System.Management.Automation.SemanticVersion]::TryParse($ExistingVersion, [ref]$parsedSemanticVersion) -and + [System.Management.Automation.SemanticVersion]::TryParse($GalleryVersion, [ref]$parsedTempSemanticVersion) + ) { + $GalleryVersion -le $parsedSemanticVersion + } + elseif ([System.Version]::TryParse($ExistingVersion, [ref]$parsedVersion)) { + $GalleryVersion -le $parsedVersion + } + + # latest, and we have latest + if( $Version -and ($Version -eq 'latest' -or $Version -eq '') -and $isGalleryVersionLessEquals) + { + Write-Verbose "You have the latest version of [$Name], with installed version [$ExistingVersion] and PSGallery version [$GalleryVersion]" + # Conditional import + Import-PSDependModule -Name $ModuleName -Action $PSDependAction -Version $ExistingVersion + + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + Write-Verbose "Continuing to install [$Name]: Requested version [$version], existing version [$ExistingVersion]" +} + +#No dependency found, return false if we're testing alone... +if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) +{ + return $False +} + +if($PSDependAction -contains 'Install') +{ + if('AllUsers', 'CurrentUser' -contains $Scope) + { + Write-Verbose "Installing [$Name] with scope [$Scope]" + Install-Module @params -Scope $Scope + } + else + { + Write-Verbose "Saving [$Name] with path [$Scope]" + Write-Verbose "Creating directory path to [$Scope]" + if(-not (Test-Path $Scope -ErrorAction SilentlyContinue)) + { + $Null = New-Item -ItemType Directory -Path $Scope -Force -ErrorAction SilentlyContinue + } + Save-Module @params -Path $Scope + } +} + +# Conditional import +$importVs = $params['RequiredVersion'] +Import-PSDependModule -Name $ModuleName -Action $PSDependAction -Version $importVs diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/PSGalleryNuget.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/PSGalleryNuget.ps1 new file mode 100644 index 0000000..0d94777 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/PSGalleryNuget.ps1 @@ -0,0 +1,205 @@ +<# + .SYNOPSIS + Installs a module from a PowerShell repository like the PowerShell Gallery using nuget.exe + + .DESCRIPTION + Installs a module from a PowerShell repository like the PowerShell Gallery using nuget.exe + + Note: If we find an existing module that doesn't meet the specified criteria in the Target, we remove it. + + Relevant Dependency metadata: + Name: The name for this module + Version: Used to identify existing installs meeting this criteria, and as RequiredVersion for installation. Defaults to 'latest' + Source: Source Uri for Nuget. Defaults to https://www.powershellgallery.com/api/v2/ + Target: Required path to save this module. No Default + Example: To install PSDeploy to C:\temp\PSDeploy, I would specify C:\temp + AddToPath: Prepend the Target to ENV:PSModulePath + + .PARAMETER Force + If specified and Target already exists, remove existing item before saving + + .PARAMETER Import + If specified, import the module in the global scope + + .PARAMETER PSDependAction + Test, Install, or Import the module. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + Import: Import the dependency + + .EXAMPLE + + @{ + PSDeploy = @{ + DependencyType = 'PSGalleryNuget' + Target = 'C:\Temp' + Version = '0.1.19' + } + } + + # Install PSDeploy via nuget PSGallery feed, to C:\temp, at version 0.1.19 + + .EXAMPLE + + @{ + PSDeploy = @{ + DependencyType = 'PSGalleryNuget' + Source = 'https://nuget.int.feed/' + Target = 'C:\Temp' + } + } + + # Install the latest version of PSDeploy on an internal nuget feed, to C:\temp, + +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [switch]$Force, + + [switch]$Import, + + [ValidateSet('Test', 'Install', 'Import')] + [string[]]$PSDependAction = @('Install') +) +# Extract data from Dependency + $DependencyName = $Dependency.DependencyName + $Name = $Dependency.Name + if(-not $Name) + { + $Name = $DependencyName + } + + $Version = $Dependency.Version + if(-not $Version) + { + $Version = 'latest' + } + + $Source = $Dependency.Source + if(-not $Dependency.Source) + { + $Source = 'https://www.powershellgallery.com/api/v2/' + } + + # We use target as a proxy for Scope + $Target = $Dependency.Target + if(-not $Dependency.Target) + { + Write-Error "PSGalleryNuget requires a Dependency Target. Skipping [$DependencyName]" + return + } + + $Credential = $Dependency.Credential + +if(-not (Get-Command Nuget -ErrorAction SilentlyContinue)) +{ + Write-Error "PSGalleryNuget requires Nuget.exe. Ensure this is in your path, or explicitly specified in $ModuleRoot\PSDepend.Config's NugetPath. Skipping [$DependencyName]" +} + +Write-Verbose -Message "Getting dependency [$name] from Nuget source [$Source]" + +# This code works for both install and save scenarios. +$ModulePath = Join-Path $Target $Name + +Add-ToPsModulePathIfRequired -Dependency $Dependency -Action $PSDependAction + +if(Test-Path $ModulePath) +{ + $Manifest = Join-Path $ModulePath "$Name.psd1" + if(-not (Test-Path $Manifest)) + { + # For now, skip if we don't find a psd1 + Write-Error "Could not find manifest [$Manifest] for dependency [$Name]" + return + } + + Write-Verbose "Found existing module [$Name]" + + # Thanks to Brandon Padgett! + $ManifestData = Import-LocalizedData -BaseDirectory $ModulePath -FileName "$Name.psd1" + $ExistingVersion = $ManifestData.ModuleVersion + $GetGalleryVersion = { (Find-NugetPackage -Name $Name -PackageSourceUrl $Source -Credential $Credential -IsLatest).Version } + + # Version string, and equal to current + if( $Version -and $Version -ne 'latest' -and $Version -eq $ExistingVersion) + { + Write-Verbose "You have the requested version [$Version] of [$Name]" + # Conditional import + Import-PSDependModule -Name $ModulePath -Action $PSDependAction -Version $ExistingVersion + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + + # latest, and we have latest + if( $Version -and + ($Version -eq 'latest' -or $Version -like '') -and + ($GalleryVersion = (& $GetGalleryVersion)) -le $ExistingVersion + ) + { + Write-Verbose "You have the latest version of [$Name], with installed version [$ExistingVersion] and PSGallery version [$GalleryVersion]" + # Conditional import + Import-PSDependModule -Name $ModulePath -Action $PSDependAction -Version $ExistingVersion + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + + Write-Verbose "Removing existing [$ModulePath]`nContinuing to install [$Name]: Requested version [$version], existing version [$ExistingVersion]" + if($PSDependAction -contains 'Install') + { + if($Force) + { + Remove-Item $ModulePath -Force -Recurse + } + else + { + Write-Verbose "Use -Force to remove existing [$ModulePath]`nSkipping install of [$Name]: Requested version [$version], existing version [$ExistingVersion]" + if( $PSDependAction -contains 'Test') + { + return $false + } + return $null + } + } +} + +#No dependency found, return false if we're testing alone... +if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) +{ + return $False +} + +if($PSDependAction -contains 'Install') +{ + $TargetExists = Test-Path $Target -PathType Container + + Write-Verbose "Saving [$Name] with path [$Target]" + $NugetParams = '-Source', $Source, '-ExcludeVersion', '-NonInteractive', '-OutputDirectory', $Target + if(-not $TargetExists) + { + Write-Verbose "Creating directory path to [$Target]" + $Null = New-Item -ItemType Directory -Path $Target -Force -ErrorAction SilentlyContinue + } + if($Version -and $Version -notlike 'latest') + { + $NugetParams += '-version', $Version + } + $NugetParams = 'install', $Name + $NugetParams + + Invoke-ExternalCommand nuget -Arguments $NugetParams +} + +# Conditional import +$importVs = if ($Version -and $Version -notlike 'latest') { + $Version +} +Import-PSDependModule -Name $ModulePath -Action $PSDependAction -Version $importVs \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Package.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Package.ps1 new file mode 100644 index 0000000..00c731a --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Package.ps1 @@ -0,0 +1,213 @@ +<# + .SYNOPSIS + EXPERIMENTAL: Installs a package using the PackageManagement module + + .DESCRIPTION + EXPERIMENTAL: Installs a package using the PackageManagement module + + Relevant Dependency metadata: + Name: The name for this Package + Version: Used to identify existing installs meeting this criteria, and as RequiredVersion for installation. Defaults to 'latest' + Target: Used as 'Scope' for PowerShellGet provider, Destination for Nuget provider + Source: Package source to use (Get-PackageSource, Register-PackageSource) + Parameters: Every parameter you specify is splatted against Install-Package + + If you don't have the Nuget package provider, we install it for you + + .PARAMETER ProviderName + Optionally specify the Provider name to use (Get-PackageProvider, Register-PackageProvider) + + .PARAMETER PSDependAction + Test, Install, or Import the module. Defaults to Install + + Test: Return true or false on whether the dependency is in place + Install: Install the dependency + + .EXAMPLE + @{ + jquery = @{ + DependencyType = 'Package' + Target = 'C:\MyProject' + Source = 'nuget.org' + } + } + + # Install jquery from the nuget.org PackageSource to C:\MyProject + # IMPORTANT: Only certain providers support specifying a target destination + + .EXAMPLE + @{ + jquery = @{ + DependencyType = 'Package' + Source = 'https://my.internal.nuget.feed/api' + Target = 'C:\MyProject' + Parameters = @{ + ProviderName = 'nuget' + } + } + } + + # Install jquery from my internal nuget feed to C:\MyProject + +#> +[cmdletbinding()] +param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [ValidateSet('Test', 'Install')] + [string[]]$PSDependAction = @('Install'), + + [String]$ProviderName +) + +# Extract data from Dependency + $DependencyName = $Dependency.DependencyName + $Source = $Dependency.Source + $Name = $Dependency.Name + if(-not $Name) + { + $Name = $DependencyName + } + + $Version = $Dependency.Version + if(-not $Version) + { + $Version = 'latest' + } + +$PackageSources = @( Get-PackageSource ) +if($PackageSources.Name -notcontains $Source -and -not $PSBoundParameters.ContainsKey('ProviderName')) +{ + Write-Error "PackageSource [$Source] is not valid. Valid sources:`n$($PackageSources.ProviderName | Out-String)" + return +} + +$PackageProviders = @( Get-PackageProvider ) +if($PSBoundParameters.ContainsKey('ProviderName') -and $PackageProviders.Name -notcontains $ProviderName) +{ + Write-Error "ProviderName [$ProviderName] is not valid. Valid sources:`n$($PackageProviders.Name | Out-String)" + return +} + +Write-Verbose -Message "Getting dependency [$name] from Package source [$Source]" + +if($PSBoundParameters.ContainsKey('ProviderName')) +{ + $ThisProvider = $ProviderName +} +else # Pick providername from this packagesource +{ + $ThisProvider = $PackageSources | Where-Object {$_.Name -eq $Source} | Select-Object -ExpandProperty ProviderName +} + +$GetParam = @{ + Name = $Name + ProviderName = $ThisProvider + ErrorAction = 'SilentlyContinue' +} +$InstallParam = @{ + Name = $Name + Source = $Source + Force = $True +} +if($Version -notlike 'latest') +{ + $GetParam.add('RequiredVersion', $Version) + $InstallParam.add('RequiredVersion', $Version) +} + +# Parse target for PowerShellGet +If($ThisProvider -eq 'PowerShellGet') +{ + $ValidScope = 'CurrentUser', 'AllUsers' + if(-not $Dependency.Target) + { + $Scope = 'AllUsers' + $InstallParam.Add('Scope', $Scope) + } + elseif($ValidScope -contains $Scope) + { + $Scope = $Dependency.Target + $InstallParam.Add('Scope', $Scope) + } +} +# Parse target for Nuget +if($ThisProvider -eq 'Nuget') +{ + if(-not $Dependency.Target) + { + throw 'Nuget provider requires that you specify a target destination. Use the dependency Target for this.' + } + $GetParam.Add('Destination', $Dependency.Target) + $InstallParam.Add('Destination', $Dependency.Target) +} + + +# Add arbitrary keys to support DynamicOptions... +if($Dependency.Parameters.Keys.Count -gt 0) +{ + foreach($Key in $Dependency.Parameters.Keys) + { + if(-not $InstallParam.ContainsKey($Key)) + { + $InstallParam.add($Key, $Dependency.Parameters.$Key) + } + else + { + $InstallParam.$Key = $Dependency.Parameters.$Key + } + } +} + +$Existing = $null +Write-Verbose "Running Get-Package with $($GetParam | Out-String)" +$Existing = Get-Package @GetParam + +if($Existing) +{ + Write-Verbose "Found existing package [$Name]" + # Thanks to Brandon Padgett! + $ExistingVersion = $Existing | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum + $GetSourceVersion = { Find-Package -Name $Name -Source $Source | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum } + + # Version string, and equal to current + if( $Version -and $Version -ne 'latest' -and $Version -eq $ExistingVersion) + { + Write-Verbose "You have the requested version [$Version] of [$Name]" + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + + # latest, and we have latest + if( $Version -and + ($Version -eq 'latest' -or $Version -like '') -and + ($SourceVersion = (& $GetSourceVersion)) -le $ExistingVersion + ) + { + Write-Verbose "You have the latest version of [$Name], with installed version [$ExistingVersion] and package source version [$SourceVersion]" + if($PSDependAction -contains 'Test') + { + return $True + } + return $null + } + + Write-Verbose "Continuing to install [$Name]: Requested version [$version], existing version [$ExistingVersion]" +} + +#No dependency found, return false if we're testing alone... +if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1) +{ + return $False +} + +if($PSDependAction -contains 'Install') +{ + Write-Verbose "Installing [$Name] with params $($InstallParam | Out-String)" + Install-Package @InstallParam +} + diff --git a/.build/modules/PSDepend/0.3.8/PSDependScripts/Task.ps1 b/.build/modules/PSDepend/0.3.8/PSDependScripts/Task.ps1 new file mode 100644 index 0000000..ea26c9a --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSDependScripts/Task.ps1 @@ -0,0 +1,77 @@ +<# + .SYNOPSIS + Support dependencies by handling simple tasks. + + .DESCRIPTION + Support dependencies by handling simple tasks. + + Relevant Dependency metadata: + Target: One or more scripts to run for this task + Parameters: Parameters to call against the task scripts + + .PARAMETER PSDependAction + Only option is to install the module. Defaults to Install + + Install: Install the dependency + + .PARAMETER Dependency + Dependency to process + + .EXAMPLE + # Assumption: you prestage a script somewhere or include it in your solution + Set-Content C:\Example.ps1 ' "Running a task on $(hostname)" ' + + # Dependency syntax with C:\Example.ps1 already in place + @{ + ExampleTask = @{ + DependencyType = 'Task' + Target = 'C:\Example.ps1' + } + } + + # Run C:\Example.ps1 + # Output: Running a task on WJ-LAB + + .EXAMPLE + + # Dependency syntax with $PWD\Example.ps1 already in place + @{ + ExampleTask = @{ + DependencyType = 'Task' + Target = '$PWD\Example.ps1' + } + } + + # Run Example.ps1 from the current directory + # Alternatively, you can use $DependencyPath to refer to the folder containing this dependency file +#> +[cmdletbinding()] +param ( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [ValidateSet('Install')] + [string[]]$PSDependAction = @('Install') # No logic for this +) + +Write-Verbose "Executing $($Dependency.count) tasks" + +foreach($Depend in $Dependency) +{ + foreach($Task in $Depend.Source) + { + if(Test-Path $Task -PathType Leaf) + { + $params = @{} + if($Depend.Parameters) + { + $params += $Depend.Parameters + } + . $Task @params + } + else + { + Write-Warning "Could not find task file [$Task] from dependency [$($Depend.DependencyName)]" + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/PSGetModuleInfo.xml b/.build/modules/PSDepend/0.3.8/PSGetModuleInfo.xml new file mode 100644 index 0000000..4c06c9a --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/PSGetModuleInfo.xml @@ -0,0 +1,139 @@ + + + + Microsoft.PowerShell.Commands.PSRepositoryItemInfo + System.Management.Automation.PSCustomObject + System.Object + + + PSDepend + 0.3.8 + Module + PowerShell Dependency Handler + Warren Frame + ramblingcookiemonster + (c) 2016 Warren F. All rights reserved. +
2020-07-05T00:54:32+01:00
+ + + https://github.com/RamblingCookieMonster/PSDepend/blob/master/LICENSE + https://github.com/RamblingCookieMonster/PSDepend/ + + + + System.Object[] + System.Array + System.Object + + + requirements + dependencies + dependency + manager + bundle + package + PSModule + + + + + System.Collections.Hashtable + System.Object + + + + DscResource + + + + + + + RoleCapability + + + + Command + + + + Get-Dependency + Get-PSDependScript + Get-PSDependType + Import-Dependency + Install-Dependency + Invoke-DependencyScript + Invoke-PSDepend + Test-Dependency + + + + + Function + + + + Get-Dependency + Get-PSDependScript + Get-PSDependType + Import-Dependency + Install-Dependency + Invoke-DependencyScript + Invoke-PSDepend + Test-Dependency + + + + + Workflow + + + + Cmdlet + + + + + + Added various PowerShell Core fixes thanks to @lipkau! + + + + + https://www.powershellgallery.com/api/v2 + PSGallery + NuGet + + + System.Management.Automation.PSCustomObject + System.Object + + + (c) 2016 Warren F. All rights reserved. + PowerShell Dependency Handler + False + Added various PowerShell Core fixes thanks to @lipkau! + True + True + 1337690 + 1695967 + 78660 + 05/07/2020 00:54:32 +01:00 + 05/07/2020 00:54:32 +01:00 + 10/04/2026 12:20:04 +01:00 + requirements dependencies dependency manager bundle package PSModule PSFunction_Get-Dependency PSCommand_Get-Dependency PSFunction_Get-PSDependScript PSCommand_Get-PSDependScript PSFunction_Get-PSDependType PSCommand_Get-PSDependType PSFunction_Import-Dependency PSCommand_Import-Dependency PSFunction_Install-Dependency PSCommand_Install-Dependency PSFunction_Invoke-DependencyScript PSCommand_Invoke-DependencyScript PSFunction_Invoke-PSDepend PSCommand_Invoke-PSDepend PSFunction_Test-Dependency PSCommand_Test-Dependency PSIncludes_Function + False + 2026-04-10T12:20:04Z + 0.3.8 + Warren Frame + false + Module + PSDepend.nuspec|PSDepend.Config|PSDepend.Format.ps1xml|PSDepend.psd1|PSDepend.psm1|PSDependMap.psd1|en-US\about_PSDepend.help.txt|en-US\about_PSDepend_Definitions.help.txt|Private\Add-ObjectDetail.ps1|Private\Add-ToItemCollection.ps1|Private\Add-ToPsModulePathIfRequired.ps1|Private\Bootstrap-Nuget.ps1|Private\Find-NugetPackage.ps1|Private\Get-ClonedObject.ps1|Private\Get-Hash.ps1|Private\Get-NodeModule.ps1|Private\Get-Parameter.ps1|Private\Get-ParameterName.ps1|Private\Get-ProjectDetail.ps1|Private\Get-PropertyOrder.ps1|Private\Get-TaggedDependency.ps1|Private\Get-TopologicalSort.ps1|Private\Get-WebFile.ps1|Private\Import-PSDependModule.ps1|Private\Install-Dotnet.ps1|Private\Install-NodeModule.ps1|Private\Invoke-ExternalCommand.ps1|Private\Resolve-DependScripts.ps1|Private\Save-NugetPackage.ps1|Private\SemanticVersion.ps1|Private\Sort-PSDependency.ps1|Private\Sort-WithCustomList.ps1|Private\Test-Dotnet.ps1|Private\Test-PlatformSupport.ps1|Private\Validate-DependencyParameters.ps1|PSDependScripts\Chocolatey.ps1|PSDependScripts\Command.ps1|PSDependScripts\DotnetSdk.ps1|PSDependScripts\FileDownload.ps1|PSDependScripts\FileSystem.ps1|PSDependScripts\Git.ps1|PSDependScripts\GitHub.ps1|PSDependScripts\Noop.ps1|PSDependScripts\Npm.ps1|PSDependScripts\Nuget.ps1|PSDependScripts\Package.ps1|PSDependScripts\PSGalleryModule.ps1|PSDependScripts\PSGalleryNuget.ps1|PSDependScripts\Task.ps1|Public\Get-Dependency.ps1|Public\Get-PSDependScript.ps1|Public\Get-PSDependType.ps1|Public\Import-Dependency.ps1|Public\Install-Dependency.ps1|Public\Invoke-DependencyScript.ps1|Public\Invoke-PSDepend.ps1|Public\Test-Dependency.ps1 + 63ea9e2a-320d-43ff-a11a-4930ca03cce6 + 3.0 + + + C:\Users\James\Documents\PSDepend\0.3.8 +
+
+
diff --git a/.build/modules/PSDepend/0.3.8/Private/Add-ObjectDetail.ps1 b/.build/modules/PSDepend/0.3.8/Private/Add-ObjectDetail.ps1 new file mode 100644 index 0000000..0a929e7 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Add-ObjectDetail.ps1 @@ -0,0 +1,167 @@ +function Add-ObjectDetail +{ + <# + .SYNOPSIS + Decorate an object with + - A TypeName + - New properties + - Default parameters + + .DESCRIPTION + Helper function to decorate an object with + - A TypeName + - New properties + - Default parameters + + .PARAMETER InputObject + Object to decorate. Accepts pipeline input. + + .PARAMETER TypeName + Typename to insert. + + This will show up when you use Get-Member against the resulting object. + + .PARAMETER PropertyToAdd + Add these noteproperties. + + Format is a hashtable with Key (Property Name) = Value (Property Value). + + Example to add a One and Date property: + + -PropertyToAdd @{ + One = 1 + Date = (Get-Date) + } + + .PARAMETER DefaultProperties + Change the default properties that show up + + .PARAMETER Passthru + Whether to pass the resulting object on. Defaults to true + + .EXAMPLE + # + # Create an object to work with + $Object = [PSCustomObject]@{ + First = 'Cookie' + Last = 'Monster' + Account = 'CMonster' + } + + #Add a type name and a random property + Add-ObjectDetail -InputObject $Object -TypeName 'ApplicationX.Account' -PropertyToAdd @{ AnotherProperty = 5 } + + # First Last Account AnotherProperty + # ----- ---- ------- --------------- + # Cookie Monster CMonster 5 + + #Verify that get-member shows us the right type + $Object | Get-Member + + # TypeName: ApplicationX.Account ... + + .EXAMPLE + # + # Create an object to work with + $Object = [PSCustomObject]@{ + First = 'Cookie' + Last = 'Monster' + Account = 'CMonster' + } + + #Add a random property, set a default property set so we only see two props by default + Add-ObjectDetail -InputObject $Object -PropertyToAdd @{ AnotherProperty = 5 } -DefaultProperties Account, AnotherProperty + + # Account AnotherProperty + # ------- --------------- + # CMonster 5 + + #Verify that the other properties are around + $Object | Select -Property * + + # First Last Account AnotherProperty + # ----- ---- ------- --------------- + # Cookie Monster CMonster 5 + + .NOTES + This breaks the 'do one thing' rule from certain perspectives... + The goal is to decorate an object all in one shot + + This abstraction simplifies decorating an object, with a slight trade-off in performance. For example: + + 10,000 objects, add a property and typename: + Add-ObjectDetail: ~4.6 seconds + Add-Member + PSObject.TypeNames.Insert: ~3 seconds + + Initial code borrowed from Shay Levy: + http://blogs.microsoft.co.il/scriptfanatic/2012/04/13/custom-objects-default-display-in-powershell-30/ + + .FUNCTIONALITY + PowerShell Language + #> + [CmdletBinding()] + param( + [Parameter( Mandatory = $true, + Position=0, + ValueFromPipeline=$true )] + [ValidateNotNullOrEmpty()] + [psobject[]]$InputObject, + + [Parameter( Mandatory = $false, + Position=1)] + [string]$TypeName, + + [Parameter( Mandatory = $false, + Position=2)] + [System.Collections.Hashtable]$PropertyToAdd, + + [Parameter( Mandatory = $false, + Position=3)] + [ValidateNotNullOrEmpty()] + [Alias('dp')] + [System.String[]]$DefaultProperties, + + [boolean]$Passthru = $True + ) + + Begin + { + if($PSBoundParameters.ContainsKey('DefaultProperties')) + { + # define a subset of properties + $ddps = New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet,$DefaultProperties + $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps + } + } + Process + { + foreach($Object in $InputObject) + { + switch ($PSBoundParameters.Keys) + { + 'PropertyToAdd' + { + foreach($Key in $PropertyToAdd.Keys) + { + #Add some noteproperties. Slightly faster than Add-Member. + $Object.PSObject.Properties.Add( ( New-Object System.Management.Automation.PSNoteProperty($Key, $PropertyToAdd[$Key]) ) ) + } + } + 'TypeName' + { + #Add specified type + [void]$Object.PSObject.TypeNames.Insert(0,$TypeName) + } + 'DefaultProperties' + { + # Attach default display property set + Add-Member -InputObject $Object -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers + } + } + if($Passthru) + { + $Object + } + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Add-ToItemCollection.ps1 b/.build/modules/PSDepend/0.3.8/Private/Add-ToItemCollection.ps1 new file mode 100644 index 0000000..e603e26 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Add-ToItemCollection.ps1 @@ -0,0 +1,19 @@ +function Add-ToItemCollection { + param( + $Delimiter = [IO.Path]::PathSeparator, + $Reference, # e.g. ENV:Path + $Item, # e.g. 'C:\Project', + [switch]$Append + ) + + $Existing = ( Get-Item -Path $Reference ).Value -split $Delimiter | Where-Object {$_ -ne $Item} + if($Append) + { + $ToAdd = ( @($Existing) + $Item | Select-Object -Unique ) -join $Delimiter + } + else + { + $ToAdd = ( @($Item) + @($Existing) | Select-Object -Unique ) -join $Delimiter + } + Set-Item -Path $Reference -Value $ToAdd +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Add-ToPsModulePathIfRequired.ps1 b/.build/modules/PSDepend/0.3.8/Private/Add-ToPsModulePathIfRequired.ps1 new file mode 100644 index 0000000..2ea4323 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Add-ToPsModulePathIfRequired.ps1 @@ -0,0 +1,30 @@ +function Add-ToPsModulePathIfRequired { + [cmdletbinding()] + param( + [PSTypeName('PSDepend.Dependency')] + [psobject]$Dependency, + [string[]]$Action + ) + + process { + $path = $Dependency.Target + if ([string]::IsNullOrWhiteSpace($path) -or -not($Dependency.AddToPath)) { + return + } + if ('AllUsers', 'CurrentUser' -contains $path) { + return + } + $isInstallOrImport = @($Action | Where-Object { $_ -In 'Install', 'Import' }).Count -gt 0 + if (-not $isInstallOrImport) { + return + } + + if ($Action -contains 'Install' -and -not(Test-Path $path -PathType Container)) { + Write-Verbose "Creating directory path to [$path]" + $Null = New-Item -ItemType Directory -Path $path -Force -ErrorAction SilentlyContinue + } + + Write-Verbose "Setting PSModulePath to`n$($path, $env:PSModulePath -join ';' | Out-String)" + Add-ToItemCollection -Reference Env:\PSModulePath -Item (Get-Item $path -Force).FullName + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Bootstrap-Nuget.ps1 b/.build/modules/PSDepend/0.3.8/Private/Bootstrap-Nuget.ps1 new file mode 100644 index 0000000..765ccd5 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Bootstrap-Nuget.ps1 @@ -0,0 +1,32 @@ +# Check for nuget exe. If it doesn't exist, create full path to parent, and download it +function BootStrap-Nuget { + [cmdletbinding()] + param( + $NugetPath = "$env:APPDATA\nuget.exe" + ) + + if($c = Get-Command 'nuget.exe' -ErrorAction SilentlyContinue) + { + write-verbose "Found Nuget at [$($c.path)]" + return + } + + #Don't have it, download it + $Parent = Split-Path $NugetPath -Parent + if(-not (Test-Path $NugetPath)) + { + if(-not (Test-Path $Parent)) + { + Write-Verbose "Creating parent paths to [$NugetPath]'s parent: [$Parent]" + $null = New-Item $Parent -ItemType Directory -Force + } + Write-Verbose "Downloading nuget to [$NugetPath]" + Invoke-WebRequest -uri 'https://dist.nuget.org/win-x86-commandline/latest/nuget.exe' -OutFile $NugetPath + } + + # Add to path + if( ($ENV:Path -split ';') -notcontains $Parent ) + { + $ENV:Path = $ENV:Path, $Parent -join ';' + } +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Find-NugetPackage.ps1 b/.build/modules/PSDepend/0.3.8/Private/Find-NugetPackage.ps1 new file mode 100644 index 0000000..903258e --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Find-NugetPackage.ps1 @@ -0,0 +1,54 @@ +# All credit and major props to Joel Bennett for this simplified solution that doesn't depend on PowerShellGet +# https://gist.github.com/Jaykul/1caf0d6d26380509b04cf4ecef807355 +function Find-NugetPackage { + [CmdletBinding()] + param( + # The name of a package to find + [Parameter(Mandatory)] + $Name, + # The repository api URL -- like https://www.powershellgallery.com/api/v2/ or https://www.nuget.org/api/v2/ + $PackageSourceUrl = 'https://www.powershellgallery.com/api/v2/', + + #If specified takes precedence over version + [switch]$IsLatest, + + [string]$Version, + + # If specified, gets passed during the Nuget source call + [pscredential]$Credential = $null + ) + + #Ugly way to do this. Prefer islatest, otherwise look for version, otherwise grab all matching modules + if($IsLatest) + { + Write-Verbose "Searching for latest [$name] module" + $URI = "${PackageSourceUrl}Packages?`$filter=Id eq '$name' and IsLatestVersion" + } + elseif($PSBoundParameters.ContainsKey($Version)) + { + Write-Verbose "Searching for version [$version] of [$name]" + $URI = "${PackageSourceUrl}Packages?`$filter=Id eq '$name' and Version eq '$Version'" + } + else + { + Write-Verbose "Searching for all versions of [$name] module" + $URI = "${PackageSourceUrl}Packages?`$filter=Id eq '$name'" + } + + $headers = @{} + if ($null -ne $Credential) + { + $basicAuthToken = [Convert]::ToBase64String(":$($Credential.GetNetworkCredential().Password)") + + $headers["X-NuGet-ApiKey"] = $Credential.UserName + $headers["Authentication"] = "Basic $basicAuthToken" + } + + Invoke-RestMethod $URI -Headers $headers | + Select-Object @{n='Name';ex={$_.title.('#text')}}, + @{n='Author';ex={$_.author.name}}, + @{n='Version';ex={$_.properties.NormalizedVersion}}, + @{n='Uri';ex={$_.Content.src}}, + @{n='Description';ex={$_.properties.Description}}, + @{n='Properties';ex={$_.properties}} +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-ClonedObject.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-ClonedObject.ps1 new file mode 100644 index 0000000..77ba56c --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-ClonedObject.ps1 @@ -0,0 +1,10 @@ +# Idea from http://stackoverflow.com/questions/7468707/deep-copy-a-dictionary-hashtable-in-powershell +# borrowed from http://stackoverflow.com/questions/8982782/does-anyone-have-a-dependency-graph-and-topological-sorting-code-snippet-for-pow +function Get-ClonedObject { + param($DeepCopyObject) + $memStream = new-object IO.MemoryStream + $formatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter + $formatter.Serialize($memStream,$DeepCopyObject) + $memStream.Position=0 + $formatter.Deserialize($memStream) +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-Hash.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-Hash.ps1 new file mode 100644 index 0000000..b50f4a9 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-Hash.ps1 @@ -0,0 +1,165 @@ +function Get-Hash { + <# + .SYNOPSIS + Calculates the hash on a given file based on the seleced hash algorithm. + + .DESCRIPTION + Calculates the hash on a given file based on the seleced hash algorithm. Multiple hashing + algorithms can be used with this command. + + .PARAMETER Path + File or files that will be scanned for hashes. + + .PARAMETER Algorithm + The type of algorithm that will be used to determine the hash of a file or files. + Default hash algorithm used is SHA256. More then 1 algorithm type can be used. + + Available hash algorithms: + + MD5 + SHA1 + SHA256 (Default) + SHA384 + SHA512 + RIPEM160 + + .NOTES + Name: Get-FileHash + Author: Boe Prox + Created: 18 March 2013 + Modified: 28 Jan 2014 + 1.1 - Fixed bug with incorrect hash when using multiple algorithms + + .OUTPUTS + System.IO.FileInfo.Hash + + .EXAMPLE + Get-Hash -Path Test2.txt + Path SHA256 + ---- ------ + C:\users\prox\desktop\TEST2.txt 5f8c58306e46b23ef45889494e991d6fc9244e5d78bc093f1712b0ce671acc15 + + Description + ----------- + Displays the SHA256 hash for the text file. + + .EXAMPLE + Get-Hash -Path .\TEST2.txt -Algorithm MD5,SHA256,RIPEMD160 | Format-List + Path : C:\users\prox\desktop\TEST2.txt + MD5 : cb8e60205f5e8cae268af2b47a8e5a13 + SHA256 : 5f8c58306e46b23ef45889494e991d6fc9244e5d78bc093f1712b0ce671acc15 + RIPEMD160 : e64d1fa7b058e607319133b2aa4f69352a3fcbc3 + + Description + ----------- + Displays MD5,SHA256 and RIPEMD160 hashes for the text file. + + .EXAMPLE + Get-ChildItem -Filter *.exe | Get-Hash -Algorithm MD5 + Path MD5 + ---- --- + C:\users\prox\desktop\handle.exe 50c128c5b28237b3a01afbdf0e546245 + C:\users\prox\desktop\PortQry.exe c6ac67f4076ca431acc575912c194245 + C:\users\prox\desktop\procexp.exe b4caa7f3d726120e1b835d52fe358d3f + C:\users\prox\desktop\Procmon.exe 9c85f494132cc6027762d8ddf1dd5a12 + C:\users\prox\desktop\PsExec.exe aeee996fd3484f28e5cd85fe26b6bdcd + C:\users\prox\desktop\pskill.exe b5891462c9ca5bddfe63d3bae3c14e0b + C:\users\prox\desktop\Tcpview.exe 485bc6763729511dcfd52ccb008f5c59 + + Description + ----------- + Uses pipeline input from Get-ChildItem to get MD5 hashes of executables. + + #> + [CmdletBinding(DefaultParameterSetName='File')] + Param( + [Parameter( ParameterSetName = 'File', + Position=0, + Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + ValueFromPipeline=$True)] + [Alias("PSPath","FullName")] + [string[]]$Path, + + [Parameter( ParameterSetName = 'String', + Position=0, + Mandatory=$true)] + [string[]]$String, + + [Parameter(Position=1)] + [ValidateSet("MD5","SHA1","SHA256","SHA384","SHA512","RIPEMD160")] + [string[]]$Algorithm = "SHA256" + ) + Process { + + if($PSCmdlet.ParameterSetName -eq 'File') + { + $Items = $Path + } + elseif($PSCmdlet.ParameterSetName -eq 'String') + { + $Items = $String + } + + ForEach ($item in $Items) { + + if($PSCmdlet.ParameterSetName -eq 'File') + { + + $item = (Resolve-Path $item).ProviderPath + If (-Not ([uri]$item).IsAbsoluteUri) { + Write-Verbose ("{0} is not a full path, using current directory: {1}" -f $item,$pwd) + $item = (Join-Path $pwd ($item -replace "\.\\","")) + } + If(Test-Path $item -Type Container) { + Write-Warning ("Cannot calculate hash for directory: {0}" -f $item) + Return + } + $object = New-Object PSObject -Property @{ + Path = $item + } + #Open the Stream + $stream = ([IO.StreamReader]$item).BaseStream + + + foreach($Type in $Algorithm) { + + [string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create( $Type ).ComputeHash( $stream ) | + ForEach-Object { "{0:x2}" -f $_ }) + + $null = $stream.Seek(0,0) + + #If multiple algorithms are used, then they will be added to existing object + $object = Add-Member -InputObject $Object -MemberType NoteProperty -Name $Type -Value $Hash -PassThru + } + } + elseif($PSCmdlet.ParameterSetName -eq 'String') + { + + $object = New-Object PSObject -Property @{ + String = $item + } + + foreach($Type in $Algorithm) { + [string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create( $Type ).ComputeHash( [System.Text.Encoding]::UTF8.GetBytes($item) ) | + ForEach-Object { "{0:x2}" -f $_ }) + + #If multiple algorithms are used, then they will be added to existing object + $object = Add-Member -InputObject $Object -MemberType NoteProperty -Name $Type -Value $Hash -PassThru + } + } + + $object.pstypenames.insert(0,'System.IO.FileInfo.Hash') + + #Output an object with the hash, algorithm and path + Write-Output $object + + if($PSCmdlet.ParameterSetName -eq 'File') + { + #Close the stream + $stream.Close() + $stream.Dispose() + } + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-NodeModule.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-NodeModule.ps1 new file mode 100644 index 0000000..beb3521 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-NodeModule.ps1 @@ -0,0 +1,9 @@ +Function Get-NodeModule { + [cmdletbinding()] + Param([switch]$Global) + If ($Global -eq $true) { + (npm ls --json --silent --global | ConvertFrom-Json).dependencies + } Else { + (npm ls --json --silent | ConvertFrom-Json).dependencies + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-Parameter.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-Parameter.ps1 new file mode 100644 index 0000000..0864d89 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-Parameter.ps1 @@ -0,0 +1,224 @@ +# Borrowed from http://poshcode.org/5929 with a minor tweak for validateset - thanks all! +Function Get-Parameter { + #.Synopsis + # Enumerates the parameters of one or more commands + #.Description + # Lists all the parameters of a command, by ParameterSet, including their aliases, type, etc. + # + # By default, formats the output to tables grouped by command and parameter set + #.Example + # Get-Command Select-Xml | Get-Parameter + #.Example + # Get-Parameter Select-Xml + [CmdletBinding(DefaultParameterSetName="ParameterName")] + param( + # The name of the command to get parameters for + [Parameter(Position = 1, Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [Alias("Name")] + [string[]]$CommandName, + + # The parameter name to filter by (allows Wilcards) + [Parameter(Position = 2, ValueFromPipelineByPropertyName=$true, ParameterSetName="FilterNames")] + [string[]]$ParameterName = "*", + + # The ParameterSet name to filter by (allows wildcards) + [Parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName="FilterSets")] + [string[]]$SetName = "*", + + # The name of the module which contains the command (this is for scoping) + [Parameter(ValueFromPipelineByPropertyName = $true)] + $ModuleName, + + # Skip testing for Provider parameters (will be much faster) + [Switch]$SkipProviderParameters, + + # Forces including the CommonParameters in the output + [switch]$Force + ) + + begin { + $PropertySet = @( "Name", + @{n="Position";e={if($_.Position -lt 0){"Named"}else{$_.Position}}}, + "Aliases", + @{n="Short";e={$_.Name}}, + @{n="Type";e={$_.ParameterType.Name}}, + @{n="ParameterSet";e={$paramset}}, + @{n="Command";e={$command}}, + @{n="Mandatory";e={$_.IsMandatory}}, + @{n="Provider";e={$_.DynamicProvider}}, + @{n="ValueFromPipeline";e={$_.ValueFromPipeline}}, + @{n="ValueFromPipelineByPropertyName";e={$_.ValueFromPipelineByPropertyName}}, + "ValidateSetValues" # This is a bit specific and not always applicable, but need it for this project.... + ) + function Join-Object { + Param( + [Parameter(Position=0)] + $First, + + [Parameter(ValueFromPipeline=$true,Position=1)] + $Second + ) + begin { + [string[]] $p1 = $First | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name + } + process { + $Output = $First | Select-Object $p1 + foreach ($p in $Second | Get-Member -MemberType Properties | Where-Object {$p1 -notcontains $_.Name} | Select-Object -ExpandProperty Name) { + Add-Member -InputObject $Output -MemberType NoteProperty -Name $p -Value $Second."$p" + } + $Output + } + } + + function Add-Parameters { + [CmdletBinding()] + param( + [Parameter(Position=0)] + [Hashtable]$Parameters, + + [Parameter(Position=1)] + [System.Management.Automation.ParameterMetadata[]]$MoreParameters + ) + + foreach ($p in $MoreParameters | Where-Object { !$Parameters.ContainsKey($_.Name) } ) { + Write-Debug ("INITIALLY: " + $p.Name) + $Parameters.($p.Name) = $p | Select-Object * + } + + [Array]$Dynamic = $MoreParameters | Where-Object { $_.IsDynamic } + if ($dynamic) { + foreach ($d in $dynamic) { + if (Get-Member -InputObject $Parameters.($d.Name) -Name DynamicProvider) { + Write-Debug ("ADD:" + $d.Name + " " + $provider.Name) + $Parameters.($d.Name).DynamicProvider += $provider.Name + } else { + Write-Debug ("CREATE:" + $d.Name + " " + $provider.Name) + $Parameters.($d.Name) = $Parameters.($d.Name) | Select-Object *, @{ n="DynamicProvider";e={ @($provider.Name) } } + } + } + } + } + } + + process { + foreach ($cmd in $CommandName) { + if ($ModuleName) {$cmd = "$ModuleName\$cmd"} + Write-Verbose "Searching for $cmd" + $commands = @(Get-Command $cmd) + + foreach ($command in $commands) { + Write-Verbose "Searching for $command" + # resolve aliases (an alias can point to another alias) + while ($command.CommandType -eq "Alias") { + $command = @(Get-Command ($command.definition))[0] + } + if (-not $command) {continue} + + Write-Verbose "Get-Parameters for $($Command.Source)\$($Command.Name)" + + $Parameters = @{} + + ## We need to detect provider parameters ... + $NoProviderParameters = !$SkipProviderParameters + ## Shortcut: assume only the core commands get Provider dynamic parameters + if(!$SkipProviderParameters -and $Command.Source -eq "Microsoft.PowerShell.Management") { + ## The best I can do is to validate that the command has a parameter which could accept a string path + foreach($param in $Command.Parameters.Values) { + if(([String[]],[String] -contains $param.ParameterType) -and ($param.ParameterSets.Values | Where-Object { $_.Position -ge 0 })) { + $NoProviderParameters = $false + break + } + } + } + + if($NoProviderParameters) { + if($Command.Parameters) { + Add-Parameters $Parameters $Command.Parameters.Values + } + } else { + foreach ($provider in Get-PSProvider) { + if($provider.Drives.Length -gt 0) { + $drive = Get-Location -PSProvider $Provider.Name + } else { + $drive = "{0}\{1}::\" -f $provider.ModuleName, $provider.Name + } + Write-Verbose ("Get-Command $command -Args $drive | Select -Expand Parameters") + + try { + $MoreParameters = (Get-Command $command -Args $drive).Parameters.Values + } catch {} + + if($MoreParameters.Length -gt 0) { + Add-Parameters $Parameters $MoreParameters + } + } + # If for some reason none of the drive paths worked, just use the default parameters + if($Parameters.Length -eq 0) { + if($Command.Parameters) { + Add-Parameters $Parameters $Command.Parameters.Values + } + } + } + + ## Calculate the shortest distinct parameter name -- do this BEFORE removing the common parameters or else. + $Aliases = $Parameters.Values | Select-Object -ExpandProperty Aliases ## Get defined aliases + $ParameterNames = $Parameters.Keys + $Aliases + foreach ($p in $($Parameters.Keys)) { + $short = "^" + $aliases = @($p) + @($Parameters.$p.Aliases) | Sort-Object { $_.Length } + $shortest = "^" + @($aliases)[0] + + foreach($name in $aliases) { + $short = "^" + foreach ($char in [char[]]$name) { + $short += $char + $mCount = ($ParameterNames -match $short).Count + if ($mCount -eq 1 ) { + if($short.Length -lt $shortest.Length) { + $shortest = $short + } + break + } + } + } + if($shortest.Length -lt @($aliases)[0].Length +1){ + # Overwrite the Aliases with this new value + $Parameters.$p = $Parameters.$p | Add-Member NoteProperty Aliases ($Parameters.$p.Aliases + @("$($shortest.SubString(1))*")) -Force -Passthru + } + + # ValidateSet... + $Parameters.$p = $Parameters.$p | Add-Member NoteProperty ValidateSetValues ($Parameters.$p.Attributes | Where-Object {$_.TypeId.name -like 'ValidateSetAttribute'}).ValidValues -Force -Passthru + + } + + # Write-Verbose "Parameters: $($Parameters.Count)`n $($Parameters | ft | out-string)" + $CommonParameters = [string[]][System.Management.Automation.Cmdlet]::CommonParameters + + foreach ($paramset in @($command.ParameterSets | Select-Object -ExpandProperty "Name")) { + $paramset = $paramset | Add-Member -Name IsDefault -MemberType NoteProperty -Value ($paramset -eq $command.DefaultParameterSet) -PassThru + foreach ($parameter in $Parameters.Keys | Sort-Object) { + # Write-Verbose "Parameter: $Parameter" + if (!$Force -and ($CommonParameters -contains $Parameter)) {continue} + if ($Parameters.$Parameter.ParameterSets.ContainsKey($paramset) -or $Parameters.$Parameter.ParameterSets.ContainsKey("__AllParameterSets")) { + if ($Parameters.$Parameter.ParameterSets.ContainsKey($paramset)) { + $output = Join-Object $Parameters.$Parameter $Parameters.$Parameter.ParameterSets.$paramSet + } else { + $output = Join-Object $Parameters.$Parameter $Parameters.$Parameter.ParameterSets.__AllParameterSets + } + + Write-Output $Output | Select-Object $PropertySet | ForEach-Object { + $null = $_.PSTypeNames.Insert(0,"System.Management.Automation.ParameterMetadata") + $null = $_.PSTypeNames.Insert(0,"System.Management.Automation.ParameterMetadataEx") + # Write-Verbose "$(($_.PSTypeNames.GetEnumerator()) -join ", ")" + $_ + } | + Add-Member ScriptMethod ToString { $this.Name } -Force -Passthru | + Where-Object {$(foreach($pn in $ParameterName) {$_ -like $Pn}) -contains $true} | + Where-Object {$(foreach($sn in $SetName) {$_.ParameterSet -like $sn}) -contains $true} + } + } + } + } + } + } +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-ParameterName.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-ParameterName.ps1 new file mode 100644 index 0000000..97a4c15 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-ParameterName.ps1 @@ -0,0 +1,29 @@ +Function Get-ParameterName { +#Get parameter names for a specific command + [cmdletbinding()] + param( + [string]$command, + [string]$parameterset = $null, + [string[]]$excludeDefault = $( "Verbose", + "Debug", + "ErrorAction", + "WarningAction", + "ErrorVariable", + "WarningVariable", + "OutVariable", + "OutBuffer", + "PipelineVariable", + "Confirm", + "Whatif" ), + [string[]]$exclude = $( "Passthru", "Commit" ) + ) + if($parameterset) + { + ((Get-Command -name $command).ParameterSets | Where-Object {$_.name -eq $parameterset} ).Parameters.Name | Where-Object {($exclude + $excludeDefault) -notcontains $_} + } + + else + { + ((Get-Command -name $command).ParameterSets | Where-Object {$_.name -eq "__AllParameterSets"} ).Parameters.Name | Where-Object {($exclude + $excludeDefault) -notcontains $_} + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-ProjectDetail.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-ProjectDetail.ps1 new file mode 100644 index 0000000..85d1d51 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-ProjectDetail.ps1 @@ -0,0 +1,92 @@ +# Borrowed and tweaked from BuildHelpers. TODO: Doc, commit back there +function Get-ProjectDetail { + <# + .SYNOPSIS + Get the name for this project + + .FUNCTIONALITY + CI/CD + + .DESCRIPTION + Get the name for this project + + Evaluates based on the following scenarios: + * Subfolder with the same name as the current folder + * Subfolder with a .psd1 file in it + * Current folder with a .psd1 file in it + + .PARAMETER Path + Path to project root. Defaults to the current working path + + .NOTES + We assume you are in the project root, for several of the fallback options + + .EXAMPLE + Get-ProjectName + + .LINK + https://github.com/RamblingCookieMonster/BuildHelpers + + .LINK + Get-BuildVariables + + .LINK + Set-BuildEnvironment + + .LINK + about_BuildHelpers + #> + [cmdletbinding()] + param( + $Path = $PWD.Path + ) + + Function Resolve-ProjectDetail { + param( + $Path = $Path, + $RelativePath = '\', + $Name + ) + [pscustomobject]@{ + Name = $Name + Path = Resolve-Path (Join-Path $Path $RelativePath) + } + } + + $CurrentFolder = Split-Path $Path -Leaf + $ExpectedPath = Join-Path -Path $Path -ChildPath $CurrentFolder + $ExpectedPsd1 = Join-Path -Path $ExpectedPath -ChildPath "$CurrentFolder.psd1" + if((Test-Path $ExpectedPath) -and (Test-Path $ExpectedPsd1)) + { + Resolve-ProjectDetail -Path $Path -RelativePath $CurrentFolder -Name $CurrentFolder + } + else + { + # Look for properly organized modules + $ProjectPaths = Get-ChildItem $Path -Directory | + Where-Object { + Test-Path $(Join-Path $_.FullName "$($_.name).psd1") + } | + Select-Object -ExpandProperty Fullname + + if( @($ProjectPaths).Count -gt 1 ) + { + Write-Warning "Found more than one project path via subfolders with psd1 files: $(Split-Path $ProjectPaths -Leaf | Out-String)" + } + elseif( @($ProjectPaths).Count -eq 1 ) + { + $Name = Split-Path $ProjectPaths -Leaf + Resolve-ProjectDetail -Path $Path -RelativePath $Name -Name $Name + } + #PSD1 in root of project - ick, but happens. + elseif( Test-Path "$ExpectedPath.psd1" ) + { + Resolve-ProjectDetail -Path $Path -Name $CurrentFolder + } + else + { + Write-Verbose "Could not find a project from [$Path], using root" + Resolve-ProjectDetail -Path $Path -Name $CurrentFolder + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-PropertyOrder.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-PropertyOrder.ps1 new file mode 100644 index 0000000..4fdb4ce --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-PropertyOrder.ps1 @@ -0,0 +1,54 @@ +#function to extract properties +Function Get-PropertyOrder { + <# + .SYNOPSIS + Gets property order for specified object + + .DESCRIPTION + Gets property order for specified object + + .PARAMETER InputObject + A single object to convert to an array of property value pairs. + + .PARAMETER Membertype + Membertypes to include + + .PARAMETER ExcludeProperty + Specific properties to exclude + + .FUNCTIONALITY + PowerShell Language + #> + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromRemainingArguments=$false)] + [PSObject]$InputObject, + + [validateset("AliasProperty", "CodeProperty", "Property", "NoteProperty", "ScriptProperty", + "Properties", "PropertySet", "Method", "CodeMethod", "ScriptMethod", "Methods", + "ParameterizedProperty", "MemberSet", "Event", "Dynamic", "All")] + [string[]]$MemberType = @( "NoteProperty", "Property", "ScriptProperty" ), + + [string[]]$ExcludeProperty = $null + ) + + begin { + + if($PSBoundParameters.ContainsKey('inputObject')) { + $firstObject = $InputObject[0] + } + } + process{ + + #we only care about one object... + $firstObject = $InputObject + } + end{ + + #Get properties that meet specified parameters + $firstObject.psobject.properties | + Where-Object { $memberType -contains $_.memberType } | + Select-Object -ExpandProperty Name | + Where-Object{ -not $excludeProperty -or $excludeProperty -notcontains $_ } + } +} #Get-PropertyOrder \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-TaggedDependency.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-TaggedDependency.ps1 new file mode 100644 index 0000000..018c0c0 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-TaggedDependency.ps1 @@ -0,0 +1,23 @@ +Function Get-TaggedDependency { + param( + [object[]]$Dependency, + [string[]]$Tags + ) + + # Only return dependency with all specified tags + foreach($Depend in $Dependency) + { + $Include = $False + foreach($Tag in @($Tags)) + { + if($Depend.Tags -contains $Tag) + { + $Include = $True + } + } + If($Include) + { + $Depend + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-TopologicalSort.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-TopologicalSort.ps1 new file mode 100644 index 0000000..c557081 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-TopologicalSort.ps1 @@ -0,0 +1,73 @@ +# Thanks to http://stackoverflow.com/questions/8982782/does-anyone-have-a-dependency-graph-and-topological-sorting-code-snippet-for-pow +# Input is a hashtable of @{ID = @(Depended,On,IDs);...} +function Get-TopologicalSort { + param( + [Parameter(Mandatory = $true, Position = 0)] + [hashtable] $edgeList + ) + + # Make sure we can use HashSet + Add-Type -AssemblyName System.Core + + # Clone it so as to not alter original + $currentEdgeList = [hashtable] (Get-ClonedObject $edgeList) + + # algorithm from http://en.wikipedia.org/wiki/Topological_sorting#Algorithms + $topologicallySortedElements = New-Object System.Collections.ArrayList + $setOfAllNodesWithNoIncomingEdges = New-Object System.Collections.Queue + + $fasterEdgeList = @{} + + # Keep track of all nodes in case they put it in as an edge destination but not source + $allNodes = New-Object -TypeName System.Collections.Generic.HashSet[object] -ArgumentList (,[object[]] $currentEdgeList.Keys) + + foreach($currentNode in $currentEdgeList.Keys) { + $currentDestinationNodes = [array] $currentEdgeList[$currentNode] + if($currentDestinationNodes.Length -eq 0) { + $setOfAllNodesWithNoIncomingEdges.Enqueue($currentNode) + } + + foreach($currentDestinationNode in $currentDestinationNodes) { + if(!$allNodes.Contains($currentDestinationNode)) { + [void] $allNodes.Add($currentDestinationNode) + } + } + + # Take this time to convert them to a HashSet for faster operation + $currentDestinationNodes = New-Object -TypeName System.Collections.Generic.HashSet[object] -ArgumentList (,[object[]] $currentDestinationNodes ) + [void] $fasterEdgeList.Add($currentNode, $currentDestinationNodes) + } + + # Now let's reconcile by adding empty dependencies for source nodes they didn't tell us about + foreach($currentNode in $allNodes) { + if(!$currentEdgeList.ContainsKey($currentNode)) { + [void] $currentEdgeList.Add($currentNode, (New-Object -TypeName System.Collections.Generic.HashSet[object])) + $setOfAllNodesWithNoIncomingEdges.Enqueue($currentNode) + } + } + + $currentEdgeList = $fasterEdgeList + + while($setOfAllNodesWithNoIncomingEdges.Count -gt 0) { + $currentNode = $setOfAllNodesWithNoIncomingEdges.Dequeue() + [void] $currentEdgeList.Remove($currentNode) + [void] $topologicallySortedElements.Add($currentNode) + + foreach($currentEdgeSourceNode in $currentEdgeList.Keys) { + $currentNodeDestinations = $currentEdgeList[$currentEdgeSourceNode] + if($currentNodeDestinations.Contains($currentNode)) { + [void] $currentNodeDestinations.Remove($currentNode) + + if($currentNodeDestinations.Count -eq 0) { + [void] $setOfAllNodesWithNoIncomingEdges.Enqueue($currentEdgeSourceNode) + } + } + } + } + + if($currentEdgeList.Count -gt 0) { + throw "Graph has at least one cycle!" + } + + return $topologicallySortedElements +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Get-WebFile.ps1 b/.build/modules/PSDepend/0.3.8/Private/Get-WebFile.ps1 new file mode 100644 index 0000000..ae6333c --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Get-WebFile.ps1 @@ -0,0 +1,9 @@ +# Wrapped for pester mocking... +Function Get-WebFile { + param($URL, $Path) + # We have the info, check for file, download it! + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + $webclient = New-Object System.Net.WebClient + $webclient.DownloadFile($URL, $Path) + $webclient.Dispose() +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Import-PSDependModule.ps1 b/.build/modules/PSDepend/0.3.8/Private/Import-PSDependModule.ps1 new file mode 100644 index 0000000..7d30233 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Import-PSDependModule.ps1 @@ -0,0 +1,24 @@ +function Import-PSDependModule { + [cmdletbinding()] + param ( + [string[]]$Name = $ModulePath, + $Action = $PSDependAction, + [string] $Version + ) + if($PSDependAction -contains 'Import') + { + foreach($Mod in $Name) + { + Write-Verbose "Importing [$Mod]" + $importParams = @{ + Name = $Mod + Scope = 'Global' + Force = $true + } + if ($Version -and $Version -ne 'latest') { + $importParams.add('RequiredVersion',$Version) + } + Import-Module @importParams + } + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Install-Dotnet.ps1 b/.build/modules/PSDepend/0.3.8/Private/Install-Dotnet.ps1 new file mode 100644 index 0000000..ef14019 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Install-Dotnet.ps1 @@ -0,0 +1,40 @@ +# This installs the .NET SDK that satisfies the Channel, Version, and InstallDir that is passed in +# If on Windows, it will download the .NET SDK PowerShell install script (dotnet-install.ps1) +# On all other platforms, it will download the .NET SDK shell script (dotnet-install.sh) +function Install-Dotnet { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [string] + $Channel, + + [Parameter(Mandatory=$true)] + [string] + $Version, + + [Parameter(Mandatory=$true)] + [string] + $InstallDir + ) + + $IsWindowsEnv = !$PSVersionTable.Platform -or $PSVersionTable.Platform -eq "Win32NT" + + $obtainUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain" + + try { + # remove the old folder, download, and run the dotnet-install script for the correct platform + Remove-Item $InstallDir -Recurse -Force -ErrorAction SilentlyContinue + $installScript = if ($IsWindowsEnv) { "dotnet-install.ps1" } else { "dotnet-install.sh" } + Invoke-WebRequest -Uri $obtainUrl/$installScript -OutFile $installScript + + if ($IsWindowsEnv) { + & .\$installScript -Channel $Channel -Version $Version -InstallDir $InstallDir + } else { + bash ./$installScript -c $Channel -v $Version --install-dir $InstallDir + } + } + finally { + # delete the downloaded install script + Remove-Item $installScript -Force -ErrorAction SilentlyContinue + } +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Install-NodeModule.ps1 b/.build/modules/PSDepend/0.3.8/Private/Install-NodeModule.ps1 new file mode 100644 index 0000000..f4f73dc --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Install-NodeModule.ps1 @@ -0,0 +1,9 @@ +Function Install-NodeModule { + [cmdletbinding()] + Param( + [string]$Version, + [switch]$Global, + [string]$PackageName + ) + npm install --silent $(If($Global -eq $true){'--global'}) $PackageName$(If(![string]::IsNullOrEmpty($Version)){"@$Version"}) +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Invoke-ExternalCommand.ps1 b/.build/modules/PSDepend/0.3.8/Private/Invoke-ExternalCommand.ps1 new file mode 100644 index 0000000..3226bd1 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Invoke-ExternalCommand.ps1 @@ -0,0 +1,15 @@ +# Pester wasn't mocking git... +# Borrowed idea from https://github.com/pester/Pester/issues/415 +function Invoke-ExternalCommand { + [cmdletbinding()] + param($Command, [string[]]$Arguments, [switch]$Passthru) + + Write-Verbose "Running $Command with arguments $($Arguments -join "; ")" + $result = $null + $result = & $command @arguments + Write-Verbose "$($result | Out-String)" + if($Passthru) + { + $Result + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Resolve-DependScripts.ps1 b/.build/modules/PSDepend/0.3.8/Private/Resolve-DependScripts.ps1 new file mode 100644 index 0000000..c657499 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Resolve-DependScripts.ps1 @@ -0,0 +1,41 @@ +# Borrowed from Pester and stripped down +# This might be overkill +function Resolve-DependScripts +{ + param ([object[]] $Path, [bool]$Recurse = $True) + $resolvedScriptInfo = @( + foreach ($object in $Path) + { + $unresolvedPath = [string] $object + if ($unresolvedPath -notmatch '[\*\?\[\]]' -and + (Test-Path -LiteralPath $unresolvedPath -PathType Leaf) -and + (Get-Item -LiteralPath $unresolvedPath -Force) -is [System.IO.FileInfo]) + { + $extension = [System.IO.Path]::GetExtension($unresolvedPath) + if ($extension -ne '.psd1') + { + Write-Error "Script path '$unresolvedPath' is not a psd1 file." + } + else + { + $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($unresolvedPath) + } + } + else + { + $RecurseParam = @{Recurse = $False} + if($Recurse) + { + $RecurseParam.Recurse = $True + } + Resolve-Path -Path $unresolvedPath | + Where-Object { $_.Provider.Name -eq 'FileSystem' } | + Select-Object -ExpandProperty ProviderPath | + Get-ChildItem -Force -Filter *.psd1 @RecurseParam | + Where-Object { -not $_.PSIsContainer -and $_.Name -match '\.depend\.psd1$|.*requirements.psd1$' } | + Select-Object -ExpandProperty FullName -Unique + } + } + ) + $resolvedScriptInfo | Select-Object -Unique +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Save-NugetPackage.ps1 b/.build/modules/PSDepend/0.3.8/Private/Save-NugetPackage.ps1 new file mode 100644 index 0000000..854624f --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Save-NugetPackage.ps1 @@ -0,0 +1,14 @@ +# All credit and major props to Joel Bennett for this simplified solution that doesn't depend on PowerShellGet +# https://gist.github.com/Jaykul/1caf0d6d26380509b04cf4ecef807355 +function Save-NugetPackage { + [CmdletBinding()] + param( + [Parameter(ValueFromPipelineByPropertyName,Mandatory)]$Name, + [Parameter(ValueFromPipelineByPropertyName,Mandatory)]$Uri, + [Parameter(ValueFromPipelineByPropertyName)]$Version="", + [string]$Path = $pwd + ) + $Path = (Join-Path $Path "$Name.$Version.nupkg") + Invoke-WebRequest $Uri -OutFile $Path + Get-Item $Path +} diff --git a/.build/modules/PSDepend/0.3.8/Private/SemanticVersion.ps1 b/.build/modules/PSDepend/0.3.8/Private/SemanticVersion.ps1 new file mode 100644 index 0000000..4789656 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/SemanticVersion.ps1 @@ -0,0 +1,692 @@ +$code = @' +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace System.Management.Automation +{ + /// + /// An implementation of semantic versioning (https://semver.org) + /// that can be converted to/from . + /// + /// When converting to , a PSNoteProperty is + /// added to the instance to store the semantic version label so + /// that it can be recovered when creating a new SemanticVersion. + /// + public sealed class SemanticVersion : IComparable, IComparable, IEquatable + { + private const string VersionSansRegEx = @"^(?\d+)(\.(?\d+))?(\.(?\d+))?$"; + private const string LabelRegEx = @"^((?[0-9A-Za-z][0-9A-Za-z\-\.]*))?(\+(?[0-9A-Za-z][0-9A-Za-z\-\.]*))?$"; + private const string LabelUnitRegEx = @"^[0-9A-Za-z][0-9A-Za-z\-\.]*$"; + private const string PreLabelPropertyName = "PSSemVerPreReleaseLabel"; + private const string BuildLabelPropertyName = "PSSemVerBuildLabel"; + private const string TypeNameForVersionWithLabel = "System.Version#IncludeLabel"; + + private string versionString; + + /// + /// Construct a SemanticVersion from a string. + /// + /// The version to parse. + /// + /// + public SemanticVersion(string version) + { + var v = SemanticVersion.Parse(version); + + major = v.Major; + minor = v.Minor; + patch = v.Patch < 0 ? 0 : v.Patch; + preReleaseLabel = v.PreReleaseLabel; + buildLabel = v.BuildLabel; + } + + /// + /// Construct a SemanticVersion. + /// + /// The major version. + /// The minor version. + /// The patch version. + /// The pre-release label for the version. + /// The build metadata for the version. + /// + /// If don't match 'LabelUnitRegEx'. + /// If don't match 'LabelUnitRegEx'. + /// + public SemanticVersion(int major, int minor, int patch, string preReleaseLabel, string buildLabel) + : this(major, minor, patch) + { + if (!string.IsNullOrEmpty(preReleaseLabel)) + { + if (!Regex.IsMatch(preReleaseLabel, LabelUnitRegEx)) throw new FormatException("preReleaseLabel"); + + this.preReleaseLabel = preReleaseLabel; + } + + if (!string.IsNullOrEmpty(buildLabel)) + { + if (!Regex.IsMatch(buildLabel, LabelUnitRegEx)) throw new FormatException("buildLabel"); + + this.buildLabel = buildLabel; + } + } + + /// + /// Construct a SemanticVersion. + /// + /// The major version. + /// The minor version. + /// The minor version. + /// The label for the version. + /// + /// + /// If don't match 'LabelRegEx'. + /// + public SemanticVersion(int major, int minor, int patch, string label) + : this(major, minor, patch) + { + // We presume the SymVer : + // 1) major.minor.patch-label + // 2) 'label' starts with letter or digit. + if (!string.IsNullOrEmpty(label)) + { + var match = Regex.Match(label, LabelRegEx); + if (!match.Success) throw new FormatException("label"); + + preReleaseLabel = match.Groups["preLabel"].Value; + buildLabel = match.Groups["buildLabel"].Value; + } + } + + /// + /// Construct a SemanticVersion. + /// + /// The major version. + /// The minor version. + /// The minor version. + /// + /// If , , or is less than 0. + /// + public SemanticVersion(int major, int minor, int patch) + { + if (major < 0) throw new ArgumentException("major"); + if (minor < 0) throw new ArgumentException("minor"); + if (patch < 0) throw new ArgumentException("patch"); + + this.major = major; + this.minor = minor; + this.patch = patch; + // We presume: + // PreReleaseLabel = null; + // BuildLabel = null; + } + + /// + /// Construct a SemanticVersion. + /// + /// The major version. + /// The minor version. + /// + /// If or is less than 0. + /// + public SemanticVersion(int major, int minor) : this(major, minor, 0) { } + + /// + /// Construct a SemanticVersion. + /// + /// The major version. + /// + /// If is less than 0. + /// + public SemanticVersion(int major) : this(major, 0, 0) { } + + /// + /// Construct a from a , + /// copying the NoteProperty storing the label if the expected property exists. + /// + /// The version. + /// + /// If is null. + /// + /// + /// If is more than 0. + /// + public SemanticVersion(Version version) + { + if (version == null) throw new ArgumentNullException("version"); + if (version.Revision > 0) throw new ArgumentException("version"); + + major = version.Major; + minor = version.Minor; + patch = version.Build == -1 ? 0 : version.Build; + var psobj = new PSObject(version); + var preLabelNote = psobj.Properties[PreLabelPropertyName]; + if (preLabelNote != null) + { + preReleaseLabel = preLabelNote.Value as string; + } + + var buildLabelNote = psobj.Properties[BuildLabelPropertyName]; + if (buildLabelNote != null) + { + buildLabel = buildLabelNote.Value as string; + } + } + + /// + /// Convert a to a . + /// If there is a or/and a , + /// it is added as a NoteProperty to the result so that you can round trip + /// back to a without losing the label. + /// + /// + public static implicit operator Version(SemanticVersion semver) + { + PSObject psobj; + + var result = new Version(semver.Major, semver.Minor, semver.Patch); + + if (!string.IsNullOrEmpty(semver.PreReleaseLabel) || !string.IsNullOrEmpty(semver.BuildLabel)) + { + psobj = new PSObject(result); + + if (!string.IsNullOrEmpty(semver.PreReleaseLabel)) + { + psobj.Properties.Add(new PSNoteProperty(PreLabelPropertyName, semver.PreReleaseLabel)); + } + + if (!string.IsNullOrEmpty(semver.BuildLabel)) + { + psobj.Properties.Add(new PSNoteProperty(BuildLabelPropertyName, semver.BuildLabel)); + } + + psobj.TypeNames.Insert(0, TypeNameForVersionWithLabel); + } + + return result; + } + + /// + /// The major version number, never negative. + /// + private int major; + + public int Major + { + get { return major; } + } + + /// + /// The minor version number, never negative. + /// + private int minor; + + public int Minor + { + get { return minor; } + } + + /// + /// The patch version, -1 if not specified. + /// + private int patch; + + public int Patch + { + get { return patch; } + } + + + /// + /// PreReleaseLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. + /// + private string preReleaseLabel; + + public string PreReleaseLabel + { + get { return preReleaseLabel; } + } + + + /// + /// BuildLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. + /// + private string buildLabel; + + public string BuildLabel + { + get { return buildLabel; } + } + + + /// + /// Parse and return the result if it is a valid , otherwise throws an exception. + /// + /// The string to parse. + /// + /// + /// + /// + public static SemanticVersion Parse(string version) + { + if (version == null) throw new ArgumentNullException("version"); + if (version == string.Empty) throw new FormatException("version"); + + var r = new VersionResult(); + r.Init(true); + TryParseVersion(version, ref r); + + return r._parsedVersion; + } + + /// + /// Parse and return true if it is a valid , otherwise return false. + /// No exceptions are raised. + /// + /// The string to parse. + /// The return value when the string is a valid + public static bool TryParse(string version, out SemanticVersion result) + { + if (version != null) + { + var r = new VersionResult(); + r.Init(false); + + if (TryParseVersion(version, ref r)) + { + result = r._parsedVersion; + return true; + } + } + + result = null; + return false; + } + + private static bool TryParseVersion(string version, ref VersionResult result) + { + if (version.EndsWith("-") || version.EndsWith("+") || version.EndsWith(".")) + { + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + string versionSansLabel = null; + var major = 0; + var minor = 0; + var patch = 0; + string preLabel = null; + string buildLabel = null; + + // We parse the SymVer 'version' string 'major.minor.patch-PreReleaseLabel+BuildLabel'. + var dashIndex = version.IndexOf('-'); + var plusIndex = version.IndexOf('+'); + + if (dashIndex > plusIndex) + { + // 'PreReleaseLabel' can contains dashes. + if (plusIndex == -1) + { + // No buildLabel: buildLabel == null + // Format is 'major.minor.patch-PreReleaseLabel' + preLabel = version.Substring(dashIndex + 1); + versionSansLabel = version.Substring(0, dashIndex); + } + else + { + // No PreReleaseLabel: preLabel == null + // Format is 'major.minor.patch+BuildLabel' + buildLabel = version.Substring(plusIndex + 1); + versionSansLabel = version.Substring(0, plusIndex); + dashIndex = -1; + } + } + else + { + if (dashIndex == -1) + { + // Here dashIndex == plusIndex == -1 + // No preLabel - preLabel == null; + // No buildLabel - buildLabel == null; + // Format is 'major.minor.patch' + versionSansLabel = version; + } + else + { + // Format is 'major.minor.patch-PreReleaseLabel+BuildLabel' + preLabel = version.Substring(dashIndex + 1, plusIndex - dashIndex - 1); + buildLabel = version.Substring(plusIndex + 1); + versionSansLabel = version.Substring(0, dashIndex); + } + } + + if ((dashIndex != -1 && string.IsNullOrEmpty(preLabel)) || + (plusIndex != -1 && string.IsNullOrEmpty(buildLabel)) || + string.IsNullOrEmpty(versionSansLabel)) + { + // We have dash and no preReleaseLabel or + // we have plus and no buildLabel or + // we have no main version part (versionSansLabel==null) + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + var match = Regex.Match(versionSansLabel, VersionSansRegEx); + if (!match.Success) + { + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + if (!int.TryParse(match.Groups["major"].Value, out major)) + { + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + if (match.Groups["minor"].Success && !int.TryParse(match.Groups["minor"].Value, out minor)) + { + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + if (match.Groups["patch"].Success && !int.TryParse(match.Groups["patch"].Value, out patch)) + { + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + if (preLabel != null && !Regex.IsMatch(preLabel, LabelUnitRegEx) || + (buildLabel != null && !Regex.IsMatch(buildLabel, LabelUnitRegEx))) + { + result.SetFailure(ParseFailureKind.FormatException); + return false; + } + + result._parsedVersion = new SemanticVersion(major, minor, patch, preLabel, buildLabel); + return true; + } + + /// + /// Implement ToString() + /// + public override string ToString() + { + if (versionString == null) + { + StringBuilder result = new StringBuilder(); + + //result.Append(Major).Append(Utils.Separators.Dot).Append(Minor).Append(Utils.Separators.Dot).Append(Patch); + result.Append(Major).Append('.').Append(Minor).Append('.').Append(Patch); + + if (!string.IsNullOrEmpty(PreReleaseLabel)) + { + result.Append("-").Append(PreReleaseLabel); + } + + if (!string.IsNullOrEmpty(BuildLabel)) + { + result.Append("+").Append(BuildLabel); + } + + versionString = result.ToString(); + } + + return versionString; + } + + /// + /// Implement Compare. + /// + public static int Compare(SemanticVersion versionA, SemanticVersion versionB) + { + if (versionA != null) + { + return versionA.CompareTo(versionB); + } + + if (versionB != null) + { + return -1; + } + + return 0; + } + + /// + /// Implement + /// + public int CompareTo(object version) + { + if (version == null) + { + return 1; + } + + var v = version as SemanticVersion; + if (v == null) + { + throw new ArgumentException("version"); + } + + return CompareTo(v); + } + + /// + /// Implement . + /// Meets SymVer 2.0 p.11 https://semver.org/ + /// + public int CompareTo(SemanticVersion value) + { + if ((object)value == null) + return 1; + + if (Major != value.Major) + return Major > value.Major ? 1 : -1; + + if (Minor != value.Minor) + return Minor > value.Minor ? 1 : -1; + + if (Patch != value.Patch) + return Patch > value.Patch ? 1 : -1; + + // SymVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata). + return ComparePreLabel(this.PreReleaseLabel, value.PreReleaseLabel); + } + + /// + /// Override + /// + public override bool Equals(object obj) + { + return Equals(obj as SemanticVersion); + } + + /// + /// Implement + /// + public bool Equals(SemanticVersion other) + { + // SymVer 2.0 standard requires to ignore 'BuildLabel' (Build metadata). + return other != null && + (Major == other.Major) && (Minor == other.Minor) && (Patch == other.Patch) && + string.Equals(PreReleaseLabel, other.PreReleaseLabel, StringComparison.Ordinal); + } + + /// + /// Override + /// + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } + + /// + /// Overloaded == operator. + /// + public static bool operator ==(SemanticVersion v1, SemanticVersion v2) + { + if (object.ReferenceEquals(v1, null)) + { + return object.ReferenceEquals(v2, null); + } + + return v1.Equals(v2); + } + + /// + /// Overloaded != operator. + /// + public static bool operator !=(SemanticVersion v1, SemanticVersion v2) + { + return !(v1 == v2); + } + + /// + /// Overloaded < operator. + /// + public static bool operator <(SemanticVersion v1, SemanticVersion v2) + { + return (Compare(v1, v2) < 0); + } + + /// + /// Overloaded <= operator. + /// + public static bool operator <=(SemanticVersion v1, SemanticVersion v2) + { + return (Compare(v1, v2) <= 0); + } + + /// + /// Overloaded > operator. + /// + public static bool operator >(SemanticVersion v1, SemanticVersion v2) + { + return (Compare(v1, v2) > 0); + } + + /// + /// Overloaded >= operator. + /// + public static bool operator >=(SemanticVersion v1, SemanticVersion v2) + { + return (Compare(v1, v2) >= 0); + } + + private static int ComparePreLabel(string preLabel1, string preLabel2) + { + // Symver 2.0 standard p.9 + // Pre-release versions have a lower precedence than the associated normal version. + // Comparing each dot separated identifier from left to right + // until a difference is found as follows: + // identifiers consisting of only digits are compared numerically + // and identifiers with letters or hyphens are compared lexically in ASCII sort order. + // Numeric identifiers always have lower precedence than non-numeric identifiers. + // A larger set of pre-release fields has a higher precedence than a smaller set, + // if all of the preceding identifiers are equal. + if (string.IsNullOrEmpty(preLabel1)) { return string.IsNullOrEmpty(preLabel2) ? 0 : 1; } + + if (string.IsNullOrEmpty(preLabel2)) { return -1; } + + var units1 = preLabel1.Split('.'); + var units2 = preLabel2.Split('.'); + + var minLength = units1.Length < units2.Length ? units1.Length : units2.Length; + + for (int i = 0; i < minLength; i++) + { + var ac = units1[i]; + var bc = units2[i]; + int number1, number2; + var isNumber1 = Int32.TryParse(ac, out number1); + var isNumber2 = Int32.TryParse(bc, out number2); + + if (isNumber1 && isNumber2) + { + if (number1 != number2) { return number1 < number2 ? -1 : 1; } + } + else + { + if (isNumber1) { return -1; } + + if (isNumber2) { return 1; } + + int result = string.CompareOrdinal(ac, bc); + if (result != 0) { return result; } + } + } + + return units1.Length.CompareTo(units2.Length); + } + + internal enum ParseFailureKind + { + ArgumentException, + ArgumentOutOfRangeException, + FormatException + } + + internal struct VersionResult + { + internal SemanticVersion _parsedVersion; + internal ParseFailureKind _failure; + internal string _exceptionArgument; + internal bool _canThrow; + + internal void Init(bool canThrow) + { + _canThrow = canThrow; + } + + internal void SetFailure(ParseFailureKind failure) + { + SetFailure(failure, string.Empty); + } + + internal void SetFailure(ParseFailureKind failure, string argument) + { + _failure = failure; + _exceptionArgument = argument; + if (_canThrow) + { + throw GetVersionParseException(); + } + } + + internal Exception GetVersionParseException() + { + switch (_failure) + { + case ParseFailureKind.ArgumentException: + return new ArgumentException("version"); + case ParseFailureKind.ArgumentOutOfRangeException: + throw new ArgumentException("ValidateRangeTooSmall", _exceptionArgument); + case ParseFailureKind.FormatException: + // Regenerate the FormatException as would be thrown by Int32.Parse() + try + { + Int32.Parse(_exceptionArgument, CultureInfo.InvariantCulture); + } + catch (FormatException e) + { + return e; + } + catch (OverflowException e) + { + return e; + } + + break; + } + + return new ArgumentException("version"); + } + } + } +} +'@ + +if ($PSVersionTable.PSVersion.Major -lt 6) { + Add-Type -TypeDefinition $code +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Sort-PSDependency.ps1 b/.build/modules/PSDepend/0.3.8/Private/Sort-PSDependency.ps1 new file mode 100644 index 0000000..1cc2af0 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Sort-PSDependency.ps1 @@ -0,0 +1,28 @@ +function Sort-PSDependency { + [cmdletbinding()] + param( + [object[]]$Dependencies + ) + + $Order = @{} + Foreach($Dependency in $Dependencies) + { + if($Dependency.DependsOn) + { + if(-not $Order.ContainsKey($Dependency.DependencyName)) + { + $Order.add($Dependency.DependencyName, $Dependency.DependsOn) + } + } + } + + if($Order.Keys.Count -gt 0) + { + $DependencyOrder = Get-TopologicalSort $Order + Sort-ObjectWithCustomList -InputObject $Dependencies -Property DependencyName -CustomList $DependencyOrder + } + else + { + $Dependencies + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Sort-WithCustomList.ps1 b/.build/modules/PSDepend/0.3.8/Private/Sort-WithCustomList.ps1 new file mode 100644 index 0000000..a30cd68 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Sort-WithCustomList.ps1 @@ -0,0 +1,51 @@ +# Thanks to https://gallery.technet.microsoft.com/scriptcenter/Sort-With-Custom-List-07b1d93a +Function Sort-ObjectWithCustomList { + Param ( + [parameter(ValueFromPipeline=$true)] + [PSObject] + $InputObject, + + [parameter(Position=1)] + [String] + $Property, + + [parameter()] + [Object[]] + $CustomList + ) + Begin + { + # convert customList (array) to hash + $hash = @{} + $rank = 0 + $customList | Select-Object -Unique | ForEach-Object { + $key = $_ + $hash.Add($key, $rank) + $rank++ + } + + # create script block for sorting + # items not in custom list will be last in sort order + $sortOrder = { + $key = if ($Property) { $_.$Property } else { $_ } + $rank = $hash[$key] + if ($rank -ne $null) { + $rank + } else { + [System.Double]::PositiveInfinity + } + } + + # create a place to collect objects from pipeline + # (I don't know how to match behavior of Sort's InputObject parameter) + $objects = @() + } + Process + { + $objects += $InputObject + } + End + { + $objects | Sort-Object -Property $sortOrder + } +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Test-Dotnet.ps1 b/.build/modules/PSDepend/0.3.8/Private/Test-Dotnet.ps1 new file mode 100644 index 0000000..b21b491 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Test-Dotnet.ps1 @@ -0,0 +1,65 @@ +# This tests if if the .NET SDK of the specified version exists +# If you specify the InstallDir, it will check if the .NET SDK exists there +# Otherwise it will use the global .NET SDK location. +function Test-Dotnet { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [string] + $Version, + + [Parameter()] + [string] + $InstallDir + ) + + $IsWindowsEnv = !$PSVersionTable.Platform -or $PSVersionTable.Platform -eq "Win32NT" + $dotnetFile = if ($IsWindowsEnv) { "dotnet.exe" } else { "dotnet" } + + if ($InstallDir) { + $dotnetExePath = Join-Path -Path $InstallDir -ChildPath $dotnetFile + } else { + # If dotnet is already in the PATH, check to see if that version of dotnet can find the required SDK. + # This is "typically" the globally installed dotnet. + $dotnetInPath = Get-Command 'dotnet' -ErrorAction SilentlyContinue + if ($dotnetInPath) { + $dotnetExePath = $dotnetInPath.Source + } else { + $LocalDotnetDirPath = if ($IsWindowsEnv) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" } + $dotnetExePath = Join-Path -Path $LocalDotnetDirPath -ChildPath $dotnetFile + } + } + + if (Test-Path $dotnetExePath) { + $installedVersion = Get-DotnetVersion $dotnetExePath + if ($Version -eq 'latest') { + # TODO: This could query the version if you have the latest + return $false + } else { + # We need to separate the prerelease from the version + $installedVer, $installedPre = ($installedVersion -split '-') + $ver, $pre = ($Version -split '-') + + if ([version] $installedVer -gt [version] $ver) { return $true } + if ([version] $installedVer -lt [version] $ver) { return $false } + + # Handle the case if they have the same version but no prerelease + if ($installedPre -eq "") { return $true } + if ($pre -eq "") { return $false } + + # Compare prerelease if they both have them + return $installedPre -ge $pre + } + } + return $false +} + +# Pulled out for mocking purpose +function Get-DotnetVersion { + param( + [string] + $dotnetExePath + ) + + & $dotnetExePath --version +} diff --git a/.build/modules/PSDepend/0.3.8/Private/Test-PlatformSupport.ps1 b/.build/modules/PSDepend/0.3.8/Private/Test-PlatformSupport.ps1 new file mode 100644 index 0000000..a88370a --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Test-PlatformSupport.ps1 @@ -0,0 +1,42 @@ +function Test-PlatformSupport { + [cmdletbinding()] + param( + $Type, + [string[]]$Support + ) + + # test core/full + if('Core' -eq $PSVersionTable.PSEdition) { + if($Support -notcontains 'core') { + Write-Verbose "Supported platforms [$Support] for type [$Type] does not contain [core]. Pull requests welcome!" + return $false + } + } + else { # full windows powershell + if($Support -notcontains 'windows') { + Write-Verbose "Supported platforms [$Support] for type [$Type] does not contain [windows]. Pull requests welcome!" + return $false + } + } + + if($IsLinux) { + if($Support -notcontains 'linux') { + Write-Verbose "Supported platforms [$Support] for type [$Type] does not contain [linux]. Pull requests welcome!" + return $false + } + } + if($IsMacOS) { + if($Support -notcontains 'macos') { + Write-Verbose "Supported platforms [$Support] for type [$Type] does not contain [macos]. Pull requests welcome!" + return $false + } + } + if($IsWindows) { + # covers support for core powershell on windows + if($Support -notcontains 'windows') { + Write-Verbose "Supported platforms [$Support] for type [$Type] does not contain [windows]. Pull requests welcome!" + return $false + } + } + $true +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Private/Validate-DependencyParameters.ps1 b/.build/modules/PSDepend/0.3.8/Private/Validate-DependencyParameters.ps1 new file mode 100644 index 0000000..5560121 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Private/Validate-DependencyParameters.ps1 @@ -0,0 +1,15 @@ +function Validate-DependencyParameters { + [cmdletbinding()] + param( + [string[]]$Required, + [string[]]$Parameters + ) + foreach($RequiredParam in $Required) + { + if($Parameters -notcontains $RequiredParam) + { + return $false + } + } + $true +} \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/Public/Get-Dependency.ps1 b/.build/modules/PSDepend/0.3.8/Public/Get-Dependency.ps1 new file mode 100644 index 0000000..3372f5a --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Get-Dependency.ps1 @@ -0,0 +1,491 @@ +function Get-Dependency { + <# + .SYNOPSIS + Read a dependency psd1 file + + .DESCRIPTION + Read a dependency psd1 file + + The resulting object contains these properties + DependencyFile : Path to psd1 file this dependency is defined in + DependencyName : Unique dependency name (the key in the psd1 file). We reserve PSDependOptions for global options. + DependencyType : Type of dependency. See Get-PSDependType + Name : Name for the dependency + Version : Version for the dependency + Parameters : Hash table of parameters to pass to dependency script + Source : Source for the dependency + Target : Target for the dependency + AddToPath : If specified and dependency type supports it, add dependency to path (e.g. a module is added to PSModulePath) + Tags : One or more tags to categorize or filter dependencies + DependsOn : Dependency that must be installed before this + PreScripts : One or more paths to PowerShell scripts to run before the dependency + PostScripts : One or more paths to PowerShell scripts to run after the dependency + PSDependOptions: Hash table of global PSDepend options + Raw : Raw output for this dependency from the PSD1. May include data outside of standard items above. + + These are parsed from dependency PSD1 files as follows: + + Simple syntax, intepreted: + @{ + DependencyName = 'Version' + } + + With the simple syntax using interpretation: + * The DependencyName (key) is used as the Name + * If no DependencyType is specified, we parse the DependencyName to pick a default: + * We default to GitHub if the DependencyName has a single / (e.g. aaa/bbb) + * We default to git if the DependencyName has more than one / (e.g. https://gitlab.fqdn/org/some.git) + * We default to PSGalleryModule in all other cases + * The Version (value) is a string, and is used as the Version + * Other properties are set to $null + + Simple syntax, with helpers: + @{ + DependencyType::DependencyName = 'Version' + } + + With the simple syntax using helpers: + * The dependency type and dependency name are included in the key (DependencyType::DependencyName, e.g. PSGalleryModule::Pester) + * The version (value) is a string, and is used as the Version + * Other properties are set to $null + + Advanced syntax: + @{ + DependencyName = @{ + DependencyType = 'TypeOfDependency'. # See Get-PSDependType + Name = 'NameForThisDependency' + Version = '0.1.0' + Parameters = @{ Example = 'Value' } # Optional parameters for the dependency script. + Source = 'Some source' # Usually optional + Target = 'Some target' # Usually optional + AddToPath = $True # Whether to add new dependency to path, if dependency type supports it. + Tags = 'prod', 'local' # One or more tags to categorize or filter dependencies + DependsOn = 'Some_Other_DependencyName' # DependencyName that must run before this + PreScripts = 'C:\script.ps1' # Script(s) to run before this dependency + PostScripts = 'C:\script2.ps1' # Script(s) to run after this dependency + } + } + + We use the same default DependencyTypes for this advanced syntax + + Global options: + @{ + PSDependOptions = @{ + Target = 'C:\temp' + } + # Supported for: + # Parameters + # Source + # Target + # AddToPath + # Tags + # DependsOn + # PreScripts + # PostScripts + + # Dependencies use these values as a default, unless you specify them explicitly for a dependency + } + + Note that you can mix these syntax together in the same psd1. + + .PARAMETER Path + Path to project root or dependency file. + + If a folder is specified, we search for and process *.depend.psd1 and requirements.psd1 files. + + .PARAMETER Tags + Limit results to one or more tags defined in the Dependencies + + .PARAMETER Recurse + If specified and path is a container, search for *.depend.psd1 and requirements.psd1 files recursively under $Path + + .PARAMETER InputObject + If specified instead of Path, treat this hashtable as the contents of a dependency file. + + For example: + + -InputObject @{ + BuildHelpers = 'latest' + PSDeploy = 'latest' + InvokeBuild = 'latest' + } + + .PARAMETER Credentials + Specifies a hashtable of PSCredentials to use for each dependency that is served from a private feed. + + For example: + + -Credentials @{ + PrivatePackage = $privateCredentials + AnotherPrivatePackage = $morePrivateCredenials + } + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-PSDependScript + + .LINK + Get-PSDependType + + .LINK + Install-Dependency + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding(DefaultParameterSetName = 'File')] + param( + [parameter(ParameterSetName='File')] + [string[]]$Path = $PWD.Path, + + [string[]]$Tags, + + [parameter(ParameterSetName='File')] + [switch]$Recurse, + + [parameter(ParameterSetName='Hashtable')] + [hashtable[]]$InputObject, + + [parameter(ParameterSetName='File')] + [parameter(ParameterSetName='Hashtable')] + [hashtable]$Credentials + ) + + # Helper to pick from global psdependoptions, or return a default + function Get-GlobalOption { + param( + $Options = $PSDependOptions, + $Name, + $Prefer, + $Default = $null + ) + # Check for preferred value, otherwise try to get value from key, otherwise use default.... + $Output = $Default + if($Prefer) + { + $Output = $Prefer + } + else + { + try + { + $Output = $Options[$Name] + } + catch + { + $Output = $Default + } + } + + # Inject variables + if( $Name -eq 'Target' -or + $Name -eq 'Source' -or + $Name -eq 'PreScripts' -or + $Name -eq 'PostScripts') + { + $Output = Inject-Variable $Output + } + $Output + } + + function Inject-Variable { + [cmdletbinding()] + param( $Value ) + $Output = $Value + switch($Value) + { + {$_ -match '^\.$|^\.\\|^\./'}{ + $Output = $Output -replace '^\.', $PWD.Path + } + + {$_ -Match '\$PWD'} { + $Output = $Output -replace '\$PWD', $PWD.Path + } + + {$_ -Match '\$ENV:ProgramData'} { + $Output = $Output -replace '\$ENV:ProgramData', $ENV:ProgramData + } + {$_ -Match '\$ENV:USERPROFILE'} { + $Output = $Output -replace '\$ENV:USERPROFILE', $ENV:USERPROFILE + } + {$_ -Match '\$ENV:APPDATA'} { + $Output = $Output -replace '\$ENV:APPDATA', $ENV:APPDATA + } + {$_ -Match '\$ENV:TEMP'} { + $Output = $Output -replace '\$ENV:TEMP', $ENV:TEMP + } + + {$_ -Match '\$DependencyFolder|\$DependencyPath'} { + $DependencyFolder = Split-Path $DependencyFile -Parent + $Output = $Output -replace '\$DependencyFolder|\$DependencyPath', $DependencyFolder + } + } + $Output + } + + # Helper to take in a dependency hash and output Dependency objects + function Parse-Dependency { + [cmdletbinding()] + param( + $ParamSet = $PSCmdlet.ParameterSetName + ) + + # Global settings.... + $PSDependOptions = $null + if($Dependencies.Containskey('PSDependOptions')) + { + $PSDependOptions = $Dependencies.PSDependOptions + $Dependencies.Remove('PSDependOptions') + } + + foreach($Dependency in $Dependencies.keys) + { + $DependencyHash = $Dependencies.$Dependency + $DependencyType = Get-GlobalOption -Name DependencyType + + $CredentialName = Get-GlobalOption -Name Credential + + # Look simple syntax with helpers in the key first + If( $DependencyHash -is [string] -and + $Dependency -match '::' -and + ($Dependency -split '::').count -eq 2 + ) + { + [pscustomobject]@{ + PSTypeName = 'PSDepend.Dependency' + DependencyFile = $DependencyFile + DependencyName = ($Dependency -split '::')[1] + DependencyType = ($Dependency -split '::')[0] + Name = ($Dependency -split '::')[1] + Version = $DependencyHash + Parameters = Get-GlobalOption -Name Parameters + Source = Get-GlobalOption -Name Source + Target = Get-GlobalOption -Name Target + AddToPath = Get-GlobalOption -Name AddToPath + Tags = Get-GlobalOption -Name Tags + DependsOn = Get-GlobalOption -Name DependsOn + PreScripts = Get-GlobalOption -Name PreScripts + PostScripts = Get-GlobalOption -Name PostScripts + PSDependOptions = $PSDependOptions + Raw = $null + } + } + #Parse simple key=name, value=version format + # It doesn't look like a git repo, and simple syntax: PSGalleryModule + elseif( $DependencyHash -is [string] -and + $Dependency -notmatch '/' -and + -not $DependencyType -or + $DependencyType -eq 'PSGalleryModule') + { + [pscustomobject]@{ + PSTypeName = 'PSDepend.Dependency' + DependencyFile = $DependencyFile + DependencyName = $Dependency + DependencyType = 'PSGalleryModule' + Name = $Dependency + Version = $DependencyHash + Parameters = Get-GlobalOption -Name Parameters + Source = Get-GlobalOption -Name Source + Target = Get-GlobalOption -Name Target + AddToPath = Get-GlobalOption -Name AddToPath + Tags = Get-GlobalOption -Name Tags + DependsOn = Get-GlobalOption -Name DependsOn + PreScripts = Get-GlobalOption -Name PreScripts + PostScripts = Get-GlobalOption -Name PostScripts + Credential = Resolve-Credential -Name $CredentialName + PSDependOptions = $PSDependOptions + Raw = $null + } + } + # It looks like a git repo, simple syntax, and not a full URI + elseif($DependencyHash -is [string] -and + $Dependency -match '/' -and + $Dependency.split('/').count -eq 2 -and + -not $DependencyType -or + $DependencyType -eq 'GitHub') + { + [pscustomobject]@{ + PSTypeName = 'PSDepend.Dependency' + DependencyFile = $DependencyFile + DependencyName = $Dependency + DependencyType = 'GitHub' + Name = $Dependency + Version = $DependencyHash + Parameters = Get-GlobalOption -Name Parameters + Source = Get-GlobalOption -Name Source + Target = Get-GlobalOption -Name Target + AddToPath = Get-GlobalOption -Name AddToPath + Tags = Get-GlobalOption -Name Tags + DependsOn = Get-GlobalOption -Name DependsOn + PreScripts = Get-GlobalOption -Name PreScripts + PostScripts = Get-GlobalOption -Name PostScripts + PSDependOptions = $PSDependOptions + Raw = $null + } + } + # It looks like a git repo, and simple syntax: Git + elseif($DependencyHash -is [string] -and + $Dependency -match '/' -and + -not $DependencyType -or + $DependencyType -eq 'Git' ) + { + [pscustomobject]@{ + PSTypeName = 'PSDepend.Dependency' + DependencyFile = $DependencyFile + DependencyName = $Dependency + DependencyType = 'Git' + Name = $Dependency + Version = $DependencyHash + Parameters = Get-GlobalOption -Name Parameters + Source = Get-GlobalOption -Name Source + Target = Get-GlobalOption -Name Target + AddToPath = Get-GlobalOption -Name AddToPath + Tags = Get-GlobalOption -Name Tags + DependsOn = Get-GlobalOption -Name DependsOn + PreScripts = Get-GlobalOption -Name PreScripts + PostScripts = Get-GlobalOption -Name PostScripts + PSDependOptions = $PSDependOptions + Raw = $null + } + } + else + { + # Parse dependency hash format + # Default type is module, unless it's in a git-style format + if(-not $DependencyHash.DependencyType) + { + # Is it a global option? + if($DependencyType) {} + # GitHub first + elseif( + # Ugly right? Watch out for split called on hashtable... + ($Dependency -match '/' -and -not $Dependency.Name -and + ($Dependency -is [string] -and $Dependency.split('/').count -eq 2) + ) -or + ($DependencyHash.Name -match '/' -and + ($DependencyHash -is [string] -and $DependencyHash.split('/').count -eq 2) + ) + ) + { + $DependencyType = 'GitHub' + } + # Now git... + elseif( + ($Dependency -match '/' -and -not $Dependency.Name) -or + $DependencyHash.Name -match '/' + ) + { + $DependencyType = 'Git' + } + else # finally, psgallerymodule + { + $DependencyType = 'PSGalleryModule' + } + } + else + { + $DependencyType = $DependencyHash.DependencyType + } + + $CredentialName = Get-GlobalOption -Name Credential -Prefer $DependencyHash.Credential + [pscustomobject]@{ + PSTypeName = 'PSDepend.Dependency' + DependencyFile = $DependencyFile + DependencyName = $Dependency + DependencyType = $DependencyType + Name = $DependencyHash.Name + Version = $DependencyHash.Version + Parameters = Get-GlobalOption -Name Parameters -Prefer $DependencyHash.Parameters + Source = Get-GlobalOption -Name Source -Prefer $DependencyHash.Source + Target = Get-GlobalOption -Name Target -Prefer $DependencyHash.Target + AddToPath = Get-GlobalOption -Name AddToPath -Prefer $DependencyHash.AddToPath + Tags = Get-GlobalOption -Name Tags -Prefer $DependencyHash.Tags + DependsOn = Get-GlobalOption -Name DependsOn -Prefer $DependencyHash.DependsOn + PreScripts = Get-GlobalOption -Name PreScripts -Prefer $DependencyHash.PreScripts + PostScripts = Get-GlobalOption -Name PostScripts -Prefer $DependencyHash.PostScripts + Credential = Resolve-Credential -Name $CredentialName + PSDependOptions = $PSDependOptions + Raw = $DependencyHash + } + } + } + } + + # Heleper to retrieve the credential for a dependency + function Resolve-Credential { + [CmdletBinding()] + param ( + [string]$Name + ) + + $credential = $null + if (($null -ne $Name) -and ($null -ne $Credentials)) { + + if ($Credentials.ContainsKey($Name)) { + $credential = $Credentials[$Name] + } else { + Write-Warning "No credential found for the specified name $Name. Was the dependency misconfigured?" + } + } + + return $credential + } + + if($PSCmdlet.ParameterSetName -eq 'File') + { + $ParsedDependencies = foreach($DependencyPath in $Path) + { + #Resolve relative paths... Thanks Oisin! http://stackoverflow.com/a/3040982/3067642 + $DependencyPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DependencyPath) + + if(Test-Path $DependencyPath -PathType Container) + { + $DependencyFiles = @( Resolve-DependScripts -Path $DependencyPath -Recurse $Recurse ) + } + else + { + $DependencyFiles = @( $DependencyPath ) + } + $DependencyFiles = $DependencyFiles | Select-Object -Unique + + foreach($DependencyFile in $DependencyFiles) + { + # Read the file + $Base = Split-Path $DependencyFile -Parent + $File = Split-Path $DependencyFile -Leaf + $Dependencies = Import-LocalizedData -BaseDirectory $Base -FileName $File + + Parse-Dependency -ParamSet $PSCmdlet.ParameterSetName + } + } + } + elseif($PSCmdlet.ParameterSetName -eq 'Hashtable') + { + $DependencyFile = 'Hashtable' + $ParsedDependencies = foreach($InputDependency in $InputObject) + { + $Dependencies = $InputDependency + + Parse-Dependency -ParamSet $PSCmdlet.ParameterSetName + } + } + + If($PSBoundParameters.ContainsKey('Tags')) + { + $ParsedDependencies = Get-TaggedDependency -Dependency $ParsedDependencies -Tags $Tags + if(-not $ParsedDependencies) + { + Write-Warning "No dependencies found with tags '$tags'" + return + } + } + Sort-PSDependency -Dependencies $ParsedDependencies +} diff --git a/.build/modules/PSDepend/0.3.8/Public/Get-PSDependScript.ps1 b/.build/modules/PSDepend/0.3.8/Public/Get-PSDependScript.ps1 new file mode 100644 index 0000000..fba005e --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Get-PSDependScript.ps1 @@ -0,0 +1,89 @@ +Function Get-PSDependScript { + <# + .SYNOPSIS + Get dependency types and associated scripts + + .DESCRIPTION + Get dependency types and associated scripts + + Checks PSDependMap.psd1, + verifies dependency scripts exist, + returns a hashtable of these. + + .PARAMETER Path + Path to PSDependMap.psd1 defining dependency types + + Defaults to PSDependMap.psd1 in the module root + + .EXAMPLE + Get-PSDependScript + + # List PSDepend scripts available in the PSDependMap.psd1 in the PSDepend module root + + .EXAMPLE + Get-PSDependScript -Path \\Path\To\Central.DependencyMap.psd1 + + # List dependency types defined in a centralized dependency map + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Get-PSDependType + + .LINK + Install-Dependency + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding()] + param( + [validatescript({Test-Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$Path = $(Join-Path $ModuleRoot PSDependMap.psd1) + ) + + # Read the file + $Base = Split-Path $Path -Parent + $File = Split-Path $Path -Leaf + $DependencyDefinitions = Import-LocalizedData -BaseDirectory $Base -FileName $File + + $DependHash = @{} + foreach($DependencyType in $DependencyDefinitions.Keys) + { + #Determine the path to this script + $Script = $DependencyDefinitions.$DependencyType.Script + if(Test-Path $Script -ErrorAction SilentlyContinue) + { + $ScriptPath = $Script + } + else + { + # account for missing ps1 + $ScriptPath = Join-Path $ModuleRoot "PSDependScripts\$($Script -replace ".ps1$").ps1" + } + + if(test-path $ScriptPath) + { + $DependHash.$DependencyType = $ScriptPath + } + else + { + Write-Error "Could not find path '$ScriptPath' for dependency $DependencyType. Origin: $($DependencyDefinitions.$DependencyType.Script)" + } + } + + $DependHash +} + + + diff --git a/.build/modules/PSDepend/0.3.8/Public/Get-PSDependType.ps1 b/.build/modules/PSDepend/0.3.8/Public/Get-PSDependType.ps1 new file mode 100644 index 0000000..efa6aab --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Get-PSDependType.ps1 @@ -0,0 +1,126 @@ +Function Get-PSDependType { + <# + .SYNOPSIS + Get dependency types and related information + + .DESCRIPTION + Get dependency types and related information + + Checks PSDependMap.psd1 for dependency types, + verifies dependency scripts exist, + gets help content for dependency scripts, + returns various info on each dependency type + + .PARAMETER Path + Path to PSDependMap.psd1 defining dependency types + + Defaults to PSDependMap.psd1 in the module root + + .PARAMETER DependencyType + Optionally limited to this DependencyType + + Accepts wildcards + + .PARAMETER ShowHelp + Show help content for specified dependency types + + .PARAMETER SkipHelp + Skip retreieving help. Mainly for internl use when it is not required + + .EXAMPLE + Get-PSDependType -DependencyType PSGalleryModule -ShowHelp + + Show help for the PSGalleryModule dependency type. + + .EXAMPLE + Get-PSDependType + + # List dependency types defined in PSDependMap.psd1 in the root of your PSDepend module folder + + .EXAMPLE + Get-PSDependType -Path \\Path\To\Central.DependencyMap.psd1 + + # List dependency types defined in a centralized dependency map + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Get-PSDependScript + + .LINK + Install-Dependency + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding()] + param( + [string]$DependencyType = '*', + [validatescript({Test-Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$Path = $(Join-Path $ModuleRoot PSDependMap.psd1), + [switch]$ShowHelp, + [switch]$SkipHelp + ) + + # Read the file + $Base = Split-Path $Path -Parent + $File = Split-Path $Path -Leaf + $DependencyDefinitions = Import-LocalizedData -BaseDirectory $Base -FileName $File + $KeysToQuery = $DependencyDefinitions.Keys | + Where-Object {$_ -like $DependencyType} | + Sort-Object + foreach($Type in $KeysToQuery) + { + #Determine the path to this script. Skip task dependencies... + $Script = $DependencyDefinitions.$Type.Script + if($Script -ne '.') + { + if(Test-Path $Script) + { + $ScriptPath = $Script + } + else + { + # account for missing ps1 + $ScriptPath = Join-Path $ModuleRoot "PSDependScripts\$($Script -replace ".ps1$").ps1" + } + + If (-not $SkipHelp) { + Try + { + $ScriptHelp = Get-Help $ScriptPath -Full -ErrorAction Stop + } + Catch + { + $ScriptHelp = "Error retrieving help: $_" + } + } + } + if($ShowHelp) + { + $ScriptHelp + } + else + { + $Support = @($DependencyDefinitions.$Type.Supports) + [pscustomobject]@{ + DependencyType = $Type + Supports = $Support + Supported = Test-PlatformSupport -Type $Type -Support $Support + Description = $DependencyDefinitions.$Type.Description + DependencyScript = $ScriptPath + HelpContent = $ScriptHelp + } + } + } +} diff --git a/.build/modules/PSDepend/0.3.8/Public/Import-Dependency.ps1 b/.build/modules/PSDepend/0.3.8/Public/Import-Dependency.ps1 new file mode 100644 index 0000000..622550b --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Import-Dependency.ps1 @@ -0,0 +1,67 @@ +Function Import-Dependency { + <# + .SYNOPSIS + Import a specific dependency + + .DESCRIPTION + Import a specific dependency, if that dependency supports it. + + Takes output from Get-Dependency + + * Runs dependency scripts depending on each dependencies type. + * Imports items if supported + + See Get-Help about_PSDepend for more information. + + .PARAMETER Dependency + Dependency object from Get-Dependency. + + .PARAMETER PSDependTypePath + Specify a PSDependMap.psd1 file that maps DependencyTypes to their scripts. + + This defaults to the PSDependMap.psd1 in the PSDepend module folder + + .PARAMETER Tags + Only test dependencies that are tagged with all of the specified Tags (-and, not -or) + + .EXAMPLE + Get-Dependency -Path C:\requirements.psd1 | Import-Dependency + + Get dependencies from C:\requirements.psd1 and import them + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Get-PSDependType + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding()] + Param( + [parameter( ValueFromPipeline = $True, + ParameterSetName='Map', + Mandatory = $True)] + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [validatescript({Test-Path -Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$PSDependTypePath = $(Join-Path $ModuleRoot PSDependMap.psd1), + + [string[]]$Tags + ) + Process + { + Invoke-DependencyScript @PSBoundParameters -PSDependAction Import + } +} diff --git a/.build/modules/PSDepend/0.3.8/Public/Install-Dependency.ps1 b/.build/modules/PSDepend/0.3.8/Public/Install-Dependency.ps1 new file mode 100644 index 0000000..e091914 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Install-Dependency.ps1 @@ -0,0 +1,72 @@ +Function Install-Dependency { + <# + .SYNOPSIS + Install a specific dependency + + .DESCRIPTION + Install a specific dependency. Typically you would use Invoke-PSDepend rather than this. + + Takes output from Get-Dependency + + * Runs dependency scripts depending on each dependencies type. + * If a dependency is not found, we continue processing other dependencies. + + See Get-Help about_PSDepend for more information. + + .PARAMETER Dependency + Dependency object from Get-Dependency. + + .PARAMETER PSDependTypePath + Specify a PSDependMap.psd1 file that maps DependencyTypes to their scripts. + + This defaults to the PSDependMap.psd1 in the PSDepend module folder + + .PARAMETER Tags + Only invoke dependencies that are tagged with all of the specified Tags (-and, not -or) + + .PARAMETER Force + Force installation, skipping prompts and confirmation + + .EXAMPLE + Get-Dependency -Path C:\requirements.psd1 | Install-Dependency + + Get dependencies from C:\requirements.psd1 and install them + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Get-PSDependType + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding( SupportsShouldProcess = $True, + ConfirmImpact='High' )] + Param( + [parameter( ValueFromPipeline = $True, + Mandatory = $True)] + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [validatescript({Test-Path -Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$PSDependTypePath = $(Join-Path $ModuleRoot PSDependMap.psd1), + + [string[]]$Tags, + + [switch]$Force + ) + Process + { + Invoke-DependencyScript @PSBoundParameters -PSDependAction Install + } +} diff --git a/.build/modules/PSDepend/0.3.8/Public/Invoke-DependencyScript.ps1 b/.build/modules/PSDepend/0.3.8/Public/Invoke-DependencyScript.ps1 new file mode 100644 index 0000000..3e492e5 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Invoke-DependencyScript.ps1 @@ -0,0 +1,208 @@ +Function Invoke-DependencyScript { + <# + .SYNOPSIS + Invoke a dependency script + + .DESCRIPTION + Invoke a dependency script + + See Get-Help about_PSDepend for more information. + + .PARAMETER Dependency + Dependency object from Get-Dependency. + + .PARAMETER PSDependTypePath + Specify a PSDependMap.psd1 file that maps DependencyTypes to their scripts. + + This defaults to the PSDependMap.psd1 in the PSDepend module folder + + .PARAMETER Tags + Only test dependencies that are tagged with all of the specified Tags (-and, not -or) + + .PARAMETER PSDependAction + PSDependAction to run. Test, Install, and Import are the most common. + + Test can only be run by itself. + + .PARAMETER Quiet + If PSDependAction is Test, and Quiet is specified, we return $true or $false based on whether a dependency exists + + .EXAMPLE + Get-Dependency -Path C:\requirements.psd1 | Import-Dependency + + Get dependencies from C:\requirements.psd1 and import them + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Get-PSDependType + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding()] + Param( + [parameter( ValueFromPipeline = $True, + ParameterSetName='Map', + Mandatory = $True)] + [PSTypeName('PSDepend.Dependency')] + [psobject]$Dependency, + + [validatescript({Test-Path -Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$PSDependTypePath = $(Join-Path $ModuleRoot PSDependMap.psd1), + + [string[]]$PSDependAction, + + [string[]]$Tags, + + [switch]$Quiet + ) + Begin + { + # This script reads a depend.psd1, installs dependencies as defined + Write-Verbose "Running Invoke-DependencyScript with ParameterSetName '$($PSCmdlet.ParameterSetName)' and params: $($PSBoundParameters | Out-String)" + $PSDependTypes = Get-PSDependType -SkipHelp + } + Process + { + Write-Verbose "Dependencies:`n$($Dependency | Select-Object -Property * | Out-String)" + + #Get definitions, and dependencies in this particular psd1 + $DependencyDefs = Get-PSDependScript + $TheseDependencyTypes = @( $Dependency.DependencyType | Sort-Object -Unique ) + + #Build up hash, we call each dependencytype script for applicable dependencies + foreach($DependencyType in $TheseDependencyTypes) + { + $PSDependType = ($PSDependTypes | Where-Object {$_.DependencyType -eq $DependencyType}) + if(-not $PSDependType.Supported) + { + Write-Warning "Skipping dependency type [$DependencyType]`nThis dependency does not support your platform`nSupported platforms: [$($PSDependType.Supports)]" + continue + } + $DependencyScript = $DependencyDefs.$DependencyType + if(-not $DependencyScript) + { + Write-Error "DependencyType $DependencyType is not defined in PSDependMap.psd1" + continue + } + $TheseDependencies = @( $Dependency | Where-Object {$_.DependencyType -eq $DependencyType}) + + #Define params for the script + #Each dependency type can have a hashtable to splat. + $RawParameters = Get-Parameter -Command $DependencyScript + $ValidParamNames = $RawParameters.Name + Write-Verbose "Found parameters [$ValidParamNames]" + + if($ValidParamNames -notcontains 'PSDependAction') + { + Write-Error "No PSDependAction found on PSDependScript [$DependencyScript]. Skipping [$($Dependency.DependencyName)]" + continue + } + [string[]]$ValidPSDependActions = $RawParameters | + Where-Object {$_.Name -like 'PSDependAction'} | + Select-Object -ExpandProperty ValidateSetValues -ErrorAction SilentlyContinue + [string[]]$PSDependActions = foreach($Action in $PSDependAction) + { + if($ValidPSDependActions -contains $Action) {$Action} + else + { + Write-Warning "Skipping PSDependAction [$Action] for dependency [$($Dependency.DependencyName)]. Valid actions: [$ValidPSDependActions]" + } + } + + if($PSDependActions.count -like 0) + { + Write-Verbose "Skipped dependency [$($Dependency.DependencyName)] due to filtered PSDependAction. See Warnings above." + continue + } + + if($PSDependActions -contains 'Test' -and ( $PSDependActions -contains 'Import' -or $PSDependActions -contains 'Install')) + { + Write-Error "Removing [Test] from PSDependActions. The Test action must run on its own." + $PSDependActions = $PSDependActions | Where-Object {$_ -ne 'Test'} + } + + foreach($ThisDependency in $TheseDependencies) + { + #Parameters for dependency types. Only accept valid params... + if($ThisDependency.Parameters.keys.count -gt 0) + { + $splat = @{} + foreach($key in $ThisDependency.Parameters.keys) + { + if($ValidParamNames -contains $key) + { + $splat.Add($key, $ThisDependency.Parameters.$key) + } + else + { + Write-Warning "Parameter [$Key] with value [$($ThisDependency.Parameters.$Key)] is not a valid parameter for [$DependencyType], ignoring. Valid params:`n[$ValidParamNames]" + } + } + + if($ThisDependency.Parameters.Import -and $PSDependActions -notcontains 'Test') + { + $PSDependActions += 'Import' + $PSDependActions = $PSDependActions | Sort-Object -Unique + } + + if($splat.ContainsKey('PSDependAction')) + { + $Splat['PSDependAction'] = $PSDependActions + } + else + { + $Splat.add('PSDependAction', $PSDependActions) + } + } + else + { + $splat = @{PSDependAction = $PSDependActions} + } + + #Define params for the script + $splat.add('Dependency', $ThisDependency) + + # PITA, but tasks can run two ways, each different than typical dependency scripts + if($PSDependActions -contains 'Install' -and $DependencyType -eq 'Task') + { + foreach($TaskScript in $ThisDependency.Target) + { + if( Test-Path $TaskScript -PathType Leaf) + { + . $TaskScript @splat + } + else + { + Write-Error "Could not process task [$TaskScript].`nAre connectivity, privileges, and other needs met to access it?" + } + } + } + else + { + Write-Verbose "Invoking '$DependencyScript' with parameters $($Splat | Out-String)" + $Output = . $DependencyScript @splat + if($PSDependActions -contains 'Test' -and -not $Quiet) + { + Add-Member -InputObject $ThisDependency -MemberType NoteProperty -Name DependencyExists -Value $Output -Force -PassThru + } + else + { + $Output + } + } + } + } + } +} diff --git a/.build/modules/PSDepend/0.3.8/Public/Invoke-PSDepend.ps1 b/.build/modules/PSDepend/0.3.8/Public/Invoke-PSDepend.ps1 new file mode 100644 index 0000000..d46d329 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Invoke-PSDepend.ps1 @@ -0,0 +1,324 @@ +Function Invoke-PSDepend { + <# + .SYNOPSIS + Invoke PSDepend + + .DESCRIPTION + Invoke PSDepend + + Searches for and runs *.depend.psd1 and requirements.psd1 files in the current and nested paths + + See Get-Help about_PSDepend for more information. + + .PARAMETER Path + Path to a specific depend.psd1 file, or to a folder that we recursively search for *.depend.psd1 files + + Defaults to the current path + + .PARAMETER Recurse + If path is a folder, whether to recursively search for *.depend.psd1 and requirements.psd1 files under that folder + + Defaults to $True + + .PARAMETER InputObject + If specified instead of Path, treat this hashtable as the contents of a dependency file. + + For example: + + -InputObject @{ + BuildHelpers = 'latest' + PSDeploy = 'latest' + InvokeBuild = 'latest' + } + + .PARAMETER Tags + Only invoke dependencies that are tagged with all of the specified Tags (-and, not -or) + + .PARAMETER PSDependTypePath + Specify a PSDependMap.psd1 file that maps DependencyTypes to their scripts. + + This defaults to the PSDependMap.psd1 in the PSDepend module folder + + .PARAMETER Force + Force dependency, skipping prompts and confirmation + + .PARAMETER Test + Run tests for dependencies we find. + + Appends a 'DependencyExists' property indicating whether the dependency exists by default + Specify Quiet to simply return $true or $false depending on whether all dependencies exist + + .PARAMETER Quiet + If specified along with Test, we return $true, or $false depending on whether all dependencies were met + + .PARAMETER Install + Run the install for a dependency + + Default behavior + + .PARAMETER Target + If specified, override the target in the PSDependOptions or Dependency. + + .PARAMETER Import + If the dependency supports it, import it + + .PARAMETER Credentials + Specifies a hashtable of PSCredentials to use for each dependency that is served from a private feed. The key of the hashtable must match the Credential property value in the dependency. + + For example: + + @{ + dependency_name = @{ + ... + Credential = 'PrivatePackage' + ... + } + } + + -Credentials @{ + PrivatePackage = $privateCredentials + AnotherPrivatePackage = $morePrivateCredenials + } + + .EXAMPLE + Invoke-PSDepend + + # Search for and run *.deploy.psd1 and requirements.psd1 files under the current path + + .EXAMPLE + Invoke-PSDepend -Path C:\Path\To\require.psd1 + + # Install dependencies from require.psd1 + + .EXAMPLE + Invoke-PSDepend -Path C:\Requirements -Recurse $False + + # Find and run *.depend.psd1 and requirements.psd1 files under C\Requirements (but not subfolders) + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Install-Dependency + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding( DefaultParameterSetName = 'installimport-file', + SupportsShouldProcess = $True, + ConfirmImpact='High' )] + Param( + [validatescript({Test-Path -Path $_ -ErrorAction Stop})] + [parameter( ParameterSetName = 'installimport-file', + Position = 0, + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [parameter( ParameterSetName = 'test-file', + Position = 0, + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [string[]]$Path = '.', + + [parameter( ParameterSetName = 'installimport-hashtable', + Position = 0, + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [parameter( ParameterSetName = 'test-hashtable', + Position = 0, + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [hashtable[]]$InputObject, + + [validatescript({Test-Path -Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$PSDependTypePath = $(Join-Path $ModuleRoot PSDependMap.psd1), + + [string[]]$Tags, + + [parameter(ParameterSetName = 'installimport-file')] + [parameter(ParameterSetName = 'test-file')] + [bool]$Recurse = $True, + + [parameter(ParameterSetName = 'test-file')] + [parameter(ParameterSetName = 'test-hashtable')] + [switch]$Test, + + [parameter(ParameterSetName = 'test-file')] + [parameter(ParameterSetName = 'test-hashtable')] + [switch]$Quiet, + + [parameter(ParameterSetName = 'installimport-file')] + [parameter(ParameterSetName = 'installimport-hashtable')] + [switch]$Import, + + [parameter(ParameterSetName = 'installimport-file')] + [parameter(ParameterSetName = 'installimport-hashtable')] + [switch]$Install, + + [switch]$Force, + + [String]$Target, + + [parameter(ParameterSetName = 'installimport-file')] + [parameter(ParameterSetName = 'installimport-hashtable')] + [hashtable]$Credentials + ) + Begin + { + # Build parameters + $InvokeParams = @{ + PSDependAction = @() + PSDependTypePath = $PSDependTypePath + } + $DoInstall = $PSCmdlet.ParameterSetName -like 'installimport-*' -and $Install + $DoImport = $PSCmdlet.ParameterSetName -like 'installimport-*' -and $Import + $DoTest = $PSCmdlet.ParameterSetName -like 'test-*' -and $Test + if($DoInstall){$InvokeParams.PSDependAction += 'Install'} + if($DoImport){$InvokeParams.PSDependAction += 'Import'} + if($DoTest){$InvokeParams.PSDependAction += 'Test'} + if($InvokeParams.PSDependAction.count -like 0) + { + $InvokeParams.PSDependAction += 'Install' + } + Write-Verbose "Running Invoke-PSDepend with ParameterSetName '$($PSCmdlet.ParameterSetName)', PSDependAction $($InvokeParams.PSDependAction), and params: $($PSBoundParameters | Out-String)" + + $DependencyFiles = New-Object System.Collections.ArrayList + $PSDependTypes = Get-PSDependType -Path $PSDependTypePath -SkipHelp + } + Process + { + $GetPSDependParams = @{} + + if($PSCmdlet.ParameterSetName -like '*-file') + { + foreach( $PathItem in $Path ) + { + # Create a map for dependencies + [void]$DependencyFiles.AddRange( @( Resolve-DependScripts -Path $PathItem -Recurse $Recurse ) ) + if ($DependencyFiles.count -gt 0) + { + Write-Verbose "Working with [$($DependencyFiles.Count)] dependency files from [$PathItem]:`n$($DependencyFiles | Out-String)" + } + else + { + Write-Warning "No *.depend.ps1 files found under [$PathItem]" + } + } + $GetPSDependParams.add('Path',$DependencyFiles) + } + elseif($PSCmdlet.ParameterSetName -like '*-hashtable') + { + $GetPSDependParams.add('InputObject',$InputObject) + } + + # Parse + if($PSBoundParameters.ContainsKey('Tags')) + { + $GetPSDependParams.Add('Tags',$Tags) + } + + if ($null -ne $Credentials) { + $GetPSDependParams.Add('Credentials', $Credentials) + } + + # Handle Dependencies + $Dependencies = Get-Dependency @GetPSDependParams + $Unsupported = ( $PSDependTypes | Where-Object {-not $_.Supported} ).DependencyType + $Dependencies = foreach($Dependency in $Dependencies) + { + if($Unsupported -contains $Dependency.DependencyType) + { + $Supports = $PSDependTypes | Where-Object {$_.DependencyType -eq $Dependency.DependencyType} | Select -ExpandProperty Supports + Write-Warning "Skipping unsupported dependency:`n$( $Dependency | Out-String)`nSupported platforms:`n$($Supports | Out-String)" + } + else + { + $Dependency + } + } + + if($DoTest -and $Quiet) + { + $TestResult = [System.Collections.ArrayList]@() + } + + #TODO: Add ShouldProcess here if install is specified... + foreach($Dependency in $Dependencies) + { + if($PSBoundParameters.ContainsKey('Target')) + { + Write-Verbose "Overriding Dependency target [$($Dependency.Target)] with target parameter value [$Target]" + $Dependency.Target = $Target + } + + if( ($Force -and -not $WhatIf) -or + ($DoTest) -or + $PSCmdlet.ShouldProcess( "Processed the dependency '$($Dependency.DependencyName -join ", ")'", + "Process the dependency '$($Dependency.DependencyName -join ", ")'?", + "Processing dependency" )) + { + $PreScriptSuccess = $True #anti pattern! Best I could come up with to handle both prescript fail and dependencies + if($DoInstall -and $Dependency.PreScripts.Count -gt 0) + { + $ExistingEA = $ErrorActionPreference + $ErrorActionPreference = 'Stop' + foreach($script in $Dependency.PreScripts) + { + Try + { + Write-Verbose "Invoking pre script: [$script]" + . $script + } + Catch + { + $PreScriptSuccess = $False + "Skipping installation due to failed pre script: [$script]" + Write-Error $_ + } + } + $ErrorActionPreference = $ExistingEA + } + + if($PreScriptSuccess) + { + if($DoTest -and $Quiet) + { + $null = $TestResult.Add( (Invoke-DependencyScript @InvokeParams -Dependency $Dependency -Quiet ) ) + } + else + { + Invoke-DependencyScript @InvokeParams -Dependency $Dependency + } + } + + if($DoInstall -and $Dependency.PostScripts.Count -gt 0) + { + foreach($script in $Dependency.PostScripts) + { + Write-Verbose "Invoking post script: $($script)" + . $script + } + } + } + } + if($DoTest -and $Quiet) + { + if($TestResult -contains $false) + { + $false + } + else + { + $true + } + } + } +} + + + diff --git a/.build/modules/PSDepend/0.3.8/Public/Test-Dependency.ps1 b/.build/modules/PSDepend/0.3.8/Public/Test-Dependency.ps1 new file mode 100644 index 0000000..b49dfe6 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/Public/Test-Dependency.ps1 @@ -0,0 +1,73 @@ +Function Test-Dependency { + <# + .SYNOPSIS + Test a specific dependency + + .DESCRIPTION + Test a specific dependency. Validates whether a dependency exists + + Takes output from Get-Dependency + + * Runs dependency scripts depending on each dependencies type. + * Appends a 'DependencyExists' property indicating whether the dependency exists; alternatively + * Returns $True or $False if the -Quiet switch is used + + See Get-Help about_PSDepend for more information. + + .PARAMETER Dependency + Dependency object from Get-Dependency. + + .PARAMETER PSDependTypePath + Specify a PSDependMap.psd1 file that maps DependencyTypes to their scripts. + + This defaults to the PSDependMap.psd1 in the PSDepend module folder + + .PARAMETER Tags + Only test dependencies that are tagged with all of the specified Tags (-and, not -or) + + .PARAMETER Quiet + Return $true or $false based on whether a dependency exists + + .EXAMPLE + Get-Dependency -Path C:\requirements.psd1 | Test-Dependency + + Get dependencies from C:\requirements.psd1 and test whether they exist + + .LINK + about_PSDepend + + .LINK + about_PSDepend_Definitions + + .LINK + Get-Dependency + + .LINK + Get-PSDependType + + .LINK + Invoke-PSDepend + + .LINK + https://github.com/RamblingCookieMonster/PSDepend + #> + [cmdletbinding()] + Param( + [parameter( ValueFromPipeline = $True, + ParameterSetName='Map', + Mandatory = $True)] + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency, + + [validatescript({Test-Path -Path $_ -PathType Leaf -ErrorAction Stop})] + [string]$PSDependTypePath = $(Join-Path $ModuleRoot PSDependMap.psd1), + + [string[]]$Tags, + + [switch]$Quiet + ) + process + { + Invoke-DependencyScript @PSBoundParameters -PSDependAction Test + } +} diff --git a/.build/modules/PSDepend/0.3.8/en-US/about_PSDepend.help.txt b/.build/modules/PSDepend/0.3.8/en-US/about_PSDepend.help.txt new file mode 100644 index 0000000..b5fdc13 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/en-US/about_PSDepend.help.txt @@ -0,0 +1,162 @@ +TOPIC + about_PSDepend + +SHORT DESCRIPTION + PSDepend is a module to simplify dependency handling + +DETAILED DESCRIPTION + PSDepend is a module to simplify dependency handling + + Think of this like a simplified (Ruby) Bundler, or (Python) pip requirements files + + Terminology and concepts + ======================== + There are three primary concepts: + + *.depend.psd1 files: PSD1 files that tell PSDepend what you want to install. + You can also use requirements.psd1 + + Dependency Type: These define how to actually install something. + Each type is associated with a script. + The default type is PSGalleryModule. + This is extensible. + + Dependency Script: These are scripts associated to a particular dependency type. + They tell PSDepend how to install a dependency type. + All should accept a 'Dependency' parameter. + For example, the PSGalleryModule script uses PowerShellGet to find + and install any dependencies specified in a depend.psd1 or requirements.psd1 file. + + Prerequisites + ============ + + Each dependency type may have certain prerequisites: + + * PSGalleryModule: Requires the PowerShellGet module (WMF 5 or a separate install) + * PSGalleryNuget: This requires nuget.exe in your path*. We handle this for you: + When you import PSDepend, we... + Look for nuget.exe in your path. + If we don't find it... Look in the path specified in PSDepend\PSDepend.Config's NugetPath. Default is PSDepend\nuget.exe + If we don't find it there, we download to that path + If we do find it there, we add the parent container to $ENV:Path + * Git: Requires git in your path, or in the file path specified in PSDepend\PSDepend.Config's GitPath + * Npm: Requires npm in your path + + Example use (*.PSDepend.ps1) + ============================ + + I want to install dependencies from C:\projectx\projectx.depend.psd1 + + C:\projectx\projectx.depend.psd1 looks like this: + + @{ + psdeploy = @{ + Version = '0.1.8' + Target = 'C:\ProjectX' + DependencyType = 'PSGalleryModule' + } + buildhelpers = @{ + Target = 'CurrentUser' + } + pester = 'latest' + psake = '4.6.0' + } + + # Install dependencies + cd C:\projectx + Invoke-PSDepend + + # Alternatively + Invoke-PSDepend -Path C:\projectx + + See about_PSDepend_Definitions for more information + + Learning about Dependency Scripts + ================================ + PSDepend Dependency scripts each treat Dependency details (e.g. Name, Source, Version) + differently. In some cases, they may have their own Parameters. For example, + a PSGalleryModule has a Repository Parameter. + + It is up to authors of Dependency scripts to expose help for this information. + + A few tools can assist in finding more information: + + # List available PSDepend Types, and their DependencyScripts + Get-PSDependType + + # Show comment-based help for the PSGalleryModule DependencyType: + Get-PSDependType -DependencyType PSGalleryModule -ShowHelp + + By convention, the comment-based help DESCRIPTION field should indicate + how Dependency details are handled. For example, here's PSGalleryModule: + + Relevant Dependency metadata: + Name: The name for this module + Version: Used to identify existing installs meeting this criteria, and as RequiredVersion for installation. Defaults to 'latest' + Target: Used as 'Scope' for Install-Module. If this is a path, we use Save-Module with this path. Defaults to 'AllUsers' + + Lastly, DependencyType specific parameters should be included as parameters + for Dependency scripts. For example, we can see the Repository parameter for PSGalleryModule: + + -Repository + PSRepository to download from. Defaults to PSGallery + + Required? false + Position? 2 + Default value PSGallery + + Extending PSDepend + ================== + PSDepend is somewhat extensible. To add a new dependency type: + + Update PSDependMap.psd1 in the PSDepend root. + + The dependency type name is the root node. + The script node defines what script to run for these dependency types + The description is... not really used. But feel free to write one! + + For example, I might add support for PackageManagement, and include a long and shorthand way to name these: + + Package: + Script: Package.ps1 + Description: Install packages via PackageManagement module + + PackageManagement: + Script: Package.ps1 + Description: Install packages via PackageManagement module + + Create the associated script in PSDepend\PSDependScripts + + For example, I would create \\Path\To\PSDepend\PSDependScripts\Package.ps1 + + Include a 'Dependency' parameter. + + See \\Path\To\PSDepend\PSDependScripts\PSGalleryModule.ps1 for an example + + Here's how I implement this: + param( + [PSTypeName('PSDepend.Dependency')] + [psobject[]]$Dependency + ... + + Include a 'PSDependAction' parameter. + + Use ValidateSet to specify whether your type supports Test, Install, Import, or other actions. + + Include details on how you use Dependency properties in the comment-based help DESCRIPTION field. + + See \\Path\To\PSDepend\PSDependScripts\PSGalleryModule.ps1 DESCRIPTION field for an example + + Include parameters and comment based help for any parameters you read from the Dependency Parameter property. + + See \\Path\To\PSDepend\PSDependScripts\PSGalleryModule.ps1 parameters and help for an example + + Update *.depend.ps1 schema as needed. + + Get-Dependency converts PSD1 files into a number of 'Dependency' objects. + If you need other data included, you can extend the schema and reference the + 'Raw' property on the dependency objects: this contains the raw data from the psd1 + +SEE ALSO + about_PSDepend_Definitions + https://github.com/RamblingCookieMonster/PSDepend \ No newline at end of file diff --git a/.build/modules/PSDepend/0.3.8/en-US/about_PSDepend_Definitions.help.txt b/.build/modules/PSDepend/0.3.8/en-US/about_PSDepend_Definitions.help.txt new file mode 100644 index 0000000..a2ac407 --- /dev/null +++ b/.build/modules/PSDepend/0.3.8/en-US/about_PSDepend_Definitions.help.txt @@ -0,0 +1,151 @@ +TOPIC + about_PSDepend_Definitions + +SHORT DESCRIPTION + PSDepend has several configuration definitions to work with + +LONG DESCRIPTION + PSDepend has several configuration definitions to work with: + + Dependency configurations: *.Depend.psd1 or requirements.psd1 script files + PSDepend configuration: PSDepend.Config file with a few configurations for PSDepend + Dependency type configurations: Map Dependency types to scripts that run them + + Please see about_PSDepend or other general PSDepend help for clarification on terminology + +DETAILED DESCRIPTION + + Dependency Configurations: *.Depend.psd1 or requirements.psd1 + ========================================= + + These are PowerShell data files that tell PSDepend what to install. + + We use the following attributes: + + DependencyFile: File that we read a dependency from + DependencyName: Unique name within a dependency file - this is the key, the rest of the data is stored in this key's value. + DependencyType: The dependency type. Defaults to PSGalleryModule + Name: Name of the thing to install. Optional, use this if DependencyName has a collision + Version: Version to install and check for. The dependency script author is responsible for testing whether this version already exists and installing it if not. + Parameters: Optional parameters if the dependency type's script takes parameters. For example, the PSGalleryModule has an optional 'Repository' parameter. + Source: Optional source. For example, a FileDownload source is a URL + Target: Optional target. For example, a PSGalleryModule can target a path (uses Save-Module), or a scope like AllUsers (uses Install-Module) + AddToPath: Optional flag to specify whether to add the installed dependency to the PATH (or PSModulePath, or comparable setting) + Tags: Optional tags to categorize and filter dependencies + DependsOn: Dependency that this Dependency depends upon. Uses DependencyName for reference. + PreScripts: One or more paths to scripts to process before the dependency is processed + PostScripts: One or more paths to scripts to process after the dependency is processed + Raw: Raw data from the dependency in the psd1 file + + The Source and Target attributes allow the substitution of select variables: + $PWD (or .) refer to the current path + $DependencyFolder or $DependencyPath refer to the parent of the DependencyFile + $ENV:ProgramData, USERPROFILE, APPDATA, and TEMP + Variables need to be in single quotes or the $ needs to be escaped. We replace the raw strings with the values for you. This will not work: Target = "$PWD\dependencies". This will: Target = '$PWD\dependencies' + + A *.depend.ps1 file will have one or more dependency nodes like this: + + @{ + # This means 'install the latest copy of pester if I don't have it already, for all users, via PSGalleryModule' + # We treat the value as a 'Version, if it's not a hashtable + pester = 'latest' + + # Install a specific version of psake for all users via PSGalleryModule + psake = '4.6.0' + + # Install the latest buildhelpers module from PSGalleryModule, for the CurrentUser only + buildhelpers = @{ + target = 'CurrentUser' + } + + # This is a fleshed out dependency that doesn't rely on default values + # Maybe I need multiple copies of PSDeploy, so I give the key a unique name... + psdeploy_0_1_8 = @{ + + # This overrides the name that typically comes from the key + name = psdeploy + + # We want a specific version + version = '0.1.8' + + # We want to install to a specific path + target = 'C:\ProjectX' + + # This is the default, but specified for clarity... + DependencyType = 'PSGalleryModule' + + # Parameters specific to our PSGalleryModule dependency type + parameters = @{ + # We want to install from an internal repository named MyPSGalleryRepository, that I've already registered + Repository = 'MyPSGalleryRepository' + } + + # Tag it. Maybe only install in certain scenarios + tags = 'prod' + + # Add to the PSModulePath + AddToPath = $True + + # Make sure buildhelpers installs first + DependsOn = 'buildhelpers' + + # Run this script after installing the module + PostScripts = "C:\Finalize-ProjectX.ps1" + } + } + + You can specify global defaults (and override them inside a Dependency): + + @{ + PSDependOptions = @{ + Target = 'C:\MyProject' # I want all my modules installed here + Parameters = @{ + Force = $True # I want to use -Force on each dependency + } + } + pester = 'latest' + psake = 'latest' + buildhelpers = 'latest' + psdeploy = @{ + Target = "C:\Exception" # Example overriding the global target + } + } + + Global defaults can be specified for the following attributes: + Parameters + Source + Target + AddToPath + Tags + DependsOn + PreScripts + PostScripts + + PSDepend configuration: PSDepend.Config + ======================================= + + This file includes a few configurations for PSDepend: + + NugetPath: Path to a nuget.exe, if it's not in your path. If it's not found in either spot, we download to this + GitPath: Path to a git, if it's not in your path. We do not resolve this dependency for you (yet). + + Dependency type map: PSDependMap.psd1 + ===================================== + + This is a file that tells PSDepend what script to use for each DependencyType. By default, it sits in your PSDepend module folder. + + There are two scenarios you would generally work with this: + + - You want to extend PSDepend to add more DependencyTypes + - You want to move the PSDependMap.psd1 to a central location that multiple systems could point to + + There are four attributes to each DependencyType in this file: + + DependencyType Name: The name of this DependencyType + Script: The name of the script to process these DependencyTypes + This looks in the PSDepend module path, under PSDependScripts + You can theoretically specify an absolute path + Description: Description for this DependencyType. Provided to a user when they run Get-PSDependType. + Supports: Platforms this script supports: windows, core, macos, linux + + See about_PSDepend for more information \ No newline at end of file diff --git a/.build/scripts/Build-PSModule.ps1 b/.build/scripts/Build-PSModule.ps1 index bf51c80..cd28d03 100644 --- a/.build/scripts/Build-PSModule.ps1 +++ b/.build/scripts/Build-PSModule.ps1 @@ -159,13 +159,13 @@ function Build-PSModule { $ConfirmPreference = 'None' $WhatIfPreference = $false - Register-PSRepository -Name BuildOutput -SourceLocation $outputPath -PublishLocation $outputPath -InstallationPolicy Trusted *>$null + Register-PSResourceRepository -Name BuildOutput -Uri $outputPath -Trusted *>$null try { - Publish-Module -Path $outputModulePath -Repository BuildOutput -Force *>$null + Publish-PSResource -Path $outputModulePath -Repository BuildOutput -SkipDependenciesCheck *>$null } finally { - Unregister-PSRepository -Name BuildOutput *>$null + Unregister-PSResourceRepository -Name BuildOutput *>$null } return "$outputPath\$moduleName.$version.nupkg" diff --git a/.build/scripts/Invoke-PSModulePSDepend.ps1 b/.build/scripts/Invoke-PSModulePSDepend.ps1 new file mode 100644 index 0000000..a49fe5c --- /dev/null +++ b/.build/scripts/Invoke-PSModulePSDepend.ps1 @@ -0,0 +1,59 @@ +<# + .SYNOPSIS + Installs module dependencies using PSDepend. +#> +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' + +function Invoke-PSModulePSDepend { + <# + .SYNOPSIS + Installs module dependencies using PSDepend. + #> + [CmdletBinding()] + param() + + $gitRoot = Resolve-Path -Path "$PSScriptRoot\..\.." + $psDependModulePath = "$PSScriptRoot\..\modules\PSDepend" + $psDependManifestPath = "$gitRoot\PSDepend.psd1" + + # Skip if no PSDepend manifest exists + if (-not (Test-Path -Path $psDependManifestPath -PathType Leaf)) { + Write-Verbose "No PSDepend manifest found at '$psDependManifestPath', skipping dependency installation." + return + } + + # Verify PSDepend module exists + if (-not (Test-Path -Path $psDependModulePath)) { + throw "PSDepend module not found at '$psDependModulePath'." + } + + try { + # Import PSDepend from local path + Import-Module -Name $psDependModulePath -Force -ErrorAction Stop -Verbose:$false -WarningAction SilentlyContinue *>$null + + # Install dependencies using PSDepend + Push-Location -Path $gitRoot + + try { + Invoke-PSDepend -Path $psDependManifestPath -Install -Force + } + finally { + Pop-Location + } + } + finally { + # Clean up - remove PSDepend module + Remove-Module -Name PSDepend -ErrorAction SilentlyContinue + } +} + +try { + Invoke-PSModulePSDepend @PSBoundParameters +} +catch { + Write-Host $_ -ForegroundColor Red + exit 1 +} diff --git a/.build/scripts/Setup-RunnerProfile.ps1 b/.build/scripts/Invoke-RunnerProfileSetup.ps1 similarity index 94% rename from .build/scripts/Setup-RunnerProfile.ps1 rename to .build/scripts/Invoke-RunnerProfileSetup.ps1 index 9f9e74e..702cb1a 100644 --- a/.build/scripts/Setup-RunnerProfile.ps1 +++ b/.build/scripts/Invoke-RunnerProfileSetup.ps1 @@ -14,7 +14,7 @@ param( $ErrorActionPreference = 'Stop' -function Setup-RunnerProfile { +function Invoke-RunnerProfileSetup { [CmdletBinding()] <# .SYNOPSIS @@ -45,7 +45,7 @@ function Setup-RunnerProfile { } try { - Setup-RunnerProfile @PSBoundParameters + Invoke-RunnerProfileSetup @PSBoundParameters } catch { Write-Host $_ -ForegroundColor Red diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml index 6fcde80..67fc611 100644 --- a/.github/workflows/pr_tests.yml +++ b/.github/workflows/pr_tests.yml @@ -31,11 +31,15 @@ jobs: - name: Setup Profile shell: pwsh - run: .\.build\scripts\Setup-RunnerProfile.ps1 -Verbose + run: .\.build\scripts\Invoke-RunnerProfileSetup.ps1 -Verbose - name: Setup Container (Custom) shell: pwsh - run: .\.build\custom\Setup-Container.ps1 + run: .\.build\custom\Invoke-ContainerSetup.ps1 + + - name: Install Dependencies + shell: pwsh + run: .\.build\scripts\Invoke-PSModulePSDepend.ps1 - name: Build Module run: .\.build\scripts\Build-PSModule.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index afd775b..01d5f41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,11 +27,15 @@ jobs: - name: Setup Profile shell: pwsh - run: .\.build\scripts\Setup-RunnerProfile.ps1 -Verbose + run: .\.build\scripts\Invoke-RunnerProfileSetup.ps1 -Verbose - name: Setup Container (Custom) shell: pwsh - run: .\.build\custom\Setup-Container.ps1 + run: .\.build\custom\Invoke-ContainerSetup.ps1 + + - name: Install Dependencies + shell: pwsh + run: .\.build\scripts\Invoke-PSModulePSDepend.ps1 - name: Build Module shell: pwsh diff --git a/.github/workflows/update_docs.yml b/.github/workflows/update_docs.yml index 8227f0c..46d947f 100644 --- a/.github/workflows/update_docs.yml +++ b/.github/workflows/update_docs.yml @@ -27,11 +27,15 @@ jobs: - name: Setup Profile shell: pwsh - run: .\.build\scripts\Setup-RunnerProfile.ps1 -Verbose + run: .\.build\scripts\Invoke-RunnerProfileSetup.ps1 -Verbose - name: Setup Container (Custom) shell: pwsh - run: .\.build\custom\Setup-Container.ps1 + run: .\.build\custom\Invoke-ContainerSetup.ps1 + + - name: Install Dependencies + shell: pwsh + run: .\.build\scripts\Invoke-PSModulePSDepend.ps1 - name: Build Module shell: pwsh diff --git a/PSDepend.psd1 b/PSDepend.psd1 new file mode 100644 index 0000000..147a98c --- /dev/null +++ b/PSDepend.psd1 @@ -0,0 +1,114 @@ +@{ + # ───────────────────────────────────────────────────────────────── + # Global options — these apply to every dependency unless overridden + # ───────────────────────────────────────────────────────────────── + # PSDependOptions = @{ + # Target = 'CurrentUser' # 'CurrentUser', 'AllUsers', or a file path + # AddToPath = $true # Add Target to $env:PSModulePath + # DependencyType = 'PSGalleryModule' # Default type if not specified per-entry + # Tags = @('Build', 'Test') # Only process deps matching these tags + # } + + # ───────────────────────────────────────────────────────────────── + # Simple format — module name = version string + # ───────────────────────────────────────────────────────────────── + # 'MyModuleName' = 'latest' # Always pull the newest version + # 'AnotherModule' = '2.4.1' # Pin to an exact version + # 'YetAnotherModule' = '1.*' # Wildcard — latest 1.x release + + # ───────────────────────────────────────────────────────────────── + # Hashtable format — PSGallery module with extra options + # ───────────────────────────────────────────────────────────────── + # 'MyGalleryModule' = @{ + # DependencyType = 'PSGalleryModule' # Explicit (this is also the default) + # Version = '3.0.0' + # Target = 'CurrentUser' + # Tags = @('Build') + # Parameters = @{ + # SkipPublisherCheck = $true + # AllowClobber = $true + # Repository = 'PSGallery' # Use a named repo + # } + # } + + # ───────────────────────────────────────────────────────────────── + # PSGallery NuGet dependency (uses NuGet directly instead of + # Install-Module — handy for build servers without PowerShellGet) + # ───────────────────────────────────────────────────────────────── + # 'MyNuGetModule' = @{ + # DependencyType = 'PSGalleryNuget' + # Version = '1.2.0' + # Target = 'C:\BuildAgent\Modules' + # } + + # ───────────────────────────────────────────────────────────────── + # Git repository dependency + # ───────────────────────────────────────────────────────────────── + # 'MyGitModule' = @{ + # DependencyType = 'Git' + # Version = 'main' # Branch, tag, or commit hash + # Source = 'https://github.com/myorg/MyGitModule.git' + # Target = 'C:\Modules\MyGitModule' + # } + + # ───────────────────────────────────────────────────────────────── + # FileSystem dependency — copy a local folder or file + # ───────────────────────────────────────────────────────────────── + # 'MyLocalModule' = @{ + # DependencyType = 'FileSystem' + # Source = 'C:\Source\MyLocalModule' + # Target = 'C:\Deploy\Modules\MyLocalModule' + # } + + # ───────────────────────────────────────────────────────────────── + # Command dependency — run an arbitrary script/command + # (useful for bootstrapping tools that aren't PS modules) + # ───────────────────────────────────────────────────────────────── + # 'InstallDotNetSdk' = @{ + # DependencyType = 'Command' + # Source = 'dotnet-install.ps1 -Channel 8.0' + # Tags = @('Build') + # } + + # ───────────────────────────────────────────────────────────────── + # Package dependency — uses PackageManagement / OneGet + # ───────────────────────────────────────────────────────────────── + # 'MyPackage' = @{ + # DependencyType = 'Package' + # Source = 'nuget' # Provider name + # Version = '4.1.0' + # Target = 'C:\Packages' + # Parameters = @{ + # ForceBootstrap = $true + # } + # } + + # ───────────────────────────────────────────────────────────────── + # Multiple dependencies with shared tags — useful for grouping + # e.g. invoke only "Test" deps: Invoke-PSDepend -Tags 'Test' + # ───────────────────────────────────────────────────────────────── + # 'TestFrameworkModule' = @{ + # Version = '5.4.0' + # Tags = @('Test') + # } + # 'MockingModule' = @{ + # Version = '1.0.0' + # Tags = @('Test') + # } + # 'BuildHelperModule' = @{ + # Version = 'latest' + # Tags = @('Build') + # } + + # ───────────────────────────────────────────────────────────────── + # Credential-protected dependency (private gallery / feed) + # ───────────────────────────────────────────────────────────────── + # 'MyPrivateModule' = @{ + # DependencyType = 'PSGalleryModule' + # Version = '2.0.0' + # Parameters = @{ + # Repository = 'MyPrivateFeed' + # Credential = $MyCredentialVariable # Pass in at runtime + # } + # } +} diff --git a/README.md b/README.md index 7a691a5..4df0944 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ These are intended for things like compiling native or foreign-language code, st ### Container Setup -`.build/scripts/Setup-Container.ps1` +`.build/scripts/Invoke-ContainerSetup.ps1` Runs during environment or container initialisation. Use this for installing SDKs, runtimes, configuring environment variables, authenticating with external feeds, or validating toolchain versions before any build steps execute.