diff --git a/Setup-Profile.ps1 b/Setup-ScriptEnv.ps1 similarity index 84% rename from Setup-Profile.ps1 rename to Setup-ScriptEnv.ps1 index 1f2e557..126f314 100644 --- a/Setup-Profile.ps1 +++ b/Setup-ScriptEnv.ps1 @@ -1,4 +1,6 @@ [CmdletBinding(SupportsShouldProcess)]param( + [switch]$WriteInitScript, + [string]$CustomInitScriptPath, [switch]$sudo, [switch]$RemoveOnly, [switch]$ShowSkipped, @@ -6,20 +8,10 @@ [switch]$NoGitAction, [string]$GitURL = 'https://code.lksz.me/lksz/PowerShell_Scripts', [Parameter(DontShow)] - [string]$My_PSScriptRoot = $MyPSScriptRoot + [string]$My_PSScriptRoot = $MyPSScriptRoot, + [switch]$NoReloadScripts ) -function _Setup { -[CmdletBinding(SupportsShouldProcess)]param( - [switch]$sudo, - [switch]$RemoveOnly, - [switch]$ShowSkipped, - [switch]$Force, - [switch]$NoGitAction, - [string]$GitURL = 'https://code.lksz.me/lksz/PowerShell_Scripts', - [Parameter(DontShow)] - [string]$My_PSScriptRoot = $MyPSScriptRoot) - ####################################################################### ## ProfileCode function (source) will contain the code to be written @@ -36,193 +28,12 @@ function ProfileCode_post_common { # This loads the personal profile section from the $MyPSScriptRoot/profile.d directory Get-Command Reload-MyScripts -ErrorAction SilentlyContinue | ForEach-Object { . $_.Name } } -####################################################################### -## _Setup Logic starts here -####################################################################### - - . ProfileCode_common - if( -not $MyPSScriptRoot ) { throw '$MyPSScriptRoot does NOT exist!' } - - if ( -not $NoGitAction ) { - if ( -not [bool]$(Get-Command git -ErrorAction SilentlyContinue) ) { - throw "No git command found, you may either omit run with the -NoGitAction switch or install git and try again." - } - Push-Location $MyPSScriptRoot - if( Test-Path $(Join-Path $MyPSScriptRoot '.git') ) { - git pull - } elseif ( $PSCmdlet.ShouldProcess("Pull git repo from $GitURL into $MyPSScriptRoot ?") ) { - $local:tmpGitDir = New-TemporaryFile - Remove-Item $tmpGitDir - New-Item -Type Directory $tmpGitDir.FullName | Out-Null - Write-Verbose "Cloning git repo [$GitURL] into temporary location ('$($tmpGitDir.FullName)')..." - $local:GitOutput = "$(& git clone $GitURL $tmpGitDir.FullName --no-checkout)" - Write-Verbose $GitOutput - Write-Verbose "Moving git repo from temp location to $MyPSScriptRoot . . ." - Copy-Item -Recurse (Join-Path $tmpGitDir.FullName .git) ./.git - Remove-Item $tmpGitDir.FullName - Write-Verbose "Checking out repo..." - $GitOutput = "$(& git checkout --force)" - Write-Verbose $GitOutput - } - Pop-Location - Write-Verbose "Git operation done." - } - - $local:ProfileSignature = [PSCustomObject]([ordered]@{ - Begin = '#### SZ Auto Profile Setup - BEGIN ####' - End = '#### SZ Auto Profile Setup - END ####' - Removed = '#### SZ Auto Profile Setup - ----- ####' - Match = '#### SZ Auto Profile Setup' - }) - - function New-ProfileSetupStatus { - param($Name, $Path, $Status, $Exist) - return [PSCustomObject]([ordered]@{ - Name = $Name - Path = $Path - Status = $Status - Exist = $Exist - }) - } - - $local:_profiles = $PROFILE | fl * -Force | Out-String -Stream | ForEach-Object { - $local:p = $($_ -split ' : [/A-Z\\]'); - if ( $p[0] -match 'User' ) { - $p[0].Trim() - } - } - $local:_profile = $profile.CurrentUserAllHosts; - $local:randomSeed = "####$(Get-Random)####"; - - if ( $RemoveOnly -and $ConfirmPreference -eq 'High' -and (-not $PSBoundParameters -or -not $PSBoundParameters.ContainsKey('Confirm')) ) { - $script:ConfirmPreference = 'Low' - } - - - if ( $sudo -or $(Test-IsAdmin) ) { $_profile = $profile.AllUsersAllHosts } - $_profiles | Foreach-Object { - $local:p = Invoke-Expression "`$profile.$_" - $local:profileContent = $randomSeed; - $local:exists = Test-Path $p - $local:userFile = $($_ -match 'CurrentUser') - $local:cleanupValue = '' - if ( $exists ) { - if ( (Get-Content -Path $p | Out-String ) -match $ProfileSignature.Match ) { - Write-Verbose "Found code in [$_]'$p', preparing..." - $local:gate = $true; - $profileContent = Get-Content $p | ForEach-Object { - if ( -not $gate ) { - if ( $_ -match $ProfileSignature.End ) { - $gate = $true; - } - } - else { - if ( $_ -match $ProfileSignature.Match ) { - if ( $_ -match $ProfileSignature.Begin ) { - $gate = $false; - } - $cleanupValue = $ProfileSignature.Removed - return $randomSeed; - } - return $_ - } - } - } else { - $profileContent += "`n`n$(Get-Content $p -Raw)" - } - } - - if( -not( $profileContent -match $randomSeed ) ) { $profileContent = "$randomSeed`n$profileContent" } - - $local:insertProfileCode = $false; - if ( ($p -eq $_profile) -and -not $RemoveOnly ) { - $insertProfileCode = $true - Write-Verbose "Inserting ProfileCode into $($_)..." - $cleanupValue = $((@( - '', - $ProfileSignature.Begin, - '', - $(Export-FunctionSource -NoHeader ProfileCode_pre_common), - '', - '', - $(Export-FunctionSource -NoHeader ProfileCode_common), - '', - '', - $(Export-FunctionSource -NoHeader ProfileCode_post_common), - '', - $ProfileSignature.End - ) -join "`n" ).Trim().Replace(('$' + '_'), ('$$' + '_'))) - } - - $profileContent = $($profileContent -replace $randomSeed, $cleanupValue).Trim() - - $local:shouldSudo = $sudo -and -not $userFile - $local:status = 'Skipped' - if ( $VerbosePreference -eq 'Continue' ) { $ShowSkipped = $true } - # Test if new profile Content will be empty - if ( -not ([string]::IsNullOrWhiteSpace($profileContent)) ) { - $status = "Need $(if($insertProfileCode){if($exists){'Setup'}else{'Init'}}else{'Cleanup'})" - if ( $sudo -or $userFile ) { - Write-Verbose "Writing content to $($_)..." - $local:tmpOutput = New-TemporaryFile -WhatIf:$false -Confirm:$false - $profileContent | Out-File $tmpOutput.FullName -WhatIf:$false -Confirm:$false - if ( $shouldSudo -and $( $PSVersionTable.Platform -eq 'Unix' ) ) { - # Make sure the file has the right permissions on a Unix machine - Invoke-ExpressionEx -sudo:$shouldSudo chmod 'a+r' $tmpOutput.FullName - } - try { - $local:errMsg = $(Invoke-ExpressionEx -Confirm:$false -sudo:$shouldSudo @" - if( -not (Test-Path $(Split-Path -Parent $p)) ) { New-Item -Type Directory "$(Split-Path -Parent $p)" -Force }; - `$null = Copy-Item "$($tmpOutput.FullName)" "$p" -Force:`$$Force -ErrorAction Stop; - Remove-Item "$($tmpOutput.FullName)" -Force:`$$Force -ErrorAction SilentlyContinue -"@ 2>&1) - if ( $errMsg ) { - Write-Error "$errMsg" - $status = $status -replace 'Need ', 'Failed ' - } - else { - $status = $status -replace 'Need ', '' - } - } - catch { - Write-Error "Writing $p failed!" - } - } - else { - $status = "Need $status" - Write-Error "Cannot write into [$_]'$p'! Please re-run with sudo." - } - } - elseif ( ($Force -or $RemoveOnly) -and $exists ) { - $status = 'Need Removal' - if ( $sudo -or $userFile ) { - Write-Verbose "Removing [$_]'$p'..." - try { - Invoke-ExpressionEx -sudo:$shouldSudo Remove-Item $p - if ( -not (Test-Path $p) ) { $status = 'Removed' } - } - catch { - Write-Error "Removal of $p failed!" - } - } - else { - Write-Error "Cannot remove [$_]'$p'! Please re-run with sudo." - } - } - if ( $status -ne 'Skipped' -or $ShowSkipped ) { - if ( $shouldSudo -and $status -ne 'Skipped' ) { $status = "$status via sudo" } - New-ProfileSetupStatus -Name $_ -Path $p -Status $status -Exist $(Test-Path $p) - } - } - - Remove-Item function:ProfileCode* -Confirm:$false -} ####################################################################### ## ProfileCode_common (source) containing mandatory code for $PROFILE ####################################################################### -function ProfileCode_common { -function Get-PowerShellPath { +function ProfileCode_common {[CmdletBinding()]param() +function Get-PowerShellPath {[CmdletBinding()]param() Get-Process -PID $PID | ForEach-Object { $_.Path, $_.Parent.Path } | Where-Object { $_ -match $('(powershell|pwsh)' + "`$") } | Select-Object -First 1 } @@ -416,29 +227,249 @@ if ( -not (Test-Path $MyPSModulePath) ) { New-Item -ItemType Directory -Path $MyPSModulePath -Force | Out-Null } -Write-Verbose $(Get-Item $MyPSModulePath | Select-Object -ExpandProperty FullName) +Write-Verbose "`$MyPSModulePath = $(Get-Item $MyPSModulePath | Select-Object -ExpandProperty FullName)" # Establish local/personal Script Root and make sure it's in $env:Path -$local:p = Split-PathEnv $env:PATH -$local:My_PSScriptRoot = Join-Path (Split-Path -Parent $MyPSModulePath) Scripts -Write-Verbose "MyPSScriptRoot = $My_PSScriptRoot" -if ( -not (Test-Path $My_PSScriptRoot) ) { - New-Item -ItemType Directory -Path $My_PSScriptRoot -Force | Out-Null - New-Item -ItemType Directory -Path $(Join-Path $My_PSScriptRoot base) -Force | Out-Null - New-Item -ItemType Directory -Path $(Join-Path $(Join-Path $My_PSScriptRoot base) profile.d) -Force | Out-Null +if( -not $MyPSScriptRoot ) { + if( $My_PSScriptRoot -and -not (Test-Path $My_PSScriptRoot) ) { + throw "`$My_PSScriptRoot is defined ($My_PSScriptRoot) but path does NOT exist! stopping" + } + $local:My_PSScriptRoot = Join-Path (Split-Path -Parent $MyPSModulePath) Scripts + Write-Verbose "My_PSScriptRoot = $My_PSScriptRoot" + if ( -not (Test-Path $My_PSScriptRoot) ) { + New-Item -ItemType Directory -Path $My_PSScriptRoot -Force | Out-Null + New-Item -ItemType Directory -Path $(Join-Path $My_PSScriptRoot base) -Force | Out-Null + New-Item -ItemType Directory -Path $(Join-Path $(Join-Path $My_PSScriptRoot base) profile.d) -Force | Out-Null + } + $global:MyPSScriptRoot = $My_PSScriptRoot } -$global:MyPSScriptRoot = $My_PSScriptRoot -$p = @($p[0], $(Join-Path $My_PSScriptRoot base)) + $($p | Select-Object -Skip 1) -$env:PATH = $p -join $PathEnvDelimiter -} - -$SetupFromWeb = [bool]$(@( $SetupFromWeb, $sfw, $LoadCode ) | Where-Object { $_ }) - -if ( -not $SetupFromWeb ) { - _setup @PSBoundParameters - Get-Item function:_setup | Remove-Item -Confirm:$false - Get-Command Reload-MyScripts -ErrorAction SilentlyContinue | - ForEach-Object { . $_.Name -Confirm:$false } +if( -not (Test-Path $MyPSScriptRoot) ) { + throw "`$MyPSScriptRoot ($My_PSScriptRoot) path does NOT exist! stopping!" } -Remove-Variable SetupFromWeb -ErrorAction SilentlyContinue -Confirm:$false + +$local:p1 = Split-PathEnv $env:PATH | Select-Object -Unique +Write-Verbose "MyPSScriptRoot = $MyPSScriptRoot" +$p2 = @($p1[0], $(Join-Path $MyPSScriptRoot base)) + $($p1 | Select-Object -Skip 1) | Select-Object -Unique +$env:PATH = $p2 -join $PathEnvDelimiter +Write-Verbose "PATH $(if( $p1.Count -eq $p2.Count ) { "modification" } else { "was modified"} )" +Remove-Variable -Name "p1","p2","My_PSScriptRoot" +} + + +####################################################################### +## _Init Logic - writing starts here +####################################################################### +function _Init { +[CmdletBinding(SupportsShouldProcess)]param( + [switch]$WriteInitScript, + [string]$CustomInitScriptPath, + [switch]$sudo, + [switch]$RemoveOnly, + [switch]$ShowSkipped, + [switch]$Force, + [switch]$NoGitAction, + [string]$GitURL = 'https://code.lksz.me/lksz/PowerShell_Scripts', + [Parameter(DontShow)] + [string]$My_PSScriptRoot = $MyPSScriptRoot, + [switch]$NoReloadScripts +) + + if ( -not $NoGitAction ) { + if ( -not [bool]$(Get-Command git -ErrorAction SilentlyContinue) ) { + throw "No git command found, you may either omit run with the -NoGitAction switch or install git and try again." + } + Push-Location $MyPSScriptRoot + if( Test-Path $(Join-Path $MyPSScriptRoot '.git') ) { + git pull + } elseif ( $PSCmdlet.ShouldProcess("Pull git repo from $GitURL into $MyPSScriptRoot ?") ) { + $local:tmpGitDir = New-TemporaryFile + Remove-Item $tmpGitDir + New-Item -Type Directory $tmpGitDir.FullName | Out-Null + Write-Verbose "Cloning git repo [$GitURL] into temporary location ('$($tmpGitDir.FullName)')..." + $local:GitOutput = "$(& git clone $GitURL $tmpGitDir.FullName --no-checkout)" + Write-Verbose $GitOutput + Write-Verbose "Moving git repo from temp location to $MyPSScriptRoot . . ." + Copy-Item -Recurse (Join-Path $tmpGitDir.FullName .git) ./.git + Remove-Item $tmpGitDir.FullName + Write-Verbose "Checking out repo..." + $GitOutput = "$(& git checkout --force)" + Write-Verbose $GitOutput + } + Pop-Location + Write-Verbose "Git operation done." + } + + $local:ProfileSignature = [PSCustomObject]([ordered]@{ + Begin = '#### SZ Auto Profile Setup - BEGIN ####' + End = '#### SZ Auto Profile Setup - END ####' + Removed = '#### SZ Auto Profile Setup - ----- ####' + Match = '#### SZ Auto Profile Setup' + }) + + function New-ProfileSetupStatus { + param($Name, $Path, $Status, $Exist) + return [PSCustomObject]([ordered]@{ + Name = $Name + Path = $Path + Status = $Status + Exist = $Exist + }) + } + + $local:_profiles = @() + $local:_profile = $CustomInitScriptPath + $local:randomSeed = "####$(Get-Random)####"; + + if( $_profile ) { + $_profiles += ,$_profile + } else { + $_profiles = $PROFILE | fl * -Force | Out-String -Stream | ForEach-Object { + $local:p = $($_ -split ' : [/A-Z\\]'); + if ( $p[0] -match 'User' ) { + $p[0].Trim() + } + } + $_profile = $profile.CurrentUserAllHosts; + if ( $sudo -or $(Test-IsAdmin) ) { $_profile = $profile.AllUsersAllHosts } + } + + if ( $RemoveOnly -and $ConfirmPreference -eq 'High' -and (-not $PSBoundParameters -or -not $PSBoundParameters.ContainsKey('Confirm')) ) { + $script:ConfirmPreference = 'Low' + } + + $_profiles | Foreach-Object { + $local:p = Invoke-Expression "`$profile.$_" + if( -not $p ) { $p = $_ } + $local:profileContent = $randomSeed; + $local:exists = Test-Path $p + $local:userFile = $($_ -match 'CurrentUser') + $local:cleanupValue = '' + if ( $exists ) { + if ( (Get-Content -Path $p | Out-String ) -match $ProfileSignature.Match ) { + Write-Verbose "Found code in [$_]'$p', preparing..." + $local:gate = $true; + $profileContent = Get-Content $p | ForEach-Object { + if ( -not $gate ) { + if ( $_ -match $ProfileSignature.End ) { + $gate = $true; + } + } + else { + if ( $_ -match $ProfileSignature.Match ) { + if ( $_ -match $ProfileSignature.Begin ) { + $gate = $false; + } + $cleanupValue = $ProfileSignature.Removed + return $randomSeed; + } + return $_ + } + } + } else { + $profileContent += "`n`n$(Get-Content $p -Raw)" + } + } + + if( -not( $profileContent -match $randomSeed ) ) { $profileContent = "$randomSeed`n$profileContent" } + + $local:insertProfileCode = $false; + if ( ($p -eq $_profile) -and -not $RemoveOnly ) { + $insertProfileCode = $true + Write-Verbose "Inserting ProfileCode into $($_)..." + $cleanupValue = $((@( + '', + $ProfileSignature.Begin, + '', + $(Export-FunctionSource -NoHeader ProfileCode_pre_common), + '', + '', + $(Export-FunctionSource -NoHeader ProfileCode_common), + '', + '', + $(Export-FunctionSource -NoHeader ProfileCode_post_common), + '', + $ProfileSignature.End + ) -join "`n" ).Trim().Replace(('$' + '_'), ('$$' + '_'))) + } + + $profileContent = $($profileContent -replace $randomSeed, $cleanupValue).Trim() + + $local:shouldSudo = $sudo -and -not $userFile + $local:status = 'Skipped' + if ( $VerbosePreference -eq 'Continue' ) { $ShowSkipped = $true } + # Test if new profile Content will be empty + if ( -not ([string]::IsNullOrWhiteSpace($profileContent)) ) { + $status = "Need $(if($insertProfileCode){if($exists){'Setup'}else{'Init'}}else{'Cleanup'})" + if ( $sudo -or $userFile ) { + Write-Verbose "Writing content to $($_)..." + $local:tmpOutput = New-TemporaryFile -WhatIf:$false -Confirm:$false + $profileContent | Out-File $tmpOutput.FullName -WhatIf:$false -Confirm:$false + if ( $shouldSudo -and $( $PSVersionTable.Platform -eq 'Unix' ) ) { + # Make sure the file has the right permissions on a Unix machine + Invoke-ExpressionEx -sudo:$shouldSudo chmod 'a+r' $tmpOutput.FullName + } + try { + $local:errMsg = $(Invoke-ExpressionEx -Confirm:$false -sudo:$shouldSudo @" + if( -not (Test-Path $(Split-Path -Parent $p)) ) { New-Item -Type Directory "$(Split-Path -Parent $p)" -Force }; + `$null = Copy-Item "$($tmpOutput.FullName)" "$p" -Force:`$$Force -ErrorAction Stop; + Remove-Item "$($tmpOutput.FullName)" -Force:`$$Force -ErrorAction SilentlyContinue +"@ 2>&1) + if ( $errMsg ) { + Write-Error "$errMsg" + $status = $status -replace 'Need ', 'Failed ' + } + else { + $status = $status -replace 'Need ', '' + } + } + catch { + Write-Error "Writing $p failed!" + } + } + else { + $status = "Need $status" + Write-Error "Cannot write into [$_]'$p'! Please re-run with sudo." + } + } + elseif ( ($Force -or $RemoveOnly) -and $exists ) { + $status = 'Need Removal' + if ( $sudo -or $userFile ) { + Write-Verbose "Removing [$_]'$p'..." + try { + Invoke-ExpressionEx -sudo:$shouldSudo Remove-Item $p + if ( -not (Test-Path $p) ) { $status = 'Removed' } + } + catch { + Write-Error "Removal of $p failed!" + } + } + else { + Write-Error "Cannot remove [$_]'$p'! Please re-run with sudo." + } + } + if ( $status -ne 'Skipped' -or $ShowSkipped ) { + if ( $shouldSudo -and $status -ne 'Skipped' ) { $status = "$status via sudo" } + New-ProfileSetupStatus -Name $_ -Path $p -Status $status -Exist $(Test-Path $p) + } + } + + Get-Item function:_Init | Remove-Item -Confirm:$false +} + +. ProfileCode_common +if( -not $MyPSScriptRoot ) { throw '$MyPSScriptRoot does NOT exist!' } + +if( $WriteInitScript ) { + _Init @PSBoundParameters +} + +Remove-Item function:ProfileCode* -Confirm:$false + +if( $NoReloadScripts ) { return } + +Get-Command Reload-MyScripts -ErrorAction SilentlyContinue | + ForEach-Object { + Write-Verbose "Reload-MyScripts" + . $_.Name -Confirm:$false + } diff --git a/base/Edit-TextFile.ps1 b/base/Edit-TextFile.ps1 index 5d8b17c..ffdb1dc 100644 --- a/base/Edit-TextFile.ps1 +++ b/base/Edit-TextFile.ps1 @@ -1,6 +1,6 @@ [CmdletBinding(SupportsShouldProcess)]param( [switch]$sudo, - [Parameter(Position = 0, ValueFromPipeline, ValueFromRemainingArguments = $true)] + [Parameter(Position = 0, ValueFromPipelineByPropertyName, ValueFromPipeline, ValueFromRemainingArguments = $true)] [string[]]$Path )