Skip to content

Instantly share code, notes, and snippets.

@uyriq
Last active March 2, 2025 13:17
Show Gist options
  • Select an option

  • Save uyriq/daef5b7b3a1f6b3e169c3298812affcc to your computer and use it in GitHub Desktop.

Select an option

Save uyriq/daef5b7b3a1f6b3e169c3298812affcc to your computer and use it in GitHub Desktop.
Updates specified packages using winget with ID or name fallback.

πŸš€πŸ“¦ βœ… WinGet+ wup PowerShell Script Documentation

πŸ“ Overview

winget+.ps1

is a PowerShell script that provides enhanced package updating capabilities for Windows Package Manager winget with a convenient alias wup.

✨ Features

  • Updates packages using either full package ID or name fallback
  • Supports single and multiple package updates
  • Works with or without quotes for package names
  • easier usage: it is way shorter to type wup alias instead of winget <command>
  • debug option: you can use -Debug switch to see what is happening under the hood

πŸ› οΈ Setup Instructions

Save the winget+.ps1 script to a permanent location.

Add the alias to your PowerShell profile:

# Open your PowerShell profile
notepad $PROFILE

# Add this line (adjust the path to where you saved winget+.ps1):
Set-Alias wup "C:\Path\To\winget+.ps1"

πŸ“š Usage Examples

# Check available updates
wup

# then you could copy packages IDs from winget output and paste to update them like next examples do

# Update single package

wup pnpm.pnpm

# Update multiple packages
wup pnpm.pnpm yarn.yarn nodejs.nodejs

# Update using array

$apps = @("Microsoft.PowerToys", "Mozilla.Firefox")
wup -Packages $apps

πŸ” -Verbose Switch

You can add the -Verbose switch to see detailed execution information:

# See detailed execution steps
wup pnpm.pnpm -Verbose

πŸ€” The way How it can be used

wup does not support pipelines so you can not use it like so:

```powershell
winget list | wup
```

but possible you can rewrite this script to support pipelines TODO: the only thing is to re-concile with ValueFromRemainingArguments = $true that could make script way bigger

# draft for pipeline feature
[CmdletBinding()]
param(
    [Parameter(ValueFromPipeline = $true,
               ValueFromPipelineByPropertyName = $true,
               Position = 0)]
    [string[]]$Packages,

    [Parameter()]
    [switch]$Install
)

begin {
    $packagesToProcess = @()
}

process {
    if ($_ -is [string]) {
        $packagesToProcess += $_
    }
    elseif ($Packages) {
        $packagesToProcess += $Packages
    }
}

end {
    foreach ($package in $packagesToProcess) {
        if ($Install) {
            Write-Verbose "Installing package: $package"
            winget install $package
        }
        else {
            Write-Verbose "Updating package: $package"
            # Original update logic here
        }
    }
}

Also, note that important --id argument have to be in form packageName.SomeOtherPArt that is most cases but not always, so in these rare cases script will fails as it do simple regex operation to extract package name. probably you could improve script by adding function that relying on winget show command to get package name, this addition will complicate script too

# Function to get package name from winget
# draft
function Get-WingetPackageName {
    param(
        [Parameter(Mandatory = $true)]
        [string]$PackageId
    )

    try {
        Write-Verbose "Getting package details for $PackageId"
        $output = & winget show --id $PackageId --accept-source-agreements 2>&1

        if ($LASTEXITCODE -eq 0) {
            # Extract Name field from winget show output
            $name = $output | Where-Object { $_ -match '^Name:' } | ForEach-Object { ($_ -split ':\s*')[1] }
            Write-Verbose "Found package name: $name"
            return $name
        }
        else {
            Write-Verbose "Winget show failed, falling back to ID parsing"
            return ($PackageId -split '\.')[0]
        }
    }
    catch {
        Write-Verbose "Error getting package name: $_"
        return ($PackageId -split '\.')[0]
    }
}

# Usage in main code
$packageName = Get-WingetPackageName -PackageId $package

πŸ“‹ Requirements

  • Windows Package Manager (winget) installed
  • PowerShell 5.1 or higher

πŸ“œ License

MIT License

This project is licensed under the MIT License - see the MIT License for details.

#! /usr/bin/env pwsh
<#
.SYNOPSIS
Updates specified packages using winget with ID or name fallback.
.DESCRIPTION
This script automates package updates using winget. It first tries to update using the package ID,
and if that fails, falls back to using the package name (first part before the dot).
.PARAMETER Packages
An array of package identifiers to update. the full IDs (e.g. "yarn.yarn")
.EXAMPLE
PS> .\wup.ps1
Shows all available updates (no parameters), then you could copy packages IDs to update them
.EXAMPLE
PS> .\wup.ps1 pnpm.pnpm
Updates single package without quotes
.EXAMPLE
PS> .\wup.ps1 "pnpm.pnpm"
Updates single package using ID with quotes
.EXAMPLE
PS> .\wup.ps1 -Packages "pnpm.pnpm", "yarn.yarn"
Updates multiple packages using full IDs
.EXAMPLE
PS> $apps = @("Microsoft.PowerToys", "Mozilla.Firefox")
PS> .\wup.ps1 -Packages $apps
Updates packages from a variable
.EXAMPLE
PS> wup pnpm.pnpm yarn.yarn node.js
Updates multiple packages using alias without quotes
.EXAMPLE
PS> wup Microsoft.PowerToys
PS> wup "Microsoft.PowerToys"
Both forms work the same (with or without quotes)
.EXAMPLE
# To create permanent alias, add to your PowerShell profile:
Set-Alias wup "$PSScriptRoot\winget+.ps1"
# Then use it like:
PS> wup pnpm.pnpm
PS> wup -Packages pnpm.pnpm,yarn.yarn
.NOTES
Author: Uyriq
Version: 1.0
Licence: MIT
Requires: Windows Package Manager (winget)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false,
Position = 0,
ValueFromRemainingArguments = $true,
HelpMessage = "Package ID(s) to update")]
[ValidateNotNullOrEmpty()]
[string[]]$Packages
)
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# Define emojis as codes to output like $([char]0xd83c)$([char]0xdf0e)
# πŸ“¦ Package U+1F4E6
# πŸš€ Rocket U+1F680
# ❌ Cross Mark (Strike) U+274C
# 🌍 Globe Showing Europe-Africa U+1F30D
# βœ… White Heavy Check Mark U+2705 0x2705
# 🌐 Globus U+
# 🏑 Home House U+
# ⚑️ Streak U+
# checkmark
$checkmark = $([char]0x2705)
# rocket
$rocket = [System.Char]::ConvertFromUtf32(0x1F680)
# package
$package = [System.Char]::ConvertFromUtf32(0x1F4E6)
# Cross Mark (Strike)
$cross = [System.Char]::ConvertFromUtf32(0x274C)
# Globus
$globe = [System.Char]::ConvertFromUtf32(0x1F310)
# Home House
$homeHouse = [System.Char]::ConvertFromUtf32(0x1F3E1)
# Streak, Lightning bolt with variation selector for color fill
$streak = [System.Text.Encoding]::UTF8.GetString(@(0xE2, 0x9A, 0xA1, 0xEF, 0xB8, 0x8F))
if (-not $Packages) {
Write-Verbose "No packages specified, showing all available updates"
winget upgrade
Write-Host "To update packages, specify package ID(s) as arguments" -ForegroundColor Yellow
return
}
Write-Verbose "Processing $(($Packages).Count) package(s)"
foreach ($package in $Packages) {
Write-Progress -Activity "Updating packages" -Status "Processing $package" -PercentComplete (($Packages.IndexOf($package) + 1) / $Packages.Count * 100)
Write-Verbose "----------------------------------------"
Write-Verbose "Attempting to update package: $package"
try {
Write-Verbose "Step 1: Trying update with --id parameter"
$output = & winget upgrade --id $package --accept-source-agreements 2>&1
Write-Verbose "Winget output: $output"
if ($output -match "No available upgrade found") {
Write-Verbose "Package $package is already up to date"
Write-Host "Package $package is already up to date"
continue
}
if ($LASTEXITCODE -eq 0) {
Write-Verbose "Successfully updated $package using ID"
# Test emoji support and use in output
if ($Host.UI.SupportsVirtualTerminal) {
Write-Host "Successfully updated $package using ID" -ForegroundColor Green -NoNewline
Write-Host " $checkmark" -ForegroundColor Green
}
else {
# Fallback for terminals without emoji support
Write-Host "[OK] Successfully updated $package using ID" -ForegroundColor Green
}
}
else {
Write-Verbose "ID update failed with exit code: $LASTEXITCODE"
throw "Exit code: $LASTEXITCODE"
}
}
catch {
Write-Verbose "Step 2: ID update failed, attempting name-based update"
try {
$packageName = ($package -split '\.')[0]
Write-Verbose "Trying with --name parameter: $packageName"
$output = & winget upgrade --name $packageName --accept-source-agreements 2>&1
Write-Verbose "Winget output: $output"
if ($output -match "No available upgrade found") {
Write-Verbose "Package $package is already up to date"
Write-Host "Package $package is already up to date"
continue
}
if ($LASTEXITCODE -eq 0) {
Write-Verbose "Successfully updated $package using name"
Write-Host "$checkmark Successfully updated $package using name" -ForegroundColor Green
}
else {
Write-Verbose "Name update failed with exit code: $LASTEXITCODE"
throw "Exit code: $LASTEXITCODE"
}
}
catch {
Write-Verbose "Both update attempts failed for $package"
Write-Warning "$cross Failed to update $package. Error: $($_.Exception.Message)"
}
}
}
Write-Progress -Activity "Updating packages" -Completed
Write-Verbose "Update operation completed"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment