Files
TinyORM/tools/Add-FolderOnPath.ps1
silverqx 5148dbb239 tools used single quotes
[skip ci]
2024-06-15 18:55:23 +02:00

422 lines
14 KiB
PowerShell

#!/usr/bin/env pwsh
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline, ValueFromPipelinebyPropertyName,
HelpMessage = 'Specifies the folder to add on the system path, is pwd by default.')]
[ValidateNotNullOrEmpty()]
[string[]] $Path = (Get-Location).Path,
[Parameter(Position = 1, ValueFromPipelinebyPropertyName,
HelpMessage = 'Specifies the environment variable to which to add a Path, is env:PATH ' +
'by default.')]
[string] $Variable,
[Parameter(HelpMessage = 'Add a Path to the LD_LIBRARY_PATH environment variable (Variable ' +
'parameter has higher priority).')]
[switch] $LibraryPath,
[Parameter(HelpMessage = 'Restore original path values for all changed environment variables.')]
[switch] $RestorePath,
[Parameter(HelpMessage = 'Append to the environment variable (default action is prepend).')]
[switch] $Append,
[Parameter(ValueFromPipelinebyPropertyName,
HelpMessage = 'Specifies that the given path is relative and also adds it as relative.')]
[switch] $Relative
)
begin {
Set-StrictMode -Version 3.0
. $PSScriptRoot\private\Common-Path.ps1
if (-not (Test-Path 'Variable:Global:TinyBackedUpVariables')) {
$Global:TinyBackedUpVariables = @()
}
$Script:Slashes = $null
$Script:IsPathFullyQualified = [IO.Path]::IsPathFullyQualified($Path)
function Get-EnvVariable {
[OutputType([string[]])]
Param()
# Get by passed Variable value
if ($Variable) {
if ($Variable.StartsWith('env:')) {
return $Variable, ($Variable.TrimStart('env:'))
}
return "env:$Variable", $Variable
}
$platform = $PSVersionTable.Platform
# Get by platform defaults
switch ($platform) {
'Win32NT' {
return 'env:Path', 'Path'
}
'Unix' {
if ($LibraryPath) {
return 'env:LD_LIBRARY_PATH', 'LD_LIBRARY_PATH'
}
return 'env:PATH', 'PATH'
}
Default {
throw "$platform platform is not supported."
}
}
}
function Backup-Environment {
[OutputType([void])]
Param()
# Restore mode or a env. variable value already backed up
if ($RestorePath -or $Global:TinyBackedUpVariables.Contains($($Script:EnvVariableRaw))) {
return
}
# Keep track of backed up env. variable names
$Global:TinyBackedUpVariables += $Script:EnvVariableRaw
$globalName = "Tiny_$Script:EnvVariableRaw"
# Backup to global variable
if (Test-Path $Script:EnvVariable) {
Set-Variable -Scope global -Name $globalName `
-Value (Get-Item $Script:EnvVariable).Value
}
# Env. variable to back up is empty, so set global variable to $null
else {
Set-Variable -Scope global -Name $globalName -Value $null
}
}
function Redo-GlobalState {
[OutputType([void])]
Param(
[Parameter(Position = 0, Mandatory,
HelpMessage = 'Specifies a variable name for which to clean the global state.')]
[ValidateNotNullOrEmpty()]
[string] $Variable
)
Remove-Variable -Scope global -Name "Tiny_$Variable" -ErrorAction SilentlyContinue
$Global:TinyBackedUpVariables = $Global:TinyBackedUpVariables.Where({ $_ -ne $Variable })
}
function Restore-Environment {
[OutputType([void])]
Param()
foreach ($variable in $Global:TinyBackedUpVariables) {
Set-Item -Path "env:$variable" `
-Value (Get-Variable -Scope global "Tiny_$variable").Value
Redo-GlobalState -Variable $variable
}
Remove-Variable -Scope global -Name 'TinyBackedUpVariables' -ErrorAction SilentlyContinue
Write-Host 'Restored' -ForegroundColor DarkGreen
}
# Throw if the given path exists and it's not a directory
function Test-PathToAdd {
[OutputType([void])]
Param(
[Parameter(Mandatory,
HelpMessage = "Throw if the given path exists and it's not a directory.")]
[AllowEmptyString()]
[string]
$Path
)
if (-not (Test-Path -Path $Path) -or
(Get-Item -Path $Path).PSIsContainer -eq $true) {
return
}
throw "The given path '$(Resolve-Path -Path $Path)' exists and it's not a directory."
}
# Throw if the Relative parameter was passed and the given Path isn't relative
function Test-RelativePath {
[OutputType([void])]
Param(
[Parameter(Mandatory,
HelpMessage = 'Throw if the given path is not relative and the Relative ' +
'parameter was passed.')]
[string]
$Path
)
if (-not $Relative -or -not $Script:IsPathFullyQualified) {
return
}
throw "The given path '$Path' must be relative if the 'Relative' parameter was passed."
}
# Obtain paths to add and excluded paths
function Get-Paths {
[CmdletBinding(PositionalBinding = $false)]
[OutputType([string[]])]
Param(
[Parameter(Mandatory,
HelpMessage = 'Specifies a value of env. variable, set to the $null value if ' +
"env. variable doesn't exist.")]
[AllowEmptyString()]
[string]
$VariableValue
)
# Check if paths to add are already on the $Script:EnvVariable ($env:$Variable)
$pathsToAdd = @()
$pathsExcluded = @()
if (-not ($VariableValue -eq '')) {
foreach ($pathToAdd in $Path) {
# Throw if the given path exists and it's not a directory
Test-PathToAdd -Path $pathToAdd
# Throw if the Relative parameter was passed and the given Path isn't relative
Test-RelativePath -Path $pathToAdd
$pathToAddNormalized =
$Relative ? (Remove-TrailingSlashes -Path $pathToAdd)
: ($Script:IsPathFullyQualified `
? (Get-FullPath -Path $pathToAdd)
: (Get-FullPath -Path $pathToAdd -BasePath (Get-Location).Path))
# Get the RegEx for a given path that can be used to determine if the given path is
# on the system path ($env:Path or $PATH).
# Also, don't resolve a relative path to absolute here and don't check if is
# on the system path ($env:Path or $PATH) as it depends on CWD!
$pathToMatch = Get-PathToMatch -Path $pathToAddNormalized
if ($VariableValue -notmatch $pathToMatch) {
$pathsToAdd += $pathToAddNormalized
}
else {
$pathsExcluded += $pathToAddNormalized
}
}
}
# When env. variable value is empty then add all Paths
else {
$pathsToAdd = Get-FullPath -Path $Path
}
return $pathsToAdd, $pathsExcluded
}
# Get final paths to add to the env. variable
function Join-PathsToAdd {
[CmdletBinding(PositionalBinding = $false)]
[OutputType([string])]
Param(
[Parameter(Mandatory,
HelpMessage = 'Specifies a value of env. variable, set to the $null value if ' +
"env. variable doesn't exist.")]
[AllowEmptyString()]
[string]
$VariableValue,
[Parameter(Mandatory,
HelpMessage = 'Specifies added paths to the env. variable.')]
[AllowEmptyCollection()]
[string[]]
$PathsToAdd
)
$pathsJoined = $PathsToAdd -join [IO.Path]::PathSeparator
# Env. variable value is empty
if ($VariableValue -eq '') {
$pathsFinal = $pathsJoined
}
# Prepend vs Append
elseif ($Append) {
$pathsFinal = $($VariableValue, $pathsJoined)
}
else {
$pathsFinal = $($pathsJoined, $VariableValue)
}
return $pathsFinal -join [IO.Path]::PathSeparator
}
function Write-Result {
[CmdletBinding(PositionalBinding=$false)]
[OutputType([void])]
Param(
[Parameter(Mandatory,
HelpMessage = 'Specifies a value of env. variable, set to the $null value if ' +
"env. variable doesn't exist.")]
[AllowEmptyString()]
[string]
$VariableValue,
[Parameter(Mandatory, HelpMessage = 'Specifies added paths to the env. variable.')]
[AllowEmptyCollection()]
[string[]]
$PathsToAdd,
[Parameter(Mandatory,
HelpMessage = 'Specifies excluded paths from the env. variable (if already ' +
'contains).')]
[AllowEmptyCollection()]
[string[]]
$PathsExcluded
)
$added = $Append ? 'appended' : 'prepended'
if ($VariableValue -eq '') {
Write-Host "All paths were $added." -ForegroundColor DarkGreen
return
}
if ($PathsToAdd.Length -gt 0) {
$addedCapital = $Append ? 'Appended' : 'Prepended'
$pathsToAddJoined = $PathsToAdd -join ', '
Write-Host "$($addedCapital): " -ForegroundColor DarkGreen -NoNewline
Write-Host $pathsToAddJoined
}
if ($PathsExcluded.Length -gt 0) {
$pathsExcludedJoined = $PathsExcluded -join ', '
Write-Host "Already contains: $pathsExcludedJoined" -ForegroundColor DarkGray
}
}
function Export-Path {
[CmdletBinding(PositionalBinding = $false)]
[OutputType([void])]
Param(
[Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelinebyPropertyName,
HelpMessage = 'Specifies a path to export.')]
[string[]]
$Path
)
begin {
Write-Host
Write-Host "Updating `$$Script:EnvVariable environment variable..." `
-ForegroundColor DarkBlue
Write-Host
}
process {
# Value of env. variable, set to the $null value if env. variable doesn't exist
$variableValue = Get-Item $Script:EnvVariable -ErrorAction SilentlyContinue
| Select-Object -ExpandProperty Value
# Obtain paths to add and excluded paths
$pathsToAdd, $pathsExcluded = Get-Paths -VariableValue $variableValue
# Nothing to add
if ($pathsToAdd.Length -eq 0) {
return
}
# Get final paths to add to the env. variable
$pathsFinal = Join-PathsToAdd -VariableValue $variableValue -PathsToAdd $pathsToAdd
Set-Item -Path $Script:EnvVariable -Value $pathsFinal
}
end {
Write-Result -VariableValue $variableValue -PathsToAdd $pathsToAdd `
-PathsExcluded $pathsExcluded
}
}
if ($RestorePath) {
Restore-Environment
exit 0
}
$Script:EnvVariable, $Script:EnvVariableRaw = Get-EnvVariable
}
process {
try {
Backup-Environment
Export-Path -Path $Path
}
catch {
# Clean global state when something goes wrong
if ($Script:EnvVariableRaw) {
Redo-GlobalState -Variable $($Script:EnvVariableRaw)
}
throw
}
}
<#
.Synopsis
Prepends or appends a path to the defined environment variable.
.Description
`Add-FolderOnPath` script prepends or appends a path to the environment variable defined by the Variable parameter or PATH env. variable by default. A path is prepended by default or you can use the Append parameter to append a path. You can also use the LibraryPath parameter that adds a path to the `LD_LIBRARY_PATH` environment variable.
The RestorePath parameter restores a current environment to its original state. It tracks all the changed environment variables and after the call, all the changed env. variables will be restored at once.
A path will not be added when an environment variable already contains this path. You can add more paths add once and the script also works on Unix systems.
.Parameter Path
Specifies the folder to add on the system path, is pwd by default.
.Parameter Variable
Specifies the environment variable to which to add a Path, is env:PATH by default.
.Parameter LibraryPath
Add a Path to the LD_LIBRARY_PATH environment variable (Variable parameter has higher priority).
.Parameter RestorePath
Restore original path values for all changed environment variables.
.Parameter Append
Append to the environment variable (default action is prepend).
.INPUTS
System.String
You can pipe a string that contains a path to `Add-FolderOnPath` script.
.OUTPUTS
Nothing.
.Example
# Prepends /tmp path to the $env:Path environment variable
Add-FolderOnPath -Path /tmp
.Example
# Prepends /tmp path to the $env:LIBRARY_PATH environment variable
Add-FolderOnPath -Path /tmp -Variable LIBRARY_PATH
.Example
# Appends /tmp path to the $env:Path environment variable
Add-FolderOnPath -Path /tmp -Append
.Example
# Appends /tmp and /usr/local/bin paths to the $env:Path environment variable
Add-FolderOnPath -Path /tmp, /usr/local/bin -Append
# Or
'/tmp', '/usr/local/bin' | Add-FolderOnPath -Append
.Example
# Restores a current environment to its original state
Add-FolderOnPath -RestorePath
#>