InstallModule.ps1
|
[CmdletBinding(SupportsShouldProcess)] param( [ValidateSet('CurrentUser', 'AllUsers', 'Custom')] [string] $Scope = 'CurrentUser', [string] $DestinationPath, [string] $ModuleName, [switch] $Mirror ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Get-ModuleNameFromRepo { $manifest = Get-ChildItem -LiteralPath $PSScriptRoot -Filter '*.psd1' -File | Select-Object -First 1 if (-not $manifest) { throw "No module manifest (*.psd1) found in '$PSScriptRoot'." } # Avoid parsing the manifest here (it may declare PowerShellVersion=7.x and fail under Windows PowerShell 5.1). return $manifest.BaseName } function Get-PSModuleRoot { param( [Parameter(Mandatory)] [ValidateSet('CurrentUser', 'AllUsers')] [string] $Scope ) $paths = $env:PSModulePath -split [System.IO.Path]::PathSeparator | Where-Object { $_ } $userProfile = [System.Environment]::GetFolderPath('UserProfile') $psHomePath = $PSHOME if ($Scope -eq 'CurrentUser') { $resolved = ($paths | Where-Object { $_ -like "$userProfile*" -and $_ -notlike "$psHomePath*" } | Select-Object -First 1) if ($resolved) { return $resolved } $docs = [Environment]::GetFolderPath('MyDocuments') if ($docs) { # Windows PowerShell typically uses WindowsPowerShell; PowerShell 7 uses PowerShell. $candidates = @( (Join-Path $docs 'PowerShell\Modules') (Join-Path $docs 'WindowsPowerShell\Modules') ) return ($candidates | Select-Object -First 1) } return $null } $resolved = ($paths | Where-Object { $_ -notlike "$userProfile*" -and $_ -notlike "$psHomePath*" } | Select-Object -First 1) if ($resolved) { return $resolved } $programFiles = $env:ProgramFiles if ($programFiles) { $candidates = @( (Join-Path $programFiles 'PowerShell\Modules') (Join-Path $programFiles 'WindowsPowerShell\Modules') ) return ($candidates | Select-Object -First 1) } return $null } if (-not $ModuleName) { $ModuleName = Get-ModuleNameFromRepo } $repoRoot = $PSScriptRoot if ($Scope -eq 'Custom') { if (-not $DestinationPath) { throw "-DestinationPath is required when -Scope Custom." } $moduleInstallPath = Join-Path -Path $DestinationPath -ChildPath $ModuleName } else { $moduleRoot = Get-PSModuleRoot -Scope $Scope if (-not $moduleRoot) { throw "Unable to resolve a PSModulePath entry for scope '$Scope'." } $moduleInstallPath = Join-Path -Path $moduleRoot -ChildPath $ModuleName } $excludeDirs = @( '.git' '.github' '.vscode' 'CI' '__tests__' 'data' 'docs' 'mdHelp' 'spikes' ) $excludeFiles = @( '.gitattributes' '.gitignore' 'azure-pipelines.yml' 'appveyor.yml' 'InstallModule.ps1' 'PublishToGallery.ps1' 'filelist.txt' ) if ($PSCmdlet.ShouldProcess($moduleInstallPath, "Install module '$ModuleName' from '$repoRoot'")) { New-Item -ItemType Directory -Path $moduleInstallPath -Force | Out-Null Push-Location $repoRoot try { $roboArgs = @( $repoRoot $moduleInstallPath '/E' # copy subdirs incl empty '/NFL' # no file list '/NDL' # no dir list '/NJH' # no job header '/NJS' # no job summary '/NP' # no progress '/R:2' '/W:1' ) if ($Mirror) { $roboArgs += '/MIR' } $roboArgs += '/XD' $roboArgs += $excludeDirs $roboArgs += '/XF' $roboArgs += $excludeFiles & robocopy @roboArgs | Out-Null if ($LASTEXITCODE -ge 8) { throw "Robocopy failed with exit code $LASTEXITCODE." } } finally { Pop-Location } Get-ChildItem -LiteralPath $moduleInstallPath -Force | Out-String | Write-Verbose } |