diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..09e2a69 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] + FriedrichWeinmann +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7d3751a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,24 @@ +on: + push: + branches: + - master + - main + +jobs: + build: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + - name: Install Prerequisites + run: .\build\psf-prerequisites.ps1 -Bootstrap + shell: powershell + - name: Validate + run: .\build\vsts-validate.ps1 + shell: powershell + - name: Build + run: .\build\psf-build.ps1 -ApiKey $env:APIKEY + shell: powershell + env: + APIKEY: ${{ secrets.ApiKey }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..fd51397 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,15 @@ +on: [pull_request] + +jobs: + validate: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + - name: Install Prerequisites + run: .\build\psf-prerequisites.ps1 -Bootstrap + shell: powershell + - name: Validate + run: .\build\vsts-validate.ps1 + shell: powershell diff --git a/PSUtil/PSUtil.psd1 b/PSUtil/PSUtil.psd1 index 6bfd9eb..c77a737 100644 --- a/PSUtil/PSUtil.psd1 +++ b/PSUtil/PSUtil.psd1 @@ -27,8 +27,8 @@ # Modules that must be imported into the global environment prior to importing # this module RequiredModules = @( - @{ ModuleName = 'PSFramework'; ModuleVersion = '1.6.205' } - @{ ModuleName = 'string'; ModuleVersion = '1.0.0' } + @{ ModuleName = 'PSFramework'; ModuleVersion = '1.12.346' } + @{ ModuleName = 'string'; ModuleVersion = '1.1.5' } ) # Assemblies that must be loaded prior to importing this module @@ -55,6 +55,7 @@ 'Register-PSUObjectConversion' 'Register-PSUObjectExpansion' 'Read-PSUKnowledge' + 'Remove-PSUDirectory' 'Remove-PSUKnowledge' 'Remove-PSUPathAlias' 'Select-PSUFunctionCode' @@ -108,6 +109,7 @@ 'ocb' 'ov' 'page' + 'rcd' 'rdk' 'read' 'Remove-PSUString' diff --git a/PSUtil/changelog.md b/PSUtil/changelog.md index 44d99a8..acdc59d 100644 --- a/PSUtil/changelog.md +++ b/PSUtil/changelog.md @@ -1,90 +1,108 @@ # Changelog +## 2.2.38 (2025-05-31) + ++ New: Remove-PSUDirectory - Removes the current directory and moves the console to the parent folder. ++ Upd: Prompt Fred - added nested prompt level if greater than 0 ++ Fix: Remove-PSUKnowledge - now actually works + ## 2.2.35 (2021-10-13) -- New: Object Conversion - NT <--> SID -- New: Object Conversion - string <--> base64 -- New: Command Get-PSUCalendarWeek - Calculates the calendar week of the specified date. -- Upd: Start-PSUTimer - added `-RandomInterval` parameter, randomizing the sound playback interval -- Upd: Start-PSUTimer - added `-DisableScreensaver` parameter, blocking screensaver for the duration of the timer -- Upd: Start-PSUTimer - now automatically adds a day if the destination-time is in the past -- Upd: Manifest - added ctx and cfx to the aliases exported, enabling auto-import from using those. ++ New: Object Conversion - NT <--> SID ++ New: Object Conversion - string <--> base64 ++ New: Command Get-PSUCalendarWeek - Calculates the calendar week of the specified date. ++ Upd: Start-PSUTimer - added `-RandomInterval` parameter, randomizing the sound playback interval ++ Upd: Start-PSUTimer - added `-DisableScreensaver` parameter, blocking screensaver for the duration of the timer ++ Upd: Start-PSUTimer - now automatically adds a day if the destination-time is in the past ++ Upd: Manifest - added ctx and cfx to the aliases exported, enabling auto-import from using those. ## 2.1.28 (2020-03-21) - - New: Component: Knowledge - store and search knowledge - - New: Command: Read-PSUKnowledge (read, page, learn) - retrieves stored knowledge - - New: Command: Write-PSUKnowledge - writes knowledge for later retrieval - - New: Command: Remove-PSUKnowledge - removes knowledge from storage. Use on alternative facts. - - New: Command: Out-PSUVariable - writes input to variable, enabling variable assignment at the end of the pipeline - - New: Alias rmn --> Remove-PSFNull - - New: Dependency on string module, dropping own string cmdlets, ading aliases for compatibility - - New: Adding psf tab completion for property names to common object commands + ++ New: Component: Knowledge - store and search knowledge ++ New: Command: Read-PSUKnowledge (read, page, learn) - retrieves stored knowledge ++ New: Command: Write-PSUKnowledge - writes knowledge for later retrieval ++ New: Command: Remove-PSUKnowledge - removes knowledge from storage. Use on alternative facts. ++ New: Command: Out-PSUVariable - writes input to variable, enabling variable assignment at the end of the pipeline ++ New: Alias rmn --> Remove-PSFNull ++ New: Dependency on string module, dropping own string cmdlets, ading aliases for compatibility ++ New: Adding psf tab completion for property names to common object commands ## 2.0.20 (2019-09-18) - - New: Command: Set-PSUPrompt - Applies a prompt from a set of pre-defined prompts - - New: Command: Register-PSUObjectExpansion - Registers custom expansion rules for Expand-PSUObject - - Upd: Start-PSUTimer - Added -MinFrequency and -MaxFrequency parameters. - - Upd: Start-PSUTimer - Refactored parameter order, rationalized message handling - - Upd: Invoke-PSUExplorer - Enabled opt-in exceptions and improved path resolution - - Upd: Set-PSUPath - Now supports environment variable expansion using %name% notation. - - Fix: Importing module from UNC path works - - Fix: Module import concurrency issue - - Fix: Convert-PSUObject unsafe Scriptblock handling - - Fix: Set-PSUShell foreground color handling (requires latest pre-release version of PSReadline) - - Fix: F1 Keybinding: On non-windows default to detailed help, rather than -ShowWindow - - Fix: F1 Keybinding: In-Console help display now uses same application as launching application + ++ New: Command: Set-PSUPrompt - Applies a prompt from a set of pre-defined prompts ++ New: Command: Register-PSUObjectExpansion - Registers custom expansion rules for Expand-PSUObject ++ Upd: Start-PSUTimer - Added -MinFrequency and -MaxFrequency parameters. ++ Upd: Start-PSUTimer - Refactored parameter order, rationalized message handling ++ Upd: Invoke-PSUExplorer - Enabled opt-in exceptions and improved path resolution ++ Upd: Set-PSUPath - Now supports environment variable expansion using %name% notation. ++ Fix: Importing module from UNC path works ++ Fix: Module import concurrency issue ++ Fix: Convert-PSUObject unsafe Scriptblock handling ++ Fix: Set-PSUShell foreground color handling (requires latest pre-release version of PSReadline) ++ Fix: F1 Keybinding: On non-windows default to detailed help, rather than -ShowWindow ++ Fix: F1 Keybinding: In-Console help display now uses same application as launching application ## Version 2.0.8 (2019-01-13) - - New: Keybinding for `Shift+SpaceBar` on PSReadline 2.0 that inserts a whitespace, helping to mitigate the typing issue in the windows release version. - - Upd: Switched input property tab completion to PSFramework implementation: PSFramework-Input-ObjectProperty - - Upd: Default keybinding for expanding aliases was changed to `Alt` + `q`. The previous one was inoperable under PSReadline 2.0 or later. - - Fix: Get-PSUPathAlias returns empty objects (thanks Corbob) + ++ New: Keybinding for `Shift+SpaceBar` on PSReadline 2.0 that inserts a whitespace, helping to mitigate the typing issue in the windows release version. ++ Upd: Switched input property tab completion to PSFramework implementation: PSFramework-Input-ObjectProperty ++ Upd: Default keybinding for expanding aliases was changed to `Alt` + `q`. The previous one was inoperable under PSReadline 2.0 or later. ++ Fix: Get-PSUPathAlias returns empty objects (thanks Corbob) ## Version 2.0.4 (2018-12-23) - - Fix: Persisting default aliases redirection fails + ++ Fix: Persisting default aliases redirection fails ## Version 2.0.3 (2018-12-23) - - New: Configuration setting 'PSUtil.Import.Alias.SystemOverride'. Persisting this will have PSUtil replace system default aliases. - - New: Tab Expansion for PowerShellGet - - New: Tab Expansion for Select-Object and Select-PSFObject + ++ New: Configuration setting 'PSUtil.Import.Alias.SystemOverride'. Persisting this will have PSUtil replace system default aliases. ++ New: Tab Expansion for PowerShellGet ++ New: Tab Expansion for Select-Object and Select-PSFObject ## Version 2.0.0 (2018-12-15) - - new: Command Get-PSUPathAlias lists all current path aliases - - new: Command Remove-PSUPathAlias removes a path alias - - new: Command Set-PSUPath used to implement the path alias functionality - - new: Command Set-PSUPathAlias creates or updates an alias for a path - - other: Major project refactoring - - rem: Command `Select-PSUObject` has been removed - - new: Alias `Select-PSUObject` pointing at `Select-PSFPobject` - - upd: Alias `ex` now points at `Export-PSFClixml` - - upd: Alias `ix` now points at `Import-PSFClixml` - - upd: Alias `spo` now points at `Select-PSFObject` - - upd: Moved the list of default properties to expand using Expand-PSUObject to configuration + ++ new: Command Get-PSUPathAlias lists all current path aliases ++ new: Command Remove-PSUPathAlias removes a path alias ++ new: Command Set-PSUPath used to implement the path alias functionality ++ new: Command Set-PSUPathAlias creates or updates an alias for a path ++ other: Major project refactoring ++ rem: Command `Select-PSUObject` has been removed ++ new: Alias `Select-PSUObject` pointing at `Select-PSFPobject` ++ upd: Alias `ex` now points at `Export-PSFClixml` ++ upd: Alias `ix` now points at `Import-PSFClixml` ++ upd: Alias `spo` now points at `Select-PSFObject` ++ upd: Moved the list of default properties to expand using Expand-PSUObject to configuration ## Version 1.1.5.17 (2018-06-19) - - new: Command Select-PSUObject - Select-Object in awesome. - - upd: Command Set-PSUString / replace - can now do lambda replacement + ++ new: Command Select-PSUObject - Select-Object in awesome. ++ upd: Command Set-PSUString / replace - can now do lambda replacement ## Version 1.1.4.15 (2018-03-30) - - new: Select-PSUFunctionCode / Inspect + ++ new: Select-PSUFunctionCode / Inspect ## Version 1.1.3.13 (2018-03-20) - - new: Command Start-PSUTimer / timer + ++ new: Command Start-PSUTimer / timer ## Version 1.1.2.12 (2018-03-06) - - new: Command Backup-PSULocation / bu (#18) - - new: Command Set-PSUDrive / set-as (#18) - - new: Command New-PSUDirectory / mcd (#18) + ++ new: Command Backup-PSULocation / bu (#18) ++ new: Command Set-PSUDrive / set-as (#18) ++ new: Command New-PSUDirectory / mcd (#18) ## Version 1.1.1.10 (2018-02-23) - - upd: Select-PSUObjectSample / s can now select last items (#15) - - fix: Format-PSUString / format will now format numbers correctly (#14) + ++ upd: Select-PSUObjectSample / s can now select last items (#15) ++ fix: Format-PSUString / format will now format numbers correctly (#14) ## Version 1.1.1.9 (2018-02-02) - - new: Command Convert-PSUObject / convert. (#9) - Allows converting input from a source format to a destination format. Can be dynamically extended. - - new: Command Register-PSUObjectConversion. (#9) - Registers conversion path for Convert-PSUObject. - - new: Added module tests and build definition for CI/CD integration - - upd: All keybindings can now be configured using the config system. Changes require reimport to apply. (#7) - - fix: Fixed 'replace' not accepting empty string as replace with option (#8) \ No newline at end of file + ++ new: Command Convert-PSUObject / convert. (#9) + Allows converting input from a source format to a destination format. Can be dynamically extended. ++ new: Command Register-PSUObjectConversion. (#9) + Registers conversion path for Convert-PSUObject. ++ new: Added module tests and build definition for CI/CD integration ++ upd: All keybindings can now be configured using the config system. Changes require reimport to apply. (#7) ++ fix: Fixed 'replace' not accepting empty string as replace with option (#8) \ No newline at end of file diff --git a/PSUtil/en-us/strings.psd1 b/PSUtil/en-us/strings.psd1 index 22c131a..58e5cc3 100644 --- a/PSUtil/en-us/strings.psd1 +++ b/PSUtil/en-us/strings.psd1 @@ -1,5 +1,8 @@ @{ + 'Remove-PSUDirectory.Error.IsRootPath' = 'This is the drive root, not deleting the entire volume for you! {0}' # location + 'Remove-PSUDirectory.Error.NotFileSystem' = 'Not a filesystem path, cannot remove the current location: {0}' # $location + 'Remove-PSUDirectory.RemoveItem' = 'Removing {0} items in {1}' # $children.Count, $location 'Set-PSUKnowledge.Library.Path.CreationFailed' = 'Failed to create library path: {0}. Validate the path and ensure the necessary permissions are in place.' # $libraryPath - 'Set-PSUKnowledge.Book.ImportFailed' = 'Failed to read the book "{0}" from "{1}"' # $Book, $bookPath - 'Set-PSUKnowledge.Book.ExportFailed' = 'Failed to write the page "{0}" into the book "{1}" at "{2}"' # $Name, $Book, $bookPath + 'Set-PSUKnowledge.Book.ImportFailed' = 'Failed to read the book "{0}" from "{1}"' # $Book, $bookPath + 'Set-PSUKnowledge.Book.ExportFailed' = 'Failed to write the page "{0}" into the book "{1}" at "{2}"' # $Name, $Book, $bookPath } \ No newline at end of file diff --git a/PSUtil/functions/explorer/Remove-PSUDirectory.ps1 b/PSUtil/functions/explorer/Remove-PSUDirectory.ps1 new file mode 100644 index 0000000..3973e6b --- /dev/null +++ b/PSUtil/functions/explorer/Remove-PSUDirectory.ps1 @@ -0,0 +1,61 @@ +function Remove-PSUDirectory { + <# + .SYNOPSIS + Removes the current directory and moves the console to the parent folder. + + .DESCRIPTION + Removes the current directory and moves the console to the parent folder. + + .PARAMETER Force + Don't ask questions, just do it. + + .PARAMETER EnableException + This parameters disables user-friendly warnings and enables the throwing of exceptions. + This is less user friendly, but allows catching exceptions in calling scripts. + + .PARAMETER WhatIf + If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. + + .PARAMETER Confirm + If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. + + .EXAMPLE + PS C:\> rcd + + Tries to remove the directory and step back one level. + #> + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param ( + [switch] + $Force, + + [switch] + $EnableException + ) + process { + $location = Get-Location + if ($location.Provider.Name -ne 'FileSystem') { + Stop-PSFFunction -String 'Remove-PSUDirectory.Error.NotFileSystem' -StringValues $location -EnableException $EnableException -Cmdlet $PSCmdlet + return + } + + if ($location.Path -eq "$($location.Drive):\") { + Stop-PSFFunction -String 'Remove-PSUDirectory.Error.IsRootPath' -StringValues $location -EnableException $EnableException -Cmdlet $PSCmdlet + return + } + + $children = Get-ChildItem -LiteralPath $location -Force -Recurse + if ($children.Count -lt 1) { + Set-Location -Path '..' + Remove-Item -LiteralPath $location -Force -Confirm:$false + return + } + + if ($Force -or (Test-PSFShouldProcess -Target $location -ActionString 'PSUtil.Remove-PSUDirectory.RemoveItem' -ActionStringValues $children.Count, $location -PSCmdlet $PSCmdlet)) { + Set-Location -Path '..' + Remove-Item -LiteralPath $location -Force -Confirm:$false + } + } +} + +Import-PSUAlias -Name "rcd" -Command "Remove-PSUDirectory" \ No newline at end of file diff --git a/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1 b/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1 index ac28d44..f34e8dc 100644 --- a/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1 +++ b/PSUtil/functions/knowledge/Remove-PSUKnowledge.ps1 @@ -42,7 +42,7 @@ { if (-not (Test-Path -Path $libraryPath)) { return } - $bookName = '{0}.json' -f [System.Text.Encoding]::UTF8.GetBytes($Book) + $bookName = '{0}.json' -f [System.Convert]::ToBase64String(([System.Text.Encoding]::UTF8.GetBytes($Book))) $bookPath = Join-Path $libraryPath $bookName if (-not (Test-Path -Path $bookPath)) { return } diff --git a/PSUtil/internal/prompts/fred.prompt.ps1 b/PSUtil/internal/prompts/fred.prompt.ps1 index 5a18166..3a85d5e 100644 --- a/PSUtil/internal/prompts/fred.prompt.ps1 +++ b/PSUtil/internal/prompts/fred.prompt.ps1 @@ -9,6 +9,9 @@ } } catch { } + if ($NestedPromptLevel -gt 0) { + Write-PSFHostColor -String "[$NestedPromptLevel] " -NoNewLine -DefaultColor DarkGreen + } $segments = $executionContext.SessionState.Path.CurrentLocation.Path -split "\\" if ($segments.Count -lt 4) { Write-Host "$($executionContext.SessionState.Path.CurrentLocation.Path)" -NoNewline } else { Write-Host "($($segments.Count - 2)) ..\$($segments[-2])\$($segments[-1])" -NoNewline } diff --git a/build/psf-build.ps1 b/build/psf-build.ps1 new file mode 100644 index 0000000..04f2850 --- /dev/null +++ b/build/psf-build.ps1 @@ -0,0 +1,72 @@ +<# +This script publishes the module to the gallery. +It expects as input an ApiKey authorized to publish the module. + +Insert any build steps you may need to take before publishing it here. +#> +param ( + $ApiKey, + + $WorkingDirectory = $env:SYSTEM_DEFAULTWORKINGDIRECTORY +) + +# Prepare publish folder +Write-PSFMessage -Level Important -Message "Creating and populating publishing directory" +$publishDir = New-Item -Path $WorkingDirectory -Name publish -ItemType Directory -Force +Remove-Item -Path "$publishDir\*" -Recurse -Force -ErrorAction SilentlyContinue +Copy-Item -Path "$($WorkingDirectory)\PSUtil" -Destination $publishDir.FullName -Recurse -Force + +#region Gather text data to compile +$text = @() +$processed = @() + +# Gather Stuff to run before +foreach ($line in (Get-Content "$($PSScriptRoot)\filesBefore.txt" | Where-Object { $_ -notlike "#*" })) +{ + if ([string]::IsNullOrWhiteSpace($line)) { continue } + + $basePath = Join-Path "$($publishDir.FullName)\PSUtil" $line + foreach ($entry in (Resolve-PSFPath -Path $basePath)) + { + $item = Get-Item $entry + if ($item.PSIsContainer) { continue } + if ($item.FullName -in $processed) { continue } + $text += [System.IO.File]::ReadAllText($item.FullName) + $processed += $item.FullName + } +} + +# Gather commands +Get-ChildItem -Path "$($publishDir.FullName)\PSUtil\internal\functions\" -Recurse -File -Filter "*.ps1" | ForEach-Object { + $text += [System.IO.File]::ReadAllText($_.FullName) +} +Get-ChildItem -Path "$($publishDir.FullName)\PSUtil\functions\" -Recurse -File -Filter "*.ps1" | ForEach-Object { + $text += [System.IO.File]::ReadAllText($_.FullName) +} + +# Gather stuff to run afterwards +foreach ($line in (Get-Content "$($PSScriptRoot)\filesAfter.txt" | Where-Object { $_ -notlike "#*" })) +{ + if ([string]::IsNullOrWhiteSpace($line)) { continue } + + $basePath = Join-Path "$($publishDir.FullName)\PSUtil" $line + foreach ($entry in (Resolve-PSFPath -Path $basePath)) + { + $item = Get-Item $entry + if ($item.PSIsContainer) { continue } + if ($item.FullName -in $processed) { continue } + $text += [System.IO.File]::ReadAllText($item.FullName) + $processed += $item.FullName + } +} +#endregion Gather text data to compile + +#region Update the psm1 file +$fileData = Get-Content -Path "$($publishDir.FullName)\PSUtil\PSUtil.psm1" -Raw +$fileData = $fileData.Replace('""', '""') +$fileData = $fileData.Replace('""', ($text -join "`n`n")) +[System.IO.File]::WriteAllText("$($publishDir.FullName)\PSUtil\PSUtil.psm1", $fileData, [System.Text.Encoding]::UTF8) +#endregion Update the psm1 file + +# Publish to Gallery +Publish-PSFModule -Path "$($publishDir.FullName)\PSUtil" -ApiKey $ApiKey \ No newline at end of file diff --git a/build/psf-prerequisites.ps1 b/build/psf-prerequisites.ps1 new file mode 100644 index 0000000..0645544 --- /dev/null +++ b/build/psf-prerequisites.ps1 @@ -0,0 +1,28 @@ +param ( + [string] + $Repository = 'PSGallery', + + [switch] + $Bootstrap +) + +if ($Bootstrap) { + Invoke-WebRequest 'https://raw.githubusercontent.com/PowershellFrameworkCollective/PSFramework.NuGet/refs/heads/master/bootstrap.ps1' | Invoke-Expression +} + +$modules = @("Pester", "PSFramework", "PSModuleDevelopment", "PSScriptAnalyzer") + +# Automatically add missing dependencies +$data = Import-PowerShellDataFile -Path "$PSScriptRoot\..\PSUtil\PSUtil.psd1" +foreach ($dependency in $data.RequiredModules) { + if ($dependency -is [string]) { + if ($modules -contains $dependency) { continue } + $modules += $dependency + } + else { + if ($modules -contains $dependency.ModuleName) { continue } + $modules += $dependency.ModuleName + } +} + +Install-PSFModule -Name $modules \ No newline at end of file