Instantly share code, notes, and snippets.
Last active
October 30, 2024 02:24
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save ergosteur/c61087cf78cdda37176f0a63a0a9145c to your computer and use it in GitHub Desktop.
Helper script that creates a sort of Ninite-like experience for Winget
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Load Windows Forms assembly for confirmation dialog | |
Add-Type -AssemblyName System.Windows.Forms | |
### Variables | |
# Array of .NET Desktop Runtime packages | |
$dotnetDesktopPackages = 5..8 | ForEach-Object { "Microsoft.DotNet.DesktopRuntime.${_}" } | |
$dotnetRuntimePackages = 5..8 | ForEach-Object { "Microsoft.DotNet.Runtime.${_}" } | |
# Grouped arrays for applications (with corrected package IDs) | |
$browsers = @("Mozilla.Firefox", "Google.Chrome", "Vivaldi.Vivaldi", "Waterfox.Waterfox") | |
$developmentTools = @("Microsoft.VisualStudioCode", "Git.Git", "Python.Python.3.12") | |
$mediaTools = @("CodecGuide.K-LiteCodecPack.Full", "Gyan.FFmpeg", "yt-dlp.yt-dlp", "Audacity.Audacity", "Nikse.SubtitleEdit", "Streamlink.Streamlink", "VideoLAN.VLC", "Jellyfin.JellyfinMediaPlayer") | |
$communicationTools = @("SlackTechnologies.Slack", "Discord.Discord", "Beeper.Beeper", "TeamViewer.TeamViewer", "Microsoft.Teams") | |
$gaming = @("Valve.Steam", "Valve.SteamCMD", "GOG.Galaxy", "HeroicGamesLauncher.HeroicGamesLauncher") | |
$utilities = @("Rufus.Rufus", "Bitwarden.Bitwarden", "AntibodySoftware.WizTree", "Microsoft.Sysinternals", "Microsoft.PowerToys", "7zip.7zip", "M2Team.NanaZip", "Notepad++.Notepad++", "Dell.CommandUpdate", "WinSCP.WinSCP", "ElaborateBytes.VirtualCloneDrive") | |
$productivity = @("Microsoft.Office", "TheDocumentFoundation.LibreOffice", "Microsoft.OneDrive", "Google.GoogleDrive", "Splashtop.SplashtopBusiness") | |
$networkingTools = @("PuTTY.PuTTY", "TeraTermProject.teraterm5", "tailscale.tailscale", "WiresharkFoundation.Wireshark", "Insecure.Npcap") | |
$vcredistPackages = @("Microsoft.VCLibs.Desktop.14", "Microsoft.VCRedist.2010.x64", "Microsoft.VCRedist.2010.x86", "Microsoft.VCRedist.2012.x64", "Microsoft.VCRedist.2012.x86", "Microsoft.VCRedist.2013.x64", "Microsoft.VCRedist.2013.x86", "Microsoft.VCRedist.2015+.x64", "Microsoft.VCRedist.2015+.x86", "Microsoft.VCRedist.2005.x64", "Microsoft.VCRedist.2005.x86", "Microsoft.VCRedist.2008.x64", "Microsoft.VCRedist.2008.x86") | |
# Combine all packages by group with labels | |
$packageGroups = @{ | |
"Browsers" = $browsers | |
"Development Tools" = $developmentTools | |
"Media Tools" = $mediaTools | |
"Communication Tools" = $communicationTools | |
"Gaming" = $gaming | |
"Utilities" = $utilities | |
"Productivity" = $productivity | |
"Networking Tools" = $networkingTools | |
"Visual C++ Redistributables" = $vcredistPackages | |
".NET Desktop Runtime" = $dotnetDesktopPackages | |
".NET Runtime" = $dotnetRuntimePackages | |
} | |
### Functions | |
# Function to check if the script is running as Administrator | |
function Check-Admin { | |
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { | |
Write-Output "This script is not running as Administrator." | |
$adminPrompt = Read-Host "Would you like to relaunch as Administrator? (y/n)" | |
if ($adminPrompt -eq 'y') { | |
# Relaunch the script with elevated permissions | |
Write-Output "Relaunching script as Administrator..." | |
Start-Process -FilePath "powershell" -ArgumentList "-File `"$PSCommandPath`"" -Verb RunAs | |
exit | |
} else { | |
Write-Output "Some installations may require Administrator privileges. Continuing without elevation..." | |
} | |
} else { | |
Write-Output "Running with Administrator privileges." | |
} | |
} | |
# Function to check if PowerShell 7 or newer is installed and relaunch the script using it | |
function Check-And-Relaunch-PowerShellCore { | |
if ($PSVersionTable.PSVersion.Major -lt 7) { | |
Write-Output "You're running Windows PowerShell (version $($PSVersionTable.PSVersion.Major))." | |
# Check if PowerShell 7 or newer is already installed | |
$newPSPath = "$env:ProgramFiles\PowerShell\7\pwsh.exe" | |
if (Test-Path $newPSPath) { | |
Write-Output "PowerShell 7 or newer is already installed. Relaunching the script using PowerShell Core..." | |
Start-Process -FilePath $newPSPath -ArgumentList "-File `"$PSCommandPath`"" -NoNewWindow | |
exit | |
} else { | |
# Prompt to install PowerShell Core if not found | |
$installPrompt = Read-Host "Would you like to install the latest version of PowerShell Core using winget? (y/n)" | |
if ($installPrompt -eq 'y') { | |
# Install PowerShell using winget | |
Write-Output "Installing PowerShell via winget..." | |
winget install --id Microsoft.Powershell --source winget | |
# Confirm installation and relaunch script | |
if (Test-Path $newPSPath) { | |
Write-Output "PowerShell installed successfully. Relaunching script in PowerShell Core..." | |
Start-Process -FilePath $newPSPath -ArgumentList "-File `"$PSCommandPath`"" -NoNewWindow | |
exit | |
} else { | |
Write-Output "Failed to install PowerShell via winget. Please try installing manually." | |
exit | |
} | |
} | |
} | |
} | |
else { | |
Write-Output "You're running PowerShell Core version $($PSVersionTable.PSVersion.Major), continuing..." | |
} | |
} | |
# Function to check and install Desktop App Installer / winget if necessary | |
function Check-And-Install-Winget { | |
if (!(Get-Command winget -ErrorAction SilentlyContinue)) { | |
Write-Output "winget is not installed on this system." | |
$installPrompt = Read-Host "Would you like to install the Desktop App Installer (winget)? (y/n)" | |
if ($installPrompt -eq 'y') { | |
# Download the Desktop App Installer package | |
$installerUrl = "https://aka.ms/MicrosoftDesktopAppInstaller" | |
$installerPath = "$env:TEMP\DesktopAppInstaller.appxbundle" | |
Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath | |
# Install the Desktop App Installer package | |
Write-Output "Installing Desktop App Installer (winget)..." | |
Add-AppxPackage -Path $installerPath | |
# Verify installation | |
if (Get-Command winget -ErrorAction SilentlyContinue) { | |
Write-Output "winget installed successfully." | |
} else { | |
Write-Output "Failed to install winget. Please try installing manually." | |
exit | |
} | |
} else { | |
Write-Output "winget is required to run this script. Exiting..." | |
exit | |
} | |
} | |
else { | |
Write-Output "winget is available on this system." | |
} | |
} | |
# Function to install packages with error handling and success confirmation | |
function Install-Package { | |
param ( | |
[string]$packageId | |
) | |
Write-Output "Preparing to install package: ${packageId}" # Debugging output | |
try { | |
Write-Output "Executing command: winget install --id ${packageId} -e" | |
$installProcess = Start-Process -FilePath "winget" -ArgumentList "install --id ${packageId} -e" -NoNewWindow -PassThru -Wait | |
# Check if installation was successful based on exit code | |
if ($installProcess.ExitCode -eq 0) { | |
Write-Output "${packageId} installed successfully." | |
} else { | |
Write-Output "Error installing ${packageId}: Exit code $($installProcess.ExitCode)" | |
} | |
} | |
catch { | |
Write-Output "Error installing ${packageId}: ${_}" | |
} | |
} | |
### Main Script execution | |
# Run the admin check | |
Check-Admin | |
# Run the PowerShell version check and relaunch if necessary | |
Check-And-Relaunch-PowerShellCore | |
# Run the winget check | |
Check-And-Install-Winget | |
# Run `winget list` once and store the output as an array of lines | |
$installedPackagesOutput = winget list | |
# Function to check if a package is installed by looking for a matching line in the output | |
function Is-PackageInstalled { | |
param ($packageId, $installedPackagesOutput) | |
return $installedPackagesOutput | Select-String -SimpleMatch $packageId | |
} | |
# Create a list with both group and individual package options, marking installed packages | |
$menuOptions = @() | |
foreach ($groupName in $packageGroups.Keys) { | |
# Add group item | |
$menuOptions += [PSCustomObject]@{ Name = "${groupName} (All)"; Type = "Group"; Installed = ""; Packages = $packageGroups[$groupName] } | |
# Add individual items, checking each package installation status | |
foreach ($package in $packageGroups[$groupName]) { | |
$isInstalled = if (Is-PackageInstalled -packageId $package -installedPackagesOutput $installedPackagesOutput) { "Y" } else { "" } | |
$menuOptions += [PSCustomObject]@{ Name = $package; Type = "Individual"; Installed = $isInstalled; Packages = @($package) } | |
} | |
} | |
# Show interactive menu to select groups or individual packages with installation status | |
$selectedItems = $menuOptions | Out-GridView -Title "Select Groups or Individual Packages to Install (Ctrl for multiple)" -PassThru | |
# Check if user made selections | |
if ($selectedItems) { | |
# Prepare list of selected package names for confirmation | |
$selectedpackageIds = $selectedItems | ForEach-Object { $_.Packages } | ForEach-Object { $_ } | Sort-Object | Get-Unique | |
$selectedText = $selectedpackageIds -join "`r`n" | |
# Confirmation popup with list of selected packages | |
$confirmationForm = New-Object Windows.Forms.Form | |
$confirmationForm.Text = "Confirm Selection" | |
$confirmationForm.Size = New-Object System.Drawing.Size(400,300) | |
$confirmationForm.StartPosition = "CenterScreen" | |
$textBox = New-Object Windows.Forms.TextBox | |
$textBox.Multiline = $true | |
$textBox.ReadOnly = $true | |
$textBox.Dock = "Fill" | |
$textBox.Text = "You selected the following packages:`r`n`r`n$selectedText" | |
$confirmationForm.Controls.Add($textBox) | |
$okButton = New-Object Windows.Forms.Button | |
$okButton.Text = "OK" | |
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK | |
$okButton.Dock = "Bottom" | |
$confirmationForm.Controls.Add($okButton) | |
$cancelButton = New-Object Windows.Forms.Button | |
$cancelButton.Text = "Cancel" | |
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel | |
$cancelButton.Dock = "Bottom" | |
$confirmationForm.Controls.Add($cancelButton) | |
$confirmationForm.AcceptButton = $okButton | |
$confirmationForm.CancelButton = $cancelButton | |
$dialogResult = $confirmationForm.ShowDialog() | |
# Process user's confirmation choice | |
if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK) { | |
Write-Output "You confirmed the following packages for installation:" | |
$selectedpackageIds | ForEach-Object { Write-Output "- $_" } | |
# Install each confirmed package | |
foreach ($package in $selectedpackageIds) { | |
Write-Output "Attempting to install package: ${package}" # Debugging output | |
Install-Package -packageId $package | |
} | |
} else { | |
Write-Output "Installation canceled by user." | |
} | |
} else { | |
Write-Output "No selection made. Exiting..." | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment