winpack.psm1
using module psCandy Import-Module "$PSScriptRoot\visuals.ps1" -Force Import-Module "$PSScriptRoot\classes.ps1" -Force Import-Module "$PSScriptRoot\tools.ps1" -Force . "$PSscriptRoot\GumEnv.ps1" [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $module = Get-InstalledModule -Name winpack -ErrorAction SilentlyContinue if (-not $module) { $Script:version = "debug" } else { $Script:version = $module.Version } $sources = @{ "winget" = "winget" "scoop" = "scoop" } function Get-FieldLength { param( [string]$buffer ) $i = 0 $buffer.ToCharArray() | ForEach-Object { $l = [Text.Encoding]::UTF8.GetByteCount($_) if ($l -ge 2) { $l = $l - 1 } $i += $l } return $i } function RetrievePackages { param( [string]$source = $null, [switch]$update = $false, [switch]$uninstall = $false ) $GetParams = @{} if ($source) { $GetParams.Add("source", $source) } if ($update -and $uninstall) { # TODO: #2 make error message a generic function [System.Console]::setcursorposition(0, $Y) $Title = gum style " ERROR " --background $($Theme["red"]) --foreground $($Theme["white"]) --bold $buffer = gum style "$($Title)`n'-update' & '-uninstall' cannot be used at the same time" --border "rounded" --width ($Host.UI.RawUI.BufferSize.Width - 2) --foreground $($Theme["yellow"]) $buffer | ForEach-Object { [System.Console]::write($_) } return $null } $Spinner = [Spinner]::new("Dots") $Spinner.Start("Loading Packages List") $packages = Get-WinGetPackage if ($source) { $packages = $packages | Where-Object { $_.Source -eq $source } } if ($update) { $packages = $packages | Where-Object { $_.IsUpdateAvailable -eq $true } } [package[]]$InstalledPackages = @() $packages | ForEach-Object { $InstalledPackages += [package]::new($_.Name, $_.Id, $_.AvailableVersions, $_.Source, $_.IsUpdateAvailable, $_.InstalledVersion) } $Spinner.Stop() return $InstalledPackages } function ShowPackages { param( [package[]]$InstalledPackages, [switch]$update = $false, [switch]$uninstall = $false ) [column[]]$cols = @() $cols += [column]::new("Name", "Name", 35) $cols += [column]::new("Id", "Id", 35) $cols += [column]::new("InstalledVersion", "Version", 17, [Align]::Right) $cols += [column]::new("Source", "Source", 10) [System.Collections.Generic.List[ListItem]]$choices = makeItems -columns $cols -items $InstalledPackages $width = $Host.UI.RawUI.BufferSize.Width - 2 $height = $Host.UI.RawUI.BufferSize.Height - 9 if ($update -eq $true) { $title = "List of Packages to Update" } elseif ($uninstall -eq $true) { $title = "List of Packages to Uninstall" } else { $title = "List of Installed Packages" } $header = makeHeader -columns $cols # $Spinner.Stop() [console]::clear() $titre = [Style]::new($title) $titre.SetBorder($true) $titre.SetColor([Colors]::Coral()) $titre.SetAlign([Align]::Center) $headercolor = [color]::new([colors]::Aqua()) $headercolor.Style = [Styles]::Underline [console]::writeline($titre.Render()) $list = [List]::new($choices) $list.SetHeight($height) # $list.SetBorder($true) $list.setHeader($header) $list.headerColor = $headercolor # $list.SetLimit($true) $c = $list.Display() [package[]]$packages = @() if ($c) { $c | ForEach-Object { $packages += $_.Value } } Clear-Host # Return choosen packages without the "Available" property return $packages #| Select-Object -Property * -ExcludeProperty Available } function Get-WGPackage { param( [string]$source = $null, [switch]$update = $false, [switch]$uninstall = $false ) $params = @{} if ($source) { $params.Add("source", $source) } if ($update) { $params.Add("update", $true) } if ($uninstall) { $params.Add("uninstall", $true) } $packages = RetrievePackages @params if ($packages) { $params = $params | Select-Object -Property * -ExcludeProperty source $packages = ShowPackages -InstalledPackages $packages -update $update -uninstall $uninstall } if ($uninstall -eq $true) { uninstallPackages -packages $packages } if ($update -eq $true) { updatePackages -packages $packages } return $packages | Select-Object -Property * -ExcludeProperty Available } function installPackages { param( [package[]]$packages ) $Spinner = [Spinner]::new("Bubble") $packages | ForEach-Object { if (-not $Spinner.running) { $Spinner.start("Installing $($_.Name)") } else { $Spinner.SetLabel("Installing $($_.Name)") } $command = "winget install --id $($_.Id)" Invoke-Expression $command | Out-Null } $Spinner.Stop() } function uninstallPackages { param( [package[]]$packages ) $Spinner = [Spinner]::new("Bubble") $packages | ForEach-Object { if (-not $Spinner.running) { $Spinner.start("Uninstalling $($_.Name)") } else { $Spinner.SetLabel("Uninstalling $($_.Name)") } $command = "winget uninstall --id $($_.Id)" Invoke-Expression $command | Out-Null } $Spinner.Stop() } function updatePackages { param( [package[]]$packages ) $Spinner = [Spinner]::new("Bubble") $packages | ForEach-Object { if (-not $Spinner.running) { $Spinner.start("Upgrading $($_.Name)") } else { $Spinner.SetLabel("Upgrading $($_.Name)") } $command = "winget upgrade --id $($_.Id)" Invoke-Expression $command | Out-Null } $Spinner.Stop() } function Find-WGPackage { param( [string]$query = $null, [string]$source = $null, [switch]$install = $false ) [Spinner]$Spinner $SearchParams = @{} $Y = $host.ui.rawui.CursorPosition.Y $width = $Host.UI.RawUI.BufferSize.Width - 2 if (-not $query -or $null -eq $query) { $buffer = gum style "Enter search query" --border "rounded" --width $width $buffer | ForEach-Object { [System.Console]::write($_) } $query = gum input --placeholder "Search for a package" $SearchParams.Add("query", $query) } if ($source) { $SearchParams.Add("source", $source) } else { $source = gum style "every sources" --foreground "#FF0000" } if ($query) { $title = gum style $query --foreground "#00FF00" --bold $Spinner = [Spinner]::new("Dots") $queries = $query.Split(",") $packages = @() $queries | ForEach-Object { if (-not $Spinner.running) { $Spinner.start("Searching for $_ in $source") } else { $Spinner.SetLabel("Searching for $_ in $source") } $SearchParams["query"] = [string]$_.Trim() $packs = Find-WinGetPackage @SearchParams $packs | ForEach-Object { $packages += $_ } } [System.Console]::setcursorposition(0, $Y) } else { [System.Console]::setcursorposition(0, $Y) $buffer = [Style]::new("No query specified") $buffer.SetBorder($true) $buffer.SetColor([Colors]::White(),[Colors]::Red()) [Console]::Write($buffer.Render()) Start-Sleep -Seconds 2 return $null } if ($packages) { [column[]]$cols = @() $cols += [column]::new("Name", "Name", 35) $cols += [column]::new("Id", "Id", 35) $cols += [column]::new("Available", "Version", 17) $cols += [column]::new("Source", "Source", 10) [package[]]$InstalledPackages = @() $packages | ForEach-Object { $InstalledPackages += [package]::new($_.Name, $_.Id, $_.AvailableVersions, $_.Source, $_.IsUpdateAvailable, $_.InstalledVersion) } [System.Collections.Generic.List[ListItem]]$choices = makeItems -columns $cols -items $InstalledPackages $height = $Host.UI.RawUI.BufferSize.Height - 9 [System.Console]::setcursorposition(0, $Y) # $title = makeTitle -title "Choose Packages to Install" -width $width $header = makeHeader -columns $cols $Spinner.Stop() Clear-Host $titre = [Style]::new("Choose Packages to Install") $titre.SetBorder($true) $titre.SetColor([Colors]::BlueViolet()) $titre.SetAlign([Align]::Center) $headercolor = [color]::new([colors]::Aqua()) $headercolor.Style = [Styles]::Underline [console]::writeline($titre.Render()) $list = [List]::new($choices) $list.SetHeight($height) # $list.SetBorder($true) $list.setHeader($header) $list.headerColor = $headercolor # $list.SetLimit($true) $c = $list.Display() [package[]]$packages = @() if ($c) { $c | ForEach-Object { $packages += $_.Value } } Clear-Host } if ($install) { installPackages -packages $packages } if ($Spinner) { $Spinner.Stop() } return $packages | Select-Object -Property * -ExcludeProperty Available } function Build-Script { param ( [switch]$preview = $false ) $first = $true $packages = RetrievePackages -source "winget" while ($true) { $Selectedpackages = ShowPackages -InstalledPackages $packages if ($Selectedpackages) { $result = gum style "Choose a script type :" --foreground $($Theme["brightGreen"]) --bold --border rounded --width ($Host.UI.RawUI.BufferSize.Width - 2) --align center GumOutput -text $result $types = @( "Winget", "Winpack" ) $type = $types -join "`n" | gum choose if ($first) { $file = gum input --placeholder "Enter the name of the script (without extension)" } $index = $types.IndexOf($type) switch ($index) { 0 { $filename = "$file.ps1" $header = @( "#", "# Path: $filename", "# Generated by WinPack $script:version", "# Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")", "#" ) if ($true -eq $first) { if (Test-Path -Path $filename) { # TODO: Optimize this part $replace = gum confirm "File already exists, do you want to replace it?" --affirmative "Yes" --negative "No" && $true || $false if ($replace) { Remove-Item -Path $filename -Force $null = New-Item -ItemType File -Path $filename -Force $header -join "`n" | Out-File -FilePath $filename -Append } } else { $null = New-Item -ItemType File -Path $filename -Force $header -join "`n" | Out-File -FilePath $filename -Append } } $Selectedpackages | ForEach-Object { "winget install --id $($_.Id) # $($_.Name)" | Out-File -FilePath $filename -Append $id = $_.Id $packages = $packages | Where-Object { $_.Id -ne $Id } } $first = $false Clear-Host # $result = gum style "Script saved as $file.ps1" --foreground $($Theme["brightYellow"]) --bold --border rounded --width ($Host.UI.RawUI.BufferSize.Width - 2) --align center # GumOutput -text $result } 1 { $filename = "$file.json" if (Test-Path -Path $filename) { $replace = gum confirm "File already exists, do you want to replace it?" --affirmative "Yes" --negative "No" && $true || $false if ($replace) { Remove-Item -Path $filename -Force $null = New-Item -ItemType File -Path $filename -Force } } $Selectedpackages | ConvertTo-Json -AsArray | Out-File -FilePath $filename -Append $first = $false } Default { return $null } } } if ($filename) { $replace = gum confirm "Would you add some entries to the file ?" --affirmative "Yes" --negative "No" && $true || $false if (-not $replace) { break } } else { break } } if ($filename) { $result = gum style "Script saved as $filename" --foreground $($Theme["brightYellow"]) --bold --border rounded --width ($Host.UI.RawUI.BufferSize.Width - 2) --align center Clear-Host GumOutput -text $result if ($preview) { Get-Content -Path $filename | gum pager --height ($Host.UI.RawUI.BufferSize.Height / 2) --width ($Host.UI.RawUI.BufferSize.Width % 2) --border "rounded" } } } function Start-Winpack { Clear-Host $result = 0 $title = [Style]::new("Welcome to WinPack $($script:version)") $title.SetColor([Colors]::Yellow()) $title.SetBorder($true) $Title.setAlign([Align]::Center) while ($result -ne -1) { [Console]::setcursorposition(0, 0) [Console]::Writeline($title.Render()) # [Console]::setcursorposition(0, 4) $items = [System.Collections.Generic.List[ListItem]]::new() $items.Add([ListItem]::new("Find Packages", 0, "🔎")) $items.Add([ListItem]::new("List Installed Packages", 1, "📃")) $items.Add([ListItem]::new("Install Packages", 2, "📦")) $items.Add([ListItem]::new("Update Packages", 3, "🌀")) $items.Add([ListItem]::new("Uninstall Packages", 4, "🗑️",[Colors]::Red())) $items.Add([ListItem]::new("Build Script", 5, "📜")) $items.Add([ListItem]::new("Exit", 100, "❌")) $list = [List]::new($items) $list.SetLimit($true) $index = $list.Display() switch ($index.value) { 0 { $null = Find-WGPackage -source "winget" } 1 { Get-WGPackage } 2 { $null = Find-WGPackage -install } 3 { Get-WGPackage -update } 4 { Get-WGPackage -uninstall } 5 { Build-Script } 100 { $result = -1 } Default { $result = -1 } } } } |