diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dbe259..89412f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ * [PSProfile - ChangeLog](#psprofile---changelog) + * [0.6.0 - 2019-11-02](#060---2019-11-02) * [0.5.0 - 2019-10-08](#050---2019-10-08) * [0.4.1 - 2019-10-08](#041---2019-10-08) * [0.4.0 - 2019-09-22](#040---2019-09-22) @@ -19,6 +20,25 @@ # PSProfile - ChangeLog +## 0.6.0 - 2019-11-02 + +* [Issue #21](https://github.com/scrthq/PSProfile/issues/21) - _Thank you [@corbob](https://github.com/corbob)!_ + * Fixed: Project folder discovery logic to ensure that project folders with the same name are added to the dictionary without conflict. +* [Issue #22](https://github.com/scrthq/PSProfile/issues/22) - _Thank you [@corbob](https://github.com/corbob)!_ + * Added: `WithInsiders` switch parameter to `Open-Code`, `Edit-PSProfilePrompt`, and `Edit-PSProfileInitScript`. +* [Issue #23](https://github.com/scrthq/PSProfile/issues/23) + * Fixed: `$PSProfile` variable should exist regardless of when you import the module (removed conditional variable setting from PSM1). +* [Issue #26](https://github.com/scrthq/PSProfile/issues/26) + * Fixed: `$PSProfile._globalize()` internal method will now update `$PSDefaultParameterValue` to `$global":PSDefaultParameterValue` on InitScripts / ExternalScripts / etc so `$PSDefaultParameterValue` persists in the main session as intended. +* [Issue #27](https://github.com/scrthq/PSProfile/issues/27) + * Removed: `Set-Prompt` alias to prevent conflict with `oh-my-posh` module. +* [Issue #29](https://github.com/scrthq/PSProfile/issues/29) + * Fixed: Secrets now persist across refreshes and sessions as intended. Details: + * Removed `PSProfileVault` class, replaced with pure hashtable. + * Updated the Secrets management functions to work directly against the Vault hashtable. +* [Issue #31](https://github.com/scrthq/PSProfile/issues/31) + * Fixed: Same as [Issue #29](https://github.com/scrthq/PSProfile/issues/29). + ## 0.5.0 - 2019-10-08 * Miscellaneous diff --git a/PSProfile/PSProfile.psd1 b/PSProfile/PSProfile.psd1 index 6a4927a..a52c5a0 100644 --- a/PSProfile/PSProfile.psd1 +++ b/PSProfile/PSProfile.psd1 @@ -12,7 +12,7 @@ RootModule = 'PSProfile.psm1' # Version number of this module. - ModuleVersion = '0.5.0' + ModuleVersion = '0.6.0' # Supported PSEditions CompatiblePSEditions = @('Desktop','Core') diff --git a/PSProfile/PSProfile.psm1 b/PSProfile/PSProfile.psm1 index 8fefb45..217fcbf 100644 --- a/PSProfile/PSProfile.psm1 +++ b/PSProfile/PSProfile.psm1 @@ -1,24 +1,20 @@ -# If we're in an interactive shell, load the profile. -if ([Environment]::UserInteractive -or ($null -eq [Environment]::UserInteractive -and $null -eq ([Environment]::GetCommandLineArgs() | Where-Object { $_ -like '-NonI*' }))) { - $global:OriginalPrompt = - $global:PSProfile = [PSProfile]::new() - $global:PSProfile.Load() - Export-ModuleMember -Variable PSProfile - $global:PSProfileConfigurationWatcher = [System.IO.FileSystemWatcher]::new($(Split-Path $global:PSProfile.Settings.ConfigurationPath -Parent),'Configuration.psd1') - $job = Register-ObjectEvent -InputObject $global:PSProfileConfigurationWatcher -EventName Changed -Action { - [PSProfile]$conf = Import-Configuration -Name PSProfile -CompanyName 'SCRT HQ' -Verbose:$false - $conf._internal = $global:PSProfile._internal - $global:PSProfile = $conf +$global:PSProfile = [PSProfile]::new() +$global:PSProfile.Load() +Export-ModuleMember -Variable PSProfile +$global:PSProfileConfigurationWatcher = [System.IO.FileSystemWatcher]::new($(Split-Path $global:PSProfile.Settings.ConfigurationPath -Parent),'Configuration.psd1') +$job = Register-ObjectEvent -InputObject $global:PSProfileConfigurationWatcher -EventName Changed -Action { + [PSProfile]$conf = Import-Configuration -Name PSProfile -CompanyName 'SCRT HQ' -Verbose:$false + $conf._internal = $global:PSProfile._internal + $global:PSProfile = $conf +} +$PSProfile_OnRemoveScript = { + try { + $global:PSProfileConfigurationWatcher.Dispose() } - $PSProfile_OnRemoveScript = { - try { - $global:PSProfileConfigurationWatcher.Dispose() - } - finally { - Remove-Variable PSProfile -Scope Global -Force - Remove-Variable PSProfileConfigurationWatcher -Scope Global -Force - } + finally { + Remove-Variable PSProfile -Scope Global -Force + Remove-Variable PSProfileConfigurationWatcher -Scope Global -Force } - $ExecutionContext.SessionState.Module.OnRemove += $PSProfile_OnRemoveScript - Register-EngineEvent -SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) -Action $PSProfile_OnRemoveScript } +$ExecutionContext.SessionState.Module.OnRemove += $PSProfile_OnRemoveScript +Register-EngineEvent -SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) -Action $PSProfile_OnRemoveScript diff --git a/PSProfile/Public/Init Scripts/Edit-PSProfileInitScript.ps1 b/PSProfile/Public/Init Scripts/Edit-PSProfileInitScript.ps1 index ec48a02..70f27b6 100644 --- a/PSProfile/Public/Init Scripts/Edit-PSProfileInitScript.ps1 +++ b/PSProfile/Public/Init Scripts/Edit-PSProfileInitScript.ps1 @@ -9,6 +9,9 @@ function Edit-PSProfileInitScript { .PARAMETER Name The name of the InitScript to edit from $PSProfile.InitScripts. + .PARAMETER WithInsiders + If $true, looks for VS Code Insiders to load. If $true and code-insiders cannot be found, opens the file using VS Code stable. If $false, opens the file using VS Code stable. Defaults to $false. + .PARAMETER Save If $true, saves the updated PSProfile after updating. @@ -22,16 +25,26 @@ function Edit-PSProfileInitScript { [Parameter(Mandatory,Position = 0,ValueFromPipeline,ValueFromPipelineByPropertyName)] [String[]] $Name, + [Alias('wi')] + [Alias('insiders')] + [Switch] + $WithInsiders, [Parameter()] [Switch] $Save ) Process { + $codeCommand = @('code','code-insiders') + if ($WithInsiders) { + $codeCommand = @('code-insiders','code') + } + $code = (Get-Command $codeCommand -All | Where-Object { $_.CommandType -notin @('Function','Alias') })[0].Source foreach ($initScript in $Name) { if ($Global:PSProfile.InitScripts.Contains($initScript)) { $in = @{ StdIn = $Global:PSProfile.InitScripts[$initScript].ScriptBlock TmpFile = [System.IO.Path]::Combine(([System.IO.Path]::GetTempPath()),"InitScript-$($initScript)-$(-join ((97..(97+25)|%{[char]$_}) | Get-Random -Count 3)).ps1") + Editor = $code } $handler = { Param( @@ -39,9 +52,8 @@ function Edit-PSProfileInitScript { $in ) try { - $code = (Get-Command code -All | Where-Object { $_.CommandType -notin @('Function','Alias') })[0].Source $in.StdIn | Set-Content $in.TmpFile -Force - & $code $in.TmpFile --wait + & $in.Editor $in.TmpFile --wait } catch { throw @@ -67,7 +79,7 @@ function Edit-PSProfileInitScript { Register-ArgumentCompleter -CommandName Edit-PSProfileInitScript -ParameterName Name -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) - $Global:PSProfile.InitScripts.Keys | Where-Object {$_ -like "$wordToComplete*"} | ForEach-Object { + $Global:PSProfile.InitScripts.Keys | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } diff --git a/PSProfile/Public/Prompts/Edit-PSProfilePrompt.ps1 b/PSProfile/Public/Prompts/Edit-PSProfilePrompt.ps1 index 6b0bd37..a3c61fa 100644 --- a/PSProfile/Public/Prompts/Edit-PSProfilePrompt.ps1 +++ b/PSProfile/Public/Prompts/Edit-PSProfilePrompt.ps1 @@ -6,6 +6,9 @@ function Edit-PSProfilePrompt { .DESCRIPTION Enables editing the prompt from the desired editor. Once temporary file is saved, the prompt is updated in $PSProfile.Prompts. + .PARAMETER WithInsiders + If $true, looks for VS Code Insiders to load. If $true and code-insiders cannot be found, opens the file using VS Code stable. If $false, opens the file using VS Code stable. Defaults to $false. + .PARAMETER Save If $true, saves prompt back to your PSProfile after updating. @@ -16,14 +19,24 @@ function Edit-PSProfilePrompt { #> [CmdletBinding()] Param( + [Alias('wi')] + [Alias('insiders')] + [Switch] + $WithInsiders, [Parameter()] [Switch] $Save ) Process { + $codeCommand = @('code','code-insiders') + if ($WithInsiders) { + $codeCommand = @('code-insiders','code') + } + $code = (Get-Command $codeCommand -All | Where-Object { $_.CommandType -notin @('Function','Alias') })[0].Source $in = @{ StdIn = Get-PSProfilePrompt -Global TmpFile = [System.IO.Path]::Combine(([System.IO.Path]::GetTempPath()),"ps-prompt-$(-join ((97..(97+25)|%{[char]$_}) | Get-Random -Count 3)).ps1") + Editor = $code } $handler = { Param( @@ -31,9 +44,8 @@ function Edit-PSProfilePrompt { $in ) try { - $code = (Get-Command code -All | Where-Object { $_.CommandType -notin @('Function','Alias') })[0].Source $in.StdIn | Set-Content $in.TmpFile -Force - & $code $in.TmpFile --wait + & $in.Editor $in.TmpFile --wait } catch { throw diff --git a/PSProfile/Public/Secrets/Add-PSProfileSecret.ps1 b/PSProfile/Public/Secrets/Add-PSProfileSecret.ps1 index c466a68..9aef68d 100644 --- a/PSProfile/Public/Secrets/Add-PSProfileSecret.ps1 +++ b/PSProfile/Public/Secrets/Add-PSProfileSecret.ps1 @@ -52,26 +52,29 @@ function Add-PSProfileSecret { Process { switch ($PSCmdlet.ParameterSetName) { PSCredential { - if ($Force -or $null -eq $Global:PSProfile.Vault.GetSecret($Credential.UserName)) { + if ($Force -or -not $Global:PSProfile.Vault._secrets.ContainsKey($Credential.UserName)) { Write-Verbose "Adding PSCredential for user '$($Credential.UserName)' to `$PSProfile.Vault" - $Global:PSProfile.Vault.SetSecret($Credential) + $Global:PSProfile.Vault._secrets[$Credential.UserName] = $Credential + if ($Save) { + Save-PSProfile + } } - elseif (-not $Force -and $null -ne $Global:PSProfile.Vault.GetSecret($Credential.UserName)) { + elseif (-not $Force -and $Global:PSProfile.Vault._secrets.ContainsKey($Credential.UserName)) { Write-Error "A secret with the name '$($Credential.UserName)' already exists! Include -Force to overwrite it." } } SecureString { - if ($Force -or $null -eq $Global:PSProfile.Vault.GetSecret($Name)) { + if ($Force -or -not $Global:PSProfile.Vault._secrets.ContainsKey($Name)) { Write-Verbose "Adding SecureString secret with name '$Name' to `$PSProfile.Vault" - $Global:PSProfile.Vault.SetSecret($Name,$SecureString) + $Global:PSProfile.Vault._secrets[$Name] = $SecureString + if ($Save) { + Save-PSProfile + } } - elseif (-not $Force -and $null -ne $Global:PSProfile.Vault.GetSecret($Name)) { + elseif (-not $Force -and $Global:PSProfile.Vault._secrets.ContainsKey($Name)) { Write-Error "A secret with the name '$Name' already exists! Include -Force to overwrite it." } } } - if ($Save) { - Save-PSProfile - } } } diff --git a/PSProfile/Public/Secrets/Get-MyCreds.ps1 b/PSProfile/Public/Secrets/Get-MyCreds.ps1 index 6314c35..c6e16a2 100644 --- a/PSProfile/Public/Secrets/Get-MyCreds.ps1 +++ b/PSProfile/Public/Secrets/Get-MyCreds.ps1 @@ -46,7 +46,7 @@ function Get-MyCreds { Process { if ($Item) { Write-Verbose "Checking Credential Vault for user '$Item'" - if ($creds = $global:PSProfile.Vault.GetSecret($Item)) { + if ($creds = $global:PSProfile.Vault._secrets[$Item]) { Write-Verbose "Found item in CredStore" if (!$env:USERDOMAIN) { $env:USERDOMAIN = [System.Environment]::MachineName @@ -59,7 +59,7 @@ function Get-MyCreds { else { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( - ([System.Management.Automation.ItemNotFoundException]"Could not find secret item '$Item' in the PSProfileVault"), + ([System.Management.Automation.ItemNotFoundException]"Could not find secret '$Item' in `$PSProfile.Vault"), 'PSProfile.Vault.SecretNotFound', [System.Management.Automation.ErrorCategory]::InvalidArgument, $global:PSProfile diff --git a/PSProfile/Public/Secrets/Get-PSProfileSecret.ps1 b/PSProfile/Public/Secrets/Get-PSProfileSecret.ps1 index 5b3193f..5b1d448 100644 --- a/PSProfile/Public/Secrets/Get-PSProfileSecret.ps1 +++ b/PSProfile/Public/Secrets/Get-PSProfileSecret.ps1 @@ -7,7 +7,7 @@ function Get-PSProfileSecret { Gets a Secret from the $PSProfile.Vault. .PARAMETER Name - The name of the Secret you would like to retrieve from the Vault. + The name of the Secret you would like to retrieve from the Vault. If excluded, returns the entire Vault contents. .PARAMETER AsPlainText If $true and Confirm:$true, returns the decrypted password if the secret is a PSCredential object or the plain-text string if a SecureString. Requires confirmation. @@ -22,7 +22,7 @@ function Get-PSProfileSecret { #> [CmdletBinding(SupportsShouldProcess,ConfirmImpact = "High")] Param( - [parameter(Mandatory,Position = 0)] + [parameter(Position = 0)] [String] $Name, [parameter()] @@ -33,21 +33,28 @@ function Get-PSProfileSecret { $Force ) Process { - Write-Verbose "Getting Secret '$Name' from `$PSProfile.Vault" - $sec = $global:PSProfile.Vault.GetSecret($Name) - if ($AsPlainText -and ($Force -or $PSCmdlet.ShouldProcess("Return plain-text value for Secret '$Name'"))) { - if ($sec -is [pscredential]) { - [PSCustomObject]@{ - UserName = $sec.UserName - Password = $sec.GetNetworkCredential().Password + if ($Name) { + Write-Verbose "Getting Secret '$Name' from `$PSProfile.Vault" + if ($sec = $global:PSProfile.Vault._secrets[$Name]) { + if ($AsPlainText -and ($Force -or $PSCmdlet.ShouldProcess("Return plain-text value for Secret '$Name'"))) { + if ($sec -is [pscredential]) { + [PSCustomObject]@{ + UserName = $sec.UserName + Password = $sec.GetNetworkCredential().Password + } + } + else { + Get-DecryptedValue $sec + } + } + else { + $sec } - } - else { - Get-DecryptedValue $sec } } else { - $sec + Write-Verbose "Getting all Secrets" + $global:PSProfile.Vault._secrets } } } diff --git a/PSProfile/Public/Secrets/Remove-PSProfileSecret.ps1 b/PSProfile/Public/Secrets/Remove-PSProfileSecret.ps1 index 85c2c31..da0b34c 100644 --- a/PSProfile/Public/Secrets/Remove-PSProfileSecret.ps1 +++ b/PSProfile/Public/Secrets/Remove-PSProfileSecret.ps1 @@ -31,7 +31,7 @@ function Remove-PSProfileSecret { if ($PSCmdlet.ShouldProcess("Removing '$Name' from `$PSProfile.Vault")) { if ($Global:PSProfile.Vault._secrets.ContainsKey($Name)) { Write-Verbose "Removing '$Name' from `$PSProfile.Vault" - $Global:PSProfile.Vault.RemoveSecret($Name) + $Global:PSProfile.Vault._secrets.Remove($Name) | Out-Null } if ($Save) { Save-PSProfile