diff --git a/BackupAndRestoreStuff/PSBackupMyStuff/PSBackupMyStuff.psm1 b/BackupAndRestoreStuff/PSBackupMyStuff/PSBackupMyStuff.psm1 index 85ce598..218c4b0 100644 --- a/BackupAndRestoreStuff/PSBackupMyStuff/PSBackupMyStuff.psm1 +++ b/BackupAndRestoreStuff/PSBackupMyStuff/PSBackupMyStuff.psm1 @@ -42,7 +42,7 @@ function Export-ScoopPackages { # "npm install" takes multiple arguments separated by space function Export-NpmGlobalPackages { # FIXME not cross platform - $rawList = npm.cmd list --global --depth=0 --parseable + $rawList = npm list --global --depth=0 --parseable $baseFolder, $packages = $rawList $basePath = Join-Path $baseFolder "node_modules\" diff --git a/BackupAndRestoreStuff/PSRestoreMyStuff/PSRestoreMyStuff.psm1 b/BackupAndRestoreStuff/PSRestoreMyStuff/PSRestoreMyStuff.psm1 index 814a4ab..1eea18f 100644 --- a/BackupAndRestoreStuff/PSRestoreMyStuff/PSRestoreMyStuff.psm1 +++ b/BackupAndRestoreStuff/PSRestoreMyStuff/PSRestoreMyStuff.psm1 @@ -10,7 +10,7 @@ function Install-ScoopWithPackages { Invoke-WebRequest -UseBasicParsing 'https://raw.githubusercontent.com/scoopinstaller/install/master/install.ps1' -OutFile install.ps1 # FIXME not cross platform - ./install.ps1 -ScoopDir 'C:/Tools/scoop' -NoProxy -RunAsAdmin + ./install.ps1 -ScoopDir 'C:/Tools/scoop' -ScoopGlobalDir 'C:/Tools/scoop-global-apps' -NoProxy -RunAsAdmin Remove-Item ./install.ps1 diff --git a/BitwardenWrapper/BitwardenWrapper.psd1 b/BitwardenWrapper/BitwardenWrapper.psd1 index 527a997..24fe3e0 100644 --- a/BitwardenWrapper/BitwardenWrapper.psd1 +++ b/BitwardenWrapper/BitwardenWrapper.psd1 @@ -82,7 +82,9 @@ # VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = @() + AliasesToExport = @( + "bw-unlock" + ) # DSC resources to export from this module # DscResourcesToExport = @() @@ -124,4 +126,3 @@ # DefaultCommandPrefix = '' } - diff --git a/BitwardenWrapper/BitwardenWrapper.psm1 b/BitwardenWrapper/BitwardenWrapper.psm1 index 50ae3c9..e8eac39 100644 --- a/BitwardenWrapper/BitwardenWrapper.psm1 +++ b/BitwardenWrapper/BitwardenWrapper.psm1 @@ -1,31 +1,40 @@ +# $CONFIG = [PSCustomObject]@{} +$BITWARDEN_WRAPPER_MESSAGES = [PSCustomObject]@{ + ALREADY_UNLOCKED = 'Bitwarden database was already unlocked' + WRONG_MASTER_PASSWORD = 'Invalid master password.' + EMPTY_MASTER_PASSWORD = 'Master password is required.' + NOT_LOGGED_IN = 'No user logged in.' +} function Unlock-BitwardenDatabase { [CmdletBinding()] - param () - - $WRONG_MASTER_PASSWORD = 'Invalid master password.' - $EMPTY_MASTER_PASSWORD = 'Master password is required.' + [Alias("bw-unlock")] + param ( + [Switch] + $RemovePSReadline + ) - if ($env:BW_SESSION) { - Write-Output "Bitwarden database was already unlocked" + $status = bw status | ConvertFrom-Json | Select-Object -ExpandProperty status + if ($status -eq "unlocked") { + Write-Warning $BITWARDEN_WRAPPER_MESSAGES.ALREADY_UNLOCKED return } - $SESSION = bw unlock --raw + $bwUnlockOutput = bw unlock --raw - if (Get-Module PSReadLine) { - Write-Verbose "Removing PSReadline module" - Remove-Module PSReadLine + if (-not $LASTEXITCODE -eq 0) { + return } - if ($SESSION -match $WRONG_MASTER_PASSWORD -or $SESSION -match $EMPTY_MASTER_PASSWORD) { - Throw "Unlock failed" + if ($RemovePSReadline -and (Get-Module PSReadLine)) { + Write-Output "Removing PSReadline module from current session..." + Remove-Module PSReadLine } Write-Output "Bitwarden database unlocked" # remember current session Write-Verbose "Saving session key to environment variable" - $env:BW_SESSION = $SESSION + $env:BW_SESSION = $bwUnlockOutput } Get-ChildItem -Recurse -File -LiteralPath "$PSScriptRoot/Classes" | ForEach-Object { @@ -62,7 +71,12 @@ function Test-ContainsSensitiveWords { $SensitiveWords ) - return $null -ne ($SensitiveWords | Where-Object { $InputString -match $_ } | Select-Object -First 1) + process { + Write-Output "matching each of $SensitiveWords against $InputString" + + return $null -ne ($SensitiveWords | Where-Object { $InputString -match $SensitiveWords } | Select-Object -First 1) + } + } # Unlock-BitwardenDatabase diff --git a/Contig-Wrapper/Contig-Wrapper.psm1 b/Contig-Wrapper/Contig-Wrapper.psm1 new file mode 100644 index 0000000..e4c5c91 --- /dev/null +++ b/Contig-Wrapper/Contig-Wrapper.psm1 @@ -0,0 +1,86 @@ + +if (-not (Get-Command -ErrorAction SilentlyContinue Contig.exe) ) { + throw "CONTIG IS NOT AVAILABLE ON PATH" +} + +function Invoke-Contig { + Write-Output ("+" * 80) + Contig.exe -nobanner $args + Write-Output ("-" * 80) + # Contig.exe -nobanner $args | Write-Output + # Contig64.exe -nobanner $args | Write-Output +} + +Set-Alias contig Invoke-Contig + +function Test-FileFragmentation { + [CmdletBinding()] + param ( + # File to analyze + [Parameter(Mandatory)] + [System.IO.FileInfo] + $file + ) + + begin { + } + + process { + Invoke-Contig -a $file + } + + end { + } +} + +function Invoke-FileDefragmentation { + [CmdletBinding()] + param ( + # File to defragment + [Parameter(Mandatory)] + [System.IO.FileInfo] + $file + ) + + begin { + } + + process { + if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) { + throw "retry as administrator" + } + + $start = Get-Date; + + Invoke-Contig $file + + $elapsed = (Get-Date) - $start; + + Write-Output "$file defragmented in $elapsed" + } + + end { + } +} + +Set-Alias defrag Invoke-FileDefragmentation + +# TODO implement +function Read-ContigOutput { + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [String] + $rawOutput + ) + + begin { + } + + process { + Write-Verbose $rawOutput + } + + end { + } +} diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index 2d7aa8a..a13ef92 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,3 +1,4 @@ @{ Severity = @('Error', 'Warning') + ExcludeRules = @('PSAvoidUsingPositionalParameters') } diff --git a/PSUtils/PSUtils.psm1 b/PSUtils/PSUtils.psm1 new file mode 100644 index 0000000..e42c860 --- /dev/null +++ b/PSUtils/PSUtils.psm1 @@ -0,0 +1,411 @@ +######################################################################################################################## +####################################### Scoop utils +######################################################################################################################## + +function Get-ScoopSize { + $cache = Get-ChildItem -Recurse $env:SCOOP/cache | + Measure-Object -Sum Length | + Select-Object -ExpandProperty Sum + + $persisted = Get-ChildItem -Recurse $env:SCOOP/persist | + Measure-Object -Sum Length | + Select-Object -ExpandProperty Sum + + $installed = Get-ChildItem -Recurse $env:SCOOP/apps | + Measure-Object -Sum Length | + Select-Object -ExpandProperty Sum + + + [PSCustomObject]@{ + 'cache size (MB)' = [math]::Round($cache / 1MB, 2) + 'persisted data size (MB)' = [math]::Round($persisted / 1MB, 2) + 'installed apps size (GB)' = [math]::Round($installed / 1GB, 2) + } +} + +function Update-EverythingHaphazardly { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param () + + if (-not $PSCmdlet.ShouldProcess('Update apps')) { + return + } + + if (Get-Command -ErrorAction SilentlyContinue pipx) { + Write-Output ('-' * $Host.UI.RawUI.WindowSize.Width) + Write-Output '- Updating pipx packages' + + pipx upgrade-all + } + + if (Get-Command -ErrorAction SilentlyContinue npm) { + Write-Output ('-' * $Host.UI.RawUI.WindowSize.Width) + Write-Output '- Outdated npm global packages' + npm outdated -g + + Write-Output '- Updating npm global packages' + npm update -g + } + + Update-ScoopAndCleanAfter + + Update-GitRepositoriesSafely | Format-Table +} + +function Update-ScoopAndCleanAfter { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param () + + Write-Output ('-' * $Host.UI.RawUI.WindowSize.Width) + Write-Output '- Updating scoop packages' + + $telegram = 'telegram' + + scoop update 6>&1 | Write-Verbose + + $status_output = scoop status 6>&1 + $status_output + if ($status_output -match $telegram) { + Write-Output '- Stopping telegram...' + Get-Process $telegram | Stop-Process + } + + if ($PSCmdlet.ShouldProcess('Update apps')) { + scoop update * + } + + Write-Output '- Running scoop cleanup...' + scoop cleanup * + + Write-Output '- Clearing cache...' + scoop cache show + scoop cache rm * + + if ($out -match $telegram) { + Write-Output '- Starting telegram...' + & $telegram + } +} + +function Update-GitRepositoriesSafely { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] + [CmdletBinding()] + param ( + [Parameter(Position = 0)] + [System.IO.DirectoryInfo[]] + $RootRepositoriesFolder = (Get-Item $env:PROJECTS_FOLDER) + ) + + if (-not $PSCmdlet.ShouldProcess('Update apps')) { + Write-Output 'Really?? Running git fetch is reaaaaaaally low risk' + + } + Write-Information -InformationAction Continue "Searching for repositories in $RootRepositoriesFolder ..." + + $repos = Get-ChildItem -Verbose -Directory -Force -Recurse $RootRepositoriesFolder | + Where-Object FullName -NotMatch 'node_modules' | + Where-Object FullName -NotMatch 'vendor' | + Where-Object FullName -NotMatch 'Library' | + Where-Object FullName -Match '.git$' | + ForEach-Object { + Write-Verbose "scanning $($_.FullName)" + $_ + } + + foreach ($gitFolder in $repos) { + $repo = Split-Path -Path $gitFolder -Parent + + git -C $repo fetch --all + git -C $repo status --short --branch | Select-String '##' + } +} +######################################################################################################################## +####################################### JSCPD +######################################################################################################################## + +function Invoke-CopyPasteDetectorDefaultConfig { + [CmdletBinding()] + param ( + # path + [Parameter(Mandatory = $true)] + [System.IO.DirectoryInfo] + $Folder + ) + + jscpd --config "$($env:PROJECTS_FOLDER)/_CONFIGS/.jscpd.json" $Folder +} + +######################################################################################################################## +####################################### File utils +######################################################################################################################## + +function Test-ContainsBOM { + [CmdletBinding()] + [OutputType([bool])] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [System.IO.FileInfo] + $file + ) + + process { + $contents = New-Object byte[] 3 + $stream = [System.IO.File]::OpenRead($file.FullName) + $stream.Read($contents, 0, 3) | Out-Null + $stream.Close() + + return $contents[0] -eq 0xEF -and $contents[1] -eq 0xBB -and $contents[2] -eq 0xBF + } +} + +function Test-HasCrlfEndings { + [CmdletBinding()] + [OutputType([bool])] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [System.IO.FileInfo] + $file + ) + + process { + (Get-Content -Raw -LiteralPath $file.FullName) -match "`r`n" + } +} + +function Test-BomHereRecursive { + + Get-ChildItem -File -Recurse | + Where-Object FullName -NotMatch '.zip' | + Where-Object FullName -NotMatch '.git' | + Where-Object FullName -NotMatch '.mypy_cache' | + Where-Object FullName -NotMatch 'node_modules' | + Where-Object FullName -NotMatch 'vendor' | + Where-Object { -not (Test-ContainsBOM $_) } | + Select-Object FullName +} + +function Get-BranchAndSha { + param () + + "$(git branch --show-current)-$(git rev-parse --short HEAD)" +} + +function Find-Duplicates { + [CmdletBinding()] + param ( + [Parameter()] + [String[]] + $Paths = '.' + ) + python 'D:/Projects/__libraries-wheels-etc/find_duplicates.py' $Paths +} + +function New-TemporaryDirectory { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] + param() + + if ($PSCmdlet.ShouldProcess('Create new temporary directory')) { + New-Item -ItemType Directory -Path (Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid())) + } +} + +# function New-FastTemporaryDirectory { +# [string] $name = [System.Guid]::NewGuid() +# New-Item -ItemType Directory -Path ("C:/TEMP-$name") +# } + +######################################################################################################################## +####################################### Hardlinks utils +######################################################################################################################## + +function New-HardLink { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + Param( + [parameter(position = 0)] [String] $Name, + [parameter(position = 1)] [Object] $Value + ) + + if ($PSCmdlet.ShouldProcess('Create new HardLink')) { + New-Item -ItemType HardLink -Name $Name -Value $Value + } +} + +function Find-HardLinks { + Get-ChildItem . -Recurse -Force | + Where-Object { $_.LinkType } | + Select-Object FullName, LinkType, Target +} + +######################################################################################################################## +####################################### Misc +######################################################################################################################## + +function Invoke-SshCopyId { + Param( + [parameter(Mandatory, Position = 1)] + [String] + $Destination + ) + + Get-Content '~/.ssh/id_rsa.pub' | ssh $Destination 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys' +} + + +function Add-ModuleShim { + [CmdletBinding()] + param ( + [System.IO.DirectoryInfo] + $ModuleFolder + ) + + $ModuleFullPath = $ModuleFolder.FullName + $ShimPath = Join-Path "$HOME/Documents/PowerShell/Modules/" $ModuleFolder.Name + + New-Item -ItemType Junction -Path $ShimPath -Value $ModuleFullPath -Confirm +} + +function Get-OldVsCodeExtensions { + [CmdletBinding()] + param ( + [System.IO.DirectoryInfo] + $VscodeExtensionsDirectory = (Get-Item 'C:/Tools/vscode/data/extensions') + ) + + $SEMVER_REGEX = '(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-((?:0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?' + $SPLITTER_REGEX = "^(?.*?)-(?$SEMVER_REGEX)$" + + # if (-not $Aggro) { + # $DATETIME_CUTOFF = (Get-Date).AddDays(-7) + # } + # else { + # $DATETIME_CUTOFF = Get-Date + # } + + $parsedExtensionFolders = @( + Get-ChildItem -Directory -Path $VscodeExtensionsDirectory | + Sort-Object -Descending CreationTime | + ForEach-Object { + $name = $_.Name + + if (-not ($name -match $SPLITTER_REGEX)) { + Write-Error "this name is not correctly matched: $name" + } + + [pscustomobject]@{ + Name = $Matches.name + Version = $Matches.version + Directory = $_ + } + } + ) + + # $VSCODE_INSTALLED_EXTENSIONS = @(code --list-extensions) | + # ForEach-Object { + # [PSCustomObject]@{ + # Name = $_ + # } + # } + # $uniqueExtensionFoldersFound = @($parsedExtensionFolders | Sort-Object Name -Unique) + + # Compare-Object -PassThru $VSCODE_INSTALLED_EXTENSIONS $uniqueExtensionFoldersFound -Property Name | + # Where-Object SideIndicator -match "=>" | + # Select-Object Name, Version, Directory + + $parsedExtensionFolders | + Group-Object Name | + Where-Object Count -GT 1 | + # Where-Object LastWriteTime -GT $DATETIME_CUTOFF | + ForEach-Object { + $newest, $old = $_.Group + + $old.Directory + } | + # Flatten array of arrays + ForEach-Object { + $_ + } +} + +function Invoke-GitGcWithReflogExpire { + [CmdletBinding()] + [Alias('git-gc-expire-unreachable')] + param ( + # Work tree of the repository where git gc should be invoked + [Parameter(Position = 0)] + [System.IO.DirectoryInfo] + $Path = '.' + ) + + process { + git -C $Path reflog expire --expire-unreachable=now --all + git -C $Path gc --aggressive --prune=now + } +} + +function Update-VsCodePortable { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory, Position = 0)] + [System.IO.DirectoryInfo] + $Destination, + [Parameter(Mandatory)] + [System.IO.DirectoryInfo] + $DataFolder, + [Switch] + $Force + ) + + if (-not $PSCmdlet.ShouldProcess('UPDATE VSCODE PORTABLE?')) { + return + } + + $allProducts = Invoke-WebRequest 'https://code.visualstudio.com/sha?build=stable' | + Select-Object -ExpandProperty Content | + ConvertFrom-Json | + Select-Object -ExpandProperty 'products' + + $winPortableProduct = $allProducts | Where-Object { $_.platform.os -match 'win32-x64-archive' } + + $readableInstalledVersion = (code -v)[0] + $installedVersionHash = (code -v)[1] + + if (-not $Force -and $winPortableProduct.version -match $installedVersionHash) { + Write-Output "VSCode is up to date. Current version is $($winPortableProduct.name)" + return + } + + if (Get-Process -Name com.docker.backend -ErrorAction SilentlyContinue) { + throw "STOP DOCKER BEFORE UPDATING VSCODE" + } + + Write-Output 'Shutting down wsl just to be sure' + wsl --shutdown + + if (Test-Path $Destination) { + $backupFolder = "$($Destination.Name)-$readableInstalledVersion" + + if (Test-Path (Join-Path $Destination $backupFolder)) { + throw 'Backup folder already exists' + } + + Write-Output "Backing up current content of $Destination $backupFolder" + Rename-Item $Destination -NewName $backupFolder -ErrorAction Stop + } + + # TODO cache downloaded archive + $archive = New-TemporaryFile + Invoke-WebRequest $winPortableProduct.url -OutFile $archive + + # TODO check hash + + Expand-Archive -Path $archive -DestinationPath $Destination + + New-Item -ItemType Junction -Path (Join-Path $Destination 'data') -Target (Resolve-Path $DataFolder) +} + +function Edit-HostsFile { + [CmdletBinding()] + param () + + sudo notepad c:\windows\system32\drivers\etc\hosts +} diff --git a/ProxyUtils/ProxyUtils.psm1 b/ProxyUtils/ProxyUtils.psm1 new file mode 100644 index 0000000..0a559f8 --- /dev/null +++ b/ProxyUtils/ProxyUtils.psm1 @@ -0,0 +1,247 @@ +$PROXY_REGISTRY_PATH = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' + +function Get-Proxy { + [CmdletBinding()] + param ( + [Switch] + $Raw, + [Switch] + $ShowWinHttpProxy + ) + + $arguments = if ($Raw) { + @{ Property = '*' } + } + else { + @{ + Property = @(@{ + Name = 'IsEnabled' + Expression = { $_.ProxyEnable -eq 1 } + } + @{ + Name = 'Server Address' + Expression = { $_.ProxyServer } + } + @{ + Name = 'Exclusions' + Expression = { $_.ProxyOverride -split ';' } + } + ) + } + } + + Get-ItemProperty -Path $PROXY_REGISTRY_PATH | Select-Object @arguments + + [PSCustomObject]@{ + # all_proxy = $env:all_proxy + http_proxy = $env:http_proxy + https_proxy = $env:https_proxy + no_proxy = $env:no_proxy + } | Format-List + + if ($ShowWinHttpProxy) { + netsh winhttp show proxy + } +} + +function Enable-Proxy { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + Param + ( + # server address + [Parameter(Position = 0, Mandatory)] + [String] + $ProxyHost, + # port number + [Parameter(Position = 1, Mandatory)] + [String] + $ProxyPort, + # Exclusions + [Parameter(Position = 2)] + [String[]] + $Exclusions, + [Switch] + $ImportWinHttpProxy, + [Switch] + $FlushDns, + [Switch] + $IncludeWsl, + [Switch] + $TestProxyAvailability, + [Switch] + $Beep, + [Switch] + $Wait + ) + + process { + $ProxyServer = "$($ProxyHost):$($ProxyPort)" + + if ($TestProxyAvailability -and -not (Test-NetConnection -ComputerName $ProxyHost -Port $ProxyPort).TcpTestSucceeded) { + Write-Error -Message "The proxy address is not valid: $ProxyServer" + } + else { + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name ProxyEnable -Value 1 + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name ProxyServer -Value $ProxyServer + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name ProxyOverride -Value ($Exclusions -join ';') + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name AutoConfigURL -Value '' + + $ProxyUrl = "http://$ProxyServer" + + # $env:all_proxy = $ProxyUrl + $env:http_proxy = $ProxyUrl + $env:https_proxy = $ProxyUrl + $env:no_proxy = $Exclusions -join ',' + # [Environment]::SetEnvironmentVariable('all_proxy', $env:all_proxy, 'User') + [Environment]::SetEnvironmentVariable('http_proxy', $env:http_proxy, 'User') + [Environment]::SetEnvironmentVariable('https_proxy', $env:https_proxy, 'User') + [Environment]::SetEnvironmentVariable('no_proxy', $env:no_proxy , 'User') + + if ($ImportWinHttpProxy) { + Import-WinHttpProxyFromIeProxy + } + + if ($FlushDns) { + ipconfig /flushdns | Write-Output + } + + if ($IncludeWsl) { + Enable-WslProxy + } + } + + Get-Proxy + + if ($Beep) { + [System.Console]::Beep() + } + + if ($Wait) { + Write-Output 'Press any key to continue...' + $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') + } + } +} + +function Disable-Proxy { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [switch] + $RemoveProxySettings, + [switch] + $ResetWinHttpProxy, + [switch] + $FlushDns, + [Switch] + $IncludeWsl, + [Switch] + $Beep, + [Switch] + $Wait + ) + + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name ProxyEnable -Value 0 + + # [Environment]::SetEnvironmentVariable('all_proxy', $null, 'User') + [Environment]::SetEnvironmentVariable('http_proxy', $null, 'User') + [Environment]::SetEnvironmentVariable('https_proxy', $null, 'User') + [Environment]::SetEnvironmentVariable('no_proxy', $null, 'User') + + # Remove from current shell if present + @( + # 'Env:\all_proxy' + 'Env:\http_proxy' + 'Env:\https_proxy' + 'Env:\no_proxy' + ) | Where-Object { Test-Path $_ } | Remove-Item + + if ($RemoveProxySettings) { + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name ProxyServer -Value '' + Set-ItemProperty -Path $PROXY_REGISTRY_PATH -Name ProxyOverride -Value '' + } + + if ($ResetWinHttpProxy) { + Reset-WinHttpProxy + } + + if ($FlushDns) { + ipconfig /flushdns | Write-Output + } + + if ($IncludeWsl) { + Disable-WslProxy + } + + Get-Proxy + + if ($Beep) { + [System.Console]::Beep() + } + + if ($Wait) { + Write-Output 'Press any key to continue...' + $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') + } +} + +function Enable-WslProxy { + [CmdletBinding()] + param () + + wsl -d ubuntu -- bash -i -c 'enable-proxy && accept-igt-proxy' +} + +function Disable-WslProxy { + [CmdletBinding()] + param () + + wsl -d ubuntu -- bash -i -c 'disable-proxy' +} + +function Import-WinHttpProxyFromIeProxy { + [CmdletBinding()] + param () + + sudo netsh winhttp import proxy source=ie +} + +function Reset-WinHttpProxy { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding()] + param () + + if ($PSCmdlet.ShouldProcess('Reset WinHTTP proxy settings?')) { + sudo netsh winhttp reset proxy + } +} + +# TODO test me +# TODO reduce duplication +function New-ProxyShortcuts { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [Parameter(Position = 0)] + [System.IO.DirectoryInfo] + $Destination = '.', + [Switch] + $EnableShortcut, + [Switch] + $DisableShortcut + ) + if (-not($PSCmdlet.ShouldProcess('Create shortcuts?'))) { + return + } + + $WshShell = New-Object -ComObject WScript.Shell + + if ($EnableShortcut) { + $Shortcut = $WshShell.CreateShortcut("$Destination\enable-proxy.lnk") + $Shortcut.TargetPath = 'pwsh.exe -NoLogo -Command "Enable-Proxy -ImportWinHttpProxy -IncludeWsl"' + $Shortcut.Save() + } + if ($DisableShortcut) { + $Shortcut = $WshShell.CreateShortcut("$Destination\disable-proxy.lnk") + $Shortcut.TargetPath = 'pwsh.exe -NoLogo -Command "Disable-Proxy -FlushDns -ResetWinHttpProxy -IncludeWsl"' + $Shortcut.Save() + } +} diff --git a/Scripts/Add-ModuleShim.ps1 b/Scripts/Add-ModuleShim.ps1 deleted file mode 100644 index 21b24d2..0000000 --- a/Scripts/Add-ModuleShim.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -[CmdletBinding()] -param ( - [System.IO.DirectoryInfo] - $ModuleFolder -) - -$ModuleName = $ModuleFolder.Name - -$ShimPath = Join-Path "$HOME/Documents/PowerShell/Modules/" $ModuleName - -New-Item -ItemType Junction -Path $ShimPath -Value $ModuleFolder -Confirm diff --git a/Scripts/Find-Duplicates.ps1 b/Scripts/Find-Duplicates.ps1 new file mode 100644 index 0000000..30d0548 --- /dev/null +++ b/Scripts/Find-Duplicates.ps1 @@ -0,0 +1,16 @@ +[CmdletBinding()] +param ( + [Parameter()] + [System.IO.DirectoryInfo[]] + $Paths = (Get-Location) +) + +# TODO implement me +throw "NOT IMPLEMENTED" + +$files = Get-ChildItem -LiteralPath $Paths + +$files | ForEach-Object { + $size = $_.Length + +} diff --git a/Scripts/Get-DirtyRepositories.ps1 b/Scripts/Get-DirtyRepositories.ps1 index 4701a5a..59b3755 100644 --- a/Scripts/Get-DirtyRepositories.ps1 +++ b/Scripts/Get-DirtyRepositories.ps1 @@ -6,28 +6,29 @@ param ( [string] $SaveToVariable ) - # TODO export type data? # Update-TypeData ... -$forEachArguments = @{ - OutVariable = if ($SaveToVariable) { - "OutVariable" - } - else { - $null - } +function Test-HasUncommittedChanges { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [System.IO.DirectoryInfo] + $path + ) + + return $null -ne (git -C $path status -s) } -function Test-HasDirtyIndex { +function Test-HasNoRemote { [CmdletBinding()] param ( [Parameter(Mandatory)] [System.IO.DirectoryInfo] - $gitDir + $path ) - return $null -ne (git --git-dir $gitDir --work-tree $workTree status -s) + return $null -eq (git -C $path remote) } function Test-HasUnpushedCommits { @@ -35,10 +36,10 @@ function Test-HasUnpushedCommits { param ( [Parameter(Mandatory)] [System.IO.DirectoryInfo] - $gitDir + $path ) - return $null -ne (git --git-dir $gitDir --work-tree $workTree log --branches --not --remotes --oneline) + return $null -ne (git -C $path log --branches --not --remotes --oneline) } function Test-HasForgottenStashes { @@ -46,45 +47,59 @@ function Test-HasForgottenStashes { param ( [Parameter(Mandatory)] [System.IO.DirectoryInfo] - $gitDir + $path ) - return $null -ne (git --git-dir $gitDir stash list) + return $null -ne (git -C $path stash list) } -Write-Output "Searching for repositories in $RootFolder ..." + +function Test-HasIgnoredFilesAndFolder { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [System.IO.DirectoryInfo] + $path + ) + + return ( git -C $path status -s --ignored ) | + Select-String -Pattern '^!!' | + Measure-Object | + Select-Object -ExpandProperty Count +} + +Write-Information -InformationAction Continue "Searching for repositories in $RootFolder ..." $repos = Get-ChildItem -Verbose -Directory -Force -Recurse $RootFolder | - Where-Object FullName -NotMatch "node_modules" | - Where-Object FullName -NotMatch "vendor" | - Where-Object FullName -NotMatch "Library" | - Where-Object FullName -Match ".git$" | + Where-Object FullName -NotMatch 'node_modules' | + Where-Object FullName -NotMatch 'vendor' | + Where-Object FullName -NotMatch 'Library' | + Where-Object FullName -Match '.git$' | ForEach-Object { Write-Verbose "scanning $($_.FullName)" $_ } -Write-Output "found $($repos.Length) repos; checking status..." +Write-Information -InformationAction Continue "found $($repos.Length) repos; checking status..." $repos | ForEach-Object { - $gitDir = $_ $workTree = Split-Path -Path $_ -Parent - $dirtyIndex = Test-HasDirtyIndex $gitDir - $unpushedCommits = Test-HasUnpushedCommits $gitDir - $forgottenStashes = Test-HasForgottenStashes $gitDir + $uncommittedChanges = Test-HasUncommittedChanges $workTree + $hasNoRemote = Test-HasNoRemote $workTree + $unpushedCommits = Test-HasUnpushedCommits $workTree + $forgottenStashes = Test-HasForgottenStashes $workTree + $ignoredFilesAndFolders = Test-HasIgnoredFilesAndFolder $workTree [pscustomobject]@{ - PSTypename = "GitRepo" - Repo = $workTree - AllGood = -not ($dirtyIndex -or $unpushedCommits -or $forgottenStashes) - ChangesToCommit = $dirtyIndex - CommitsToPush = $unpushedCommits - StashesToClear = $forgottenStashes + PSTypename = 'GitRepo' + Repo = $workTree + AllGood = -not ($uncommittedChanges -or $hasNoRemote -or $unpushedCommits -or $forgottenStashes ) + UncommittedChanges = $uncommittedChanges + HasNoRemote = $hasNoRemote + CommitsToPush = $unpushedCommits + StashesToClear = $forgottenStashes + # AllGood does not count these + Ignored = $ignoredFilesAndFolders } - } @forEachArguments - -if ($SaveToVariable) { - Write-Verbose "setting variable $SaveToVariable in parent scope" - Set-Variable -Scope 1 -Name $SaveToVariable -Value $OutVariable -} + } diff --git a/Scripts/Invoke-GitGarbageCollectorInAllRepositories.ps1 b/Scripts/Invoke-GitGarbageCollectorInAllRepositories.ps1 index 269b029..dabead8 100644 --- a/Scripts/Invoke-GitGarbageCollectorInAllRepositories.ps1 +++ b/Scripts/Invoke-GitGarbageCollectorInAllRepositories.ps1 @@ -9,23 +9,28 @@ param ( Write-Output "Searching for repositories in $RootFolder..." -$repos = Get-ChildItem -Directory -Force -Recurse $RootFolder -Include ".git" | - Where-Object FullName -NotMatch "node_modules" +$repos = Get-ChildItem -Verbose -Directory -Force -Recurse $RootFolder | + Where-Object FullName -NotMatch "node_modules" | + Where-Object FullName -NotMatch "vendor" | + Where-Object FullName -NotMatch "Library" | + Where-Object FullName -Match ".git$" | + ForEach-Object { + Write-Verbose "scanning $($_.FullName)" + $_ + } Write-Output "Found $($repos.Length) repos." $repos | ForEach-Object { - $gitDir = $_ $workTree = Split-Path -Path $_ -Parent Write-Output "running aggressive garbage collection in $workTree" - if ($Quiet) { - git --git-dir $gitDir gc --aggressive | Out-Null - } - else { - git --git-dir $gitDir gc --aggressive + $output = git -C $workTree gc --aggressive + + if (-not $Quiet) { + Write-Output $output } if ($LASTEXITCODE) { diff --git a/Scripts/Move-DockerDesktopDataDistro.ps1 b/Scripts/Move-DockerDesktopDataDistro.ps1 new file mode 100644 index 0000000..53a510e --- /dev/null +++ b/Scripts/Move-DockerDesktopDataDistro.ps1 @@ -0,0 +1,39 @@ +[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] +param ( + # Path of tar file + [Parameter()] + [string] + $TarFileName = "W:\docker-desktop-data.tar", + # Path where the new distro is imported + [string] + [string] + $WslDistroDestination = "W:\docker-desktop-data\" +) + +Write-Output "shutting down WSL" +wsl --shutdown + +Write-Output "---" +wsl -l -v +Write-Output "---" + +if ($PSCmdlet.ShouldProcess($TarFileName, "export wsl distro")) { + wsl --export docker-desktop-data $TarFileName +} +else { + throw "cannot continue" +} + +if ($PSCmdlet.ShouldProcess("docker-desktop-data", "unregister wsl distro")) { + wsl --unregister docker-desktop-data +} +else { + throw "cannot continue" +} + +if ($PSCmdlet.ShouldProcess($WslDistroDestination, "import wsl distro")) { + wsl --import docker-desktop-data $WslDistroDestination $TarFileName --version 2 +} +else { + throw "cannot continue" +} diff --git a/Scripts/Send-WOL.ps1 b/Scripts/Send-WOL.ps1 deleted file mode 100644 index 144fd9f..0000000 --- a/Scripts/Send-WOL.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -# function Send-WOL { -<# - .SYNOPSIS - Send a WOL packet to a broadcast address - .PARAMETER mac - The MAC address of the device that need to wake up - .PARAMETER ip - The IP address where the WOL packet will be sent to - .EXAMPLE - Send-WOL -mac 00:11:32:21:2D:11 -ip 192.168.8.255 -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory = $True, Position = 1)] - [string]$mac, - [string]$ip = "255.255.255.255", - [int]$port = 9 -) -$broadcast = [Net.IPAddress]::Parse($ip) - -$mac = (($mac.replace(":", "")).replace("-", "")).replace(".", "") -$target = 0, 2, 4, 6, 8, 10 | ForEach-Object { [convert]::ToByte($mac.substring($_, 2), 16) } -$packet = (, [byte]255 * 6) + ($target * 16) - -$UDPclient = New-Object System.Net.Sockets.UdpClient -$UDPclient.Connect($broadcast, $port) -[void]$UDPclient.Send($packet, 102) -# } diff --git a/Scripts/Test-GradleWrapperIntegrity.ps1 b/Scripts/Test-GradleWrapperIntegrity.ps1 new file mode 100644 index 0000000..5455b75 --- /dev/null +++ b/Scripts/Test-GradleWrapperIntegrity.ps1 @@ -0,0 +1,19 @@ +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [String] + $Version +) + +$wrapperPath = "gradle/wrapper/gradle-wrapper.jar" + +if (-not (Test-Path $wrapperPath )) { + throw "No wrapper found" +} + +$expected = Invoke-RestMethod -Uri "https://services.gradle.org/distributions/gradle-$Version-wrapper.jar.sha256" +$actual = (Get-FileHash $wrapperPath -Algorithm SHA256).Hash.ToLower() +Write-Verbose "expected hash: $expected" +Write-Verbose "actual hash: $actual" + +@{$true = 'OK: Checksum match'; $false = "ERROR: Checksum mismatch!`nExpected: $expected`nActual: $actual" }[$actual -eq $expected] diff --git a/Scripts/Wake.ps1 b/Scripts/Wake.ps1 new file mode 100644 index 0000000..dab82f9 --- /dev/null +++ b/Scripts/Wake.ps1 @@ -0,0 +1,79 @@ +####################################################### +## +## Wake.ps1, v1.0, 2013 +## +## Adapted by Ammaar Limbada +## Original Author: Matthijs ten Seldam, Microsoft (see: http://blogs.technet.com/matthts) +## +####################################################### + +<# +.SYNOPSIS +Starts a list of physical machines by using Wake On LAN. + +.DESCRIPTION +Wake sends a Wake On LAN magic packet to a given machine's MAC address. + +.PARAMETER MacAddress +MacAddress of target machine to wake. + +.EXAMPLE +Wake A0DEF169BE02 + +.INPUTS +None + +.OUTPUTS +None + +.NOTES +Make sure the MAC addresses supplied don't contain "-" or ".". +#> + + +param( [Parameter(Mandatory = $true, HelpMessage = "MAC address of target machine to wake up")] + [string] $MacAddress ) + + +Set-StrictMode -Version Latest + +function Send-Packet([string]$MacAddress) { + <# + .SYNOPSIS + Sends a number of magic packets using UDP broadcast. + + .DESCRIPTION + Send-Packet sends a specified number of magic packets to a MAC address in order to wake up the machine. + + .PARAMETER MacAddress + The MAC address of the machine to wake up. + #> + + try { + $Broadcast = ([System.Net.IPAddress]::Broadcast) + + ## Create UDP client instance + $UdpClient = New-Object Net.Sockets.UdpClient + + ## Create IP endpoints for each port + $IPEndPoint = New-Object Net.IPEndPoint $Broadcast, 9 + + ## Construct physical address instance for the MAC address of the machine (string to byte array) + $MAC = [Net.NetworkInformation.PhysicalAddress]::Parse($MacAddress.ToUpper()) + + ## Construct the Magic Packet frame + $Packet = [Byte[]](, 0xFF * 6) + ($MAC.GetAddressBytes() * 16) + + ## Broadcast UDP packets to the IP endpoint of the machine + $UdpClient.Send($Packet, $Packet.Length, $IPEndPoint) | Out-Null + $UdpClient.Close() + } + catch { + $UdpClient.Dispose() + $Error | Write-Error; + } +} + +## Send magic packet to wake machine +Write-Output "Sending magic packet to $MacAddress" +Send-Packet $MacAddress diff --git a/Scripts/Watch-Command.ps1 b/Scripts/Watch-Command.ps1 new file mode 100644 index 0000000..6ca55bf --- /dev/null +++ b/Scripts/Watch-Command.ps1 @@ -0,0 +1,123 @@ +############################################################################## +## +## Watch-Command +## +## From Windows PowerShell Cookbook (O'Reilly) +## by Lee Holmes (http://www.leeholmes.com/guide) +## +## as found on https://www.powershellcookbook.com/recipe/PrtD/program-monitor-a-command-for-changes +## +############################################################################## + +<# + +.SYNOPSIS + +Watches the result of a command invocation, alerting you when the output +either matches a specified string, lacks a specified string, or has simply +changed. + +.EXAMPLE + +PS > Watch-Command { Get-Process -Name Notepad | Measure } -UntilChanged +Monitors Notepad processes until you start or stop one. + +.EXAMPLE + +PS > Watch-Command { Get-Process -Name Notepad | Measure } -Until "Count : 1" +Monitors Notepad processes until there is exactly one open. + +.EXAMPLE + +PS > Watch-Command { + Get-Process -Name Notepad | Measure } -While 'Count : \d\s*\n' +Monitors Notepad processes while there are between 0 and 9 open +(once number after the colon). + +#> + + +[CmdletBinding(DefaultParameterSetName = "Forever")] +param( + ## The script block to invoke while monitoring + [Parameter(Mandatory = $true, Position = 0)] + [ScriptBlock] $ScriptBlock, + + ## The delay, in seconds, between monitoring attempts + [Parameter()] + [Double] $DelaySeconds = 1, + + ## Specifies that the alert sound should not be played + [Parameter()] + [Switch] $Quiet, + + ## Monitoring continues only while the output of the + ## command remains the same. + [Parameter(ParameterSetName = "UntilChanged", Mandatory = $false)] + [Switch] $UntilChanged, + + ## The regular expression to search for. Monitoring continues + ## until this expression is found. + [Parameter(ParameterSetName = "Until", Mandatory = $false)] + [String] $Until, + + ## The regular expression to search for. Monitoring continues + ## until this expression is not found. + [Parameter(ParameterSetName = "While", Mandatory = $false)] + [String] $While +) + +Set-StrictMode -Version 3 + +$initialOutput = "" + +## Start a continuous loop +while ($true) { + ## Run the provided script block + $r = & $ScriptBlock + + ## Clear the screen and display the results + Clear-Host + $ScriptBlock.ToString().Trim() + "" + $textOutput = $r | Out-String + $textOutput + + ## Remember the initial output, if we haven't + ## stored it yet + if (-not $initialOutput) { + $initialOutput = $textOutput + } + + ## If we are just looking for any change, + ## see if the text has changed. + if ($UntilChanged) { + if ($initialOutput -ne $textOutput) { + break + } + } + + ## If we need to ensure some text is found, + ## break if we didn't find it. + if ($While) { + if ($textOutput -notmatch $While) { + break + } + } + + ## If we need to wait for some text to be found, + ## break if we find it. + if ($Until) { + if ($textOutput -match $Until) { + break + } + } + + ## Delay + Start-Sleep -Seconds $DelaySeconds +} + +## Notify the user +if (-not $Quiet) { + [Console]::Beep(1000, 1000) +} diff --git a/Scripts/Write-RepoContributions.ps1 b/Scripts/Write-RepoContributions.ps1 new file mode 100644 index 0000000..46fc852 --- /dev/null +++ b/Scripts/Write-RepoContributions.ps1 @@ -0,0 +1,42 @@ + +[CmdletBinding()] +param ( + [System.IO.DirectoryInfo] + $TargetRepo = ".", + # comma-separated string with folder/file names + [System.IO.DirectoryInfo[]] + $Include, + [System.IO.DirectoryInfo[]] + $Exclude, + [Switch] + $Quiet +) +$repoName = Split-Path -Leaf $TargetRepo +Write-Output "------------------------------------------------" +Write-Output "- $repoName" +Write-Output "------------------------------------------------" +Write-Output "" + +$cmdArgs = @( + if ($Quiet) { + "--silent-progress" + } + "-C" # Detect inter-file line moves and copies + "-M" # Detect intra-file line moves and copies + "--ignore-whitespace" + "--cost"; "cocomo,hours" + "--branch"; "master" + if ($Include) { + "--incl" + (( $Include | Resolve-Path -Relative ) -join "," -replace "\\", "/" ) + } + if ($Exclude) { + "--excl" + (( $Exclude | Resolve-Path -Relative ) -join "," -replace "\\", "/" ) + } + $TargetRepo +) +Write-Verbose "git-fame $cmdArgs" +git-fame @cmdArgs + +Write-Output ""