Update-AllTheThings.ps1
|
<#PSScriptInfo
.DESCRIPTION A script to automatically update all PowerShell modules, PowerShell Help, GitHub CLI extensions, GitHub Copilot CLI, and packages (apt, brew, Chocolatey, WinGet). .VERSION 0.6.0 .GUID 3a1a1ec9-0ef6-4f84-963d-be1505dab6a8 .AUTHOR Sam Erde .COPYRIGHT (c) 2024 Sam Erde. All rights reserved. .TAGS Update PowerShell Windows macOS Linux Ubuntu .LICENSEURI https://github.com/SamErde/PSPreWorkout/blob/main/LICENSE .PROJECTURI https://github.com/SamErde/PSPreWorkout/ .ICONURI #> function Test-PSPreworkoutCommand { <# .SYNOPSIS Tests whether a command is available. .DESCRIPTION Resolves a command without emitting an error and returns whether it was found. .PARAMETER Name The name of the command to resolve. .OUTPUTS System.Boolean #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name ) return $null -ne (Get-Command -Name $Name -ErrorAction SilentlyContinue) } function Get-PSPreworkoutPlatform { <# .SYNOPSIS Gets the current operating-system platform. .DESCRIPTION Returns a stable platform name across Windows PowerShell 5.1 and PowerShell 7. .OUTPUTS System.String #> [CmdletBinding()] [OutputType([string])] param() if ($PSVersionTable.PSEdition -eq 'Desktop') { return 'Windows' } if (Get-Variable -Name IsWindows -ValueOnly -ErrorAction SilentlyContinue) { return 'Windows' } if (Get-Variable -Name IsLinux -ValueOnly -ErrorAction SilentlyContinue) { return 'Linux' } if (Get-Variable -Name IsMacOS -ValueOnly -ErrorAction SilentlyContinue) { return 'macOS' } return 'Unknown' } function Write-UpdateAllTheThingsProgress { <# .SYNOPSIS Writes progress for Update-AllTheThings. .DESCRIPTION Centralizes the parent progress record used by Update-AllTheThings and its private update helpers. .PARAMETER CurrentOperation The operation currently being performed. .PARAMETER PercentComplete The percentage of the overall update that is complete. .PARAMETER Completed Completes and removes the progress record. .OUTPUTS None #> [CmdletBinding()] param( [Parameter()] [ValidateNotNullOrEmpty()] [string]$CurrentOperation, [Parameter()] [ValidateRange(0, 100)] [int]$PercentComplete = 0, [Parameter()] [switch]$Completed ) if ($Completed) { Write-Progress -Id 0 -Activity 'Update Everything' -Completed return } $ProgressParameters = @{ Id = 0 Activity = 'Update Everything' CurrentOperation = $CurrentOperation Status = "Progress: $PercentComplete`% Complete" PercentComplete = $PercentComplete } Write-Progress @ProgressParameters } function Invoke-UpdateNativeCommand { <# .SYNOPSIS Invokes a native update command and validates its exit code. .DESCRIPTION Provides consistent invocation and explicit failure handling for native package-manager commands. .PARAMETER Name The native command to invoke. .PARAMETER ArgumentList Arguments passed to the native command. .PARAMETER FailureMessage Context included in the terminating error when the command fails. .PARAMETER Command An invocation seam used by unit tests. .OUTPUTS None #> [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [AllowEmptyCollection()] [string[]]$ArgumentList = @(), [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$FailureMessage, [Parameter(DontShow)] [ValidateNotNull()] [scriptblock]$Command = { param($CommandName, $CommandArguments) & $CommandName @CommandArguments | Out-Host return $LASTEXITCODE } ) $ExitCode = & $Command $Name $ArgumentList if ($ExitCode -ne 0) { throw "$FailureMessage failed with exit code $ExitCode." } } function Get-CimOperatingSystemCaption { <# .SYNOPSIS Gets the operating-system caption through CIM. .DESCRIPTION Queries CIM_OperatingSystem and returns its caption. .OUTPUTS System.String #> [CmdletBinding()] [OutputType([string])] param( [Parameter(DontShow)] [ValidateNotNull()] [scriptblock]$Query = { (Get-CimInstance -ClassName CIM_OperatingSystem -ErrorAction Stop).Caption } ) return & $Query } function Get-WmiOperatingSystemCaption { <# .SYNOPSIS Gets the operating-system caption through WMI. .DESCRIPTION Queries Win32_OperatingSystem and returns its caption for Windows PowerShell 5.1 environments where CimCmdlets are unavailable. .OUTPUTS System.String #> [CmdletBinding()] [OutputType([string])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingWMICmdlet', '', Justification = 'Get-WmiObject is required as a Windows PowerShell 5.1 fallback when CimCmdlets are unavailable.' )] param( [Parameter(DontShow)] [ValidateNotNull()] [scriptblock]$Query = { (Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop).Caption } ) return & $Query } function Get-WindowsOperatingSystemCaption { <# .SYNOPSIS Gets the Windows operating-system caption. .DESCRIPTION Uses CIM when available and falls back to WMI for Windows PowerShell 5.1 environments where the CimCmdlets module is unavailable. .OUTPUTS System.String #> [CmdletBinding()] [OutputType([string])] param() if (Test-PSPreworkoutCommand -Name 'Get-CimInstance') { try { return Get-CimOperatingSystemCaption } catch { Write-Verbose 'Get-CimInstance failed while checking the Windows operating system caption.' } } if (Test-PSPreworkoutCommand -Name 'Get-WmiObject') { try { return Get-WmiOperatingSystemCaption } catch { Write-Verbose 'Get-WmiObject failed while checking the Windows operating system caption.' } } } function Read-WinGetServerChoice { <# .SYNOPSIS Prompts before updating packages on Windows Server. .DESCRIPTION Shows a host choice prompt that defaults to declining WinGet package updates. .OUTPUTS System.Int32 #> [CmdletBinding()] [OutputType([int])] param( [Parameter(DontShow)] [ValidateNotNull()] [object]$HostUserInterface = $Host.UI ) $Options = [System.Management.Automation.Host.ChoiceDescription[]]@( [System.Management.Automation.Host.ChoiceDescription]::new('&Yes', 'Continue with WinGet package updates.') [System.Management.Automation.Host.ChoiceDescription]::new('&No', 'Skip WinGet package updates.') ) return $HostUserInterface.PromptForChoice( 'Windows Server OS Found', "Do you want to run 'winget upgrade' on your server?", $Options, 1 ) } function Invoke-WinGetUpgrade { <# .SYNOPSIS Upgrades all user-scoped WinGet packages. .DESCRIPTION Invokes WinGet non-interactively and throws when WinGet reports a failure. .OUTPUTS None #> [CmdletBinding()] param( [Parameter(DontShow)] [ValidateNotNull()] [scriptblock]$Command = { & winget upgrade --silent --scope user --accept-package-agreements --accept-source-agreements --all | Out-Host return $LASTEXITCODE } ) $ExitCode = & $Command if ($ExitCode -ne 0) { throw "WinGet package upgrade failed with exit code $ExitCode." } } function Invoke-GitHubCliExtensionUpgrade { <# .SYNOPSIS Upgrades installed GitHub CLI extensions. .DESCRIPTION Invokes the GitHub CLI extension upgrade command and throws when it fails. .OUTPUTS None #> [CmdletBinding()] param( [Parameter(DontShow)] [ValidateNotNull()] [scriptblock]$Command = { & gh extension upgrade --all | Out-Host return $LASTEXITCODE } ) $ExitCode = & $Command if ($ExitCode -ne 0) { throw "GitHub CLI extension upgrade failed with exit code $ExitCode." } } function Invoke-GitHubCopilotCliUpdate { <# .SYNOPSIS Updates the GitHub Copilot CLI. .DESCRIPTION Invokes the GitHub Copilot CLI update command and throws when it fails. .OUTPUTS None #> [CmdletBinding()] param( [Parameter(DontShow)] [ValidateNotNull()] [scriptblock]$Command = { & copilot update | Out-Host return $LASTEXITCODE } ) $ExitCode = & $Command if ($ExitCode -ne 0) { throw "GitHub Copilot CLI update failed with exit code $ExitCode." } } function Set-UpdateRepositoryTrust { <# .SYNOPSIS Trusts supported PowerShell Gallery repositories. .DESCRIPTION Configures PSGallery as trusted for the installed PowerShellGet and PSResourceGet clients. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param() Write-Verbose 'Set the PowerShell Gallery as a trusted installation source.' if ( (Test-PSPreworkoutCommand -Name 'Set-PSRepository') -and $PSCmdlet.ShouldProcess('PSGallery', 'Set PowerShellGet repository as trusted') ) { Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted } if ( (Test-PSPreworkoutCommand -Name 'Set-PSResourceRepository') -and $PSCmdlet.ShouldProcess('PSGallery', 'Set PSResourceGet repository as trusted') ) { Set-PSResourceRepository -Name 'PSGallery' -Trusted } } function Update-PowerShellArtifact { <# .SYNOPSIS Updates installed PowerShell modules, scripts, and help. .DESCRIPTION Handles PowerShell-native update operations and their progress reporting. .PARAMETER SkipModules Skips installed module updates. .PARAMETER SkipScripts Skips installed script updates. .PARAMETER SkipHelp Skips PowerShell help updates. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] [switch]$SkipModules, [Parameter()] [switch]$SkipScripts, [Parameter()] [switch]$SkipHelp ) Write-UpdateAllTheThingsProgress -CurrentOperation 'Getting Installed PowerShell Modules' -PercentComplete 1 if ($SkipModules) { Write-Information '[1] Skipping PowerShell Modules' -InformationAction Continue } elseif (-not (Test-PSPreworkoutCommand -Name 'Get-InstalledModule')) { Write-Warning 'Get-InstalledModule was not found. Skipping PowerShell module updates.' } else { Write-Information '[1] Getting Installed PowerShell Modules' -InformationAction Continue $Modules = @(Get-InstalledModule) Write-Information "[2] Updating $($Modules.Count) PowerShell Modules" -InformationAction Continue $ModuleIndex = 0 foreach ($Module in $Modules) { ++$ModuleIndex $ModulePercent = [math]::Ceiling(($ModuleIndex / $Modules.Count) * 100) $OverallPercent = [math]::Ceiling(10 + (60 * ($ModulePercent / 100))) Write-UpdateAllTheThingsProgress -CurrentOperation "Updating PowerShell module $($Module.Name)" -PercentComplete $OverallPercent Write-Progress -Id 1 -ParentId 0 -Activity 'Updating PowerShell Modules' ` -CurrentOperation $Module.Name -Status "Progress: $ModulePercent`% Complete" ` -PercentComplete $ModulePercent if ($Module.Version -match 'alpha|beta|prerelease|preview') { Write-Information "`t`tSkipping $($Module.Name) because a prerelease version is currently installed." -InformationAction Continue continue } try { if ($PSCmdlet.ShouldProcess($Module.Name, 'Update PowerShell module')) { Update-Module -Name $Module.Name } } catch [Microsoft.PowerShell.Commands.WriteErrorException] { Write-Warning "Unable to update PowerShell module '$($Module.Name)': $($_.Exception.Message)" } } } Write-Progress -Id 1 -Activity 'Updating PowerShell Modules' -Completed if ($SkipScripts) { Write-Information '[2] Skipping PowerShell Scripts' -InformationAction Continue } elseif (-not (Test-PSPreworkoutCommand -Name 'Update-Script')) { Write-Warning 'Update-Script was not found. Skipping PowerShell script updates.' } else { Write-Information '[2] Updating PowerShell Scripts' -InformationAction Continue if ($PSCmdlet.ShouldProcess('Installed PowerShell scripts', 'Update scripts')) { Update-Script } } Write-UpdateAllTheThingsProgress -CurrentOperation 'Updating PowerShell Help' -PercentComplete 70 if ($SkipHelp) { Write-Information '[3] Skipping PowerShell Help' -InformationAction Continue } else { Write-Information '[3] Updating PowerShell Help' -InformationAction Continue if ($PSCmdlet.ShouldProcess('PowerShell help files', 'Update help')) { if ((Get-Culture).LCID -eq 127) { Update-Help -UICulture en-US -ErrorAction SilentlyContinue } else { Update-Help -ErrorAction SilentlyContinue } } } } function Update-WinGetPackage { <# .SYNOPSIS Updates user-scoped WinGet packages. .DESCRIPTION Applies Windows Server safeguards before invoking WinGet. .PARAMETER Skip Skips WinGet package updates. .PARAMETER AcceptPrompts Bypasses the additional Windows Server confirmation prompt. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] [switch]$Skip, [Parameter()] [switch]$AcceptPrompts ) if ($Skip) { Write-Information '[4] Skipping WinGet' -InformationAction Continue return } if (-not (Test-PSPreworkoutCommand -Name 'winget')) { Write-Information '[4] WinGet was not found. Skipping WinGet update.' -InformationAction Continue return } $ShouldUpdate = $PSCmdlet.ShouldProcess('WinGet packages', 'Upgrade all user-scoped packages') $SkipServerPrompt = $AcceptPrompts -or $WhatIfPreference -or ( $PSBoundParameters.ContainsKey('Confirm') -and (-not $Confirm) ) $ShouldProcessHandlesConsent = ($PSBoundParameters.ContainsKey('Confirm') -and $Confirm) -or ( ($ConfirmPreference -ne [System.Management.Automation.ConfirmImpact]::None) -and (([System.Management.Automation.ConfirmImpact]$ConfirmPreference) -le [System.Management.Automation.ConfirmImpact]::Medium) ) $NeedServerPrompt = (-not $SkipServerPrompt) -and (-not $ShouldProcessHandlesConsent) if ($NeedServerPrompt) { $WindowsOsCaption = Get-WindowsOperatingSystemCaption if ([string]::IsNullOrWhiteSpace($WindowsOsCaption)) { Write-Warning -Message 'Unable to determine the Windows operating system caption. Skipping WinGet updates as a safety precaution.' return } if ($ShouldUpdate -and ($WindowsOsCaption -match 'Server')) { Write-Warning -Message 'This is a server and updates could affect production systems. Do you want to continue with updating packages?' if ((Read-WinGetServerChoice) -ne 0) { return } Write-Verbose 'Continuing with WinGet package updates.' } } if ($ShouldUpdate -or $WhatIfPreference) { Write-Information '[4] Updating WinGet Packages' -InformationAction Continue Write-UpdateAllTheThingsProgress -CurrentOperation 'Updating WinGet Packages' -PercentComplete 80 } if ($ShouldUpdate) { Invoke-WinGetUpgrade } } function Update-LinuxPackage { <# .SYNOPSIS Updates packages with supported Linux package managers. .DESCRIPTION Updates apt and dnf packages, using sudo only when the current process is not elevated. .PARAMETER AcceptPrompts Passes the non-interactive acceptance argument to package managers. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] [switch]$AcceptPrompts ) $AptAvailable = Test-PSPreworkoutCommand -Name 'apt' $DnfAvailable = Test-PSPreworkoutCommand -Name 'dnf' if ((-not $AptAvailable) -and (-not $DnfAvailable)) { return } $NeedsSudo = -not (Test-IsElevated) if ($AptAvailable) { Write-Information '[5] Updating apt packages.' -InformationAction Continue if ($PSCmdlet.ShouldProcess('apt packages', 'Update and upgrade packages')) { $CommandName = if ($NeedsSudo) { 'sudo' } else { 'apt' } $UpdateArguments = if ($NeedsSudo) { @('apt', 'update') } else { @('update') } [string[]]$UpgradeArguments = if ($NeedsSudo) { @('apt', 'upgrade') } else { @('upgrade') } if ($AcceptPrompts) { $UpgradeArguments += '-y' } Invoke-UpdateNativeCommand -Name $CommandName -ArgumentList $UpdateArguments -FailureMessage 'apt package index update' Invoke-UpdateNativeCommand -Name $CommandName -ArgumentList $UpgradeArguments -FailureMessage 'apt package upgrade' } } if ($DnfAvailable) { Write-Information '[5] Updating dnf packages.' -InformationAction Continue if ($PSCmdlet.ShouldProcess('dnf packages', 'Update packages')) { $CommandName = if ($NeedsSudo) { 'sudo' } else { 'dnf' } [string[]]$Arguments = if ($NeedsSudo) { @('dnf', 'update') } else { @('update') } if ($AcceptPrompts) { $Arguments += '-y' } Invoke-UpdateNativeCommand -Name $CommandName -ArgumentList $Arguments -FailureMessage 'dnf package update' } } } function Update-MacOSPackage { <# .SYNOPSIS Updates supported macOS software. .DESCRIPTION Lists macOS software updates and updates Homebrew packages when Homebrew is installed. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param() if ($PSCmdlet.ShouldProcess('macOS software updates', 'List available updates')) { Invoke-UpdateNativeCommand -Name 'softwareupdate' -ArgumentList '-l' -FailureMessage 'macOS software update query' } if (Test-PSPreworkoutCommand -Name 'brew') { Write-Information '[6] Updating brew packages.' -InformationAction Continue if ($PSCmdlet.ShouldProcess('Homebrew packages', 'Update and upgrade packages')) { Invoke-UpdateNativeCommand -Name 'brew' -ArgumentList 'update' -FailureMessage 'Homebrew update' Invoke-UpdateNativeCommand -Name 'brew' -ArgumentList 'upgrade' -FailureMessage 'Homebrew upgrade' } } } function Update-OptionalCli { <# .SYNOPSIS Updates an optional command-line tool. .DESCRIPTION Runs a supplied update command when its command-line tool is installed. .PARAMETER CommandName The command used to detect whether the tool is installed. .PARAMETER DisplayName The status text displayed to the user. .PARAMETER CurrentOperation The operation shown in the progress record. .PARAMETER TargetName The ShouldProcess target. .PARAMETER ActionName The ShouldProcess action. .PARAMETER PercentComplete The overall completion percentage. .PARAMETER UpdateCommand The update operation to invoke. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$CommandName, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$DisplayName, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$CurrentOperation, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$TargetName, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$ActionName, [Parameter(Mandatory)] [ValidateRange(0, 100)] [int]$PercentComplete, [Parameter(Mandatory)] [ValidateNotNull()] [scriptblock]$UpdateCommand ) if (-not (Test-PSPreworkoutCommand -Name $CommandName)) { Write-Verbose "$DisplayName was skipped because $CommandName was not found." return } Write-Information $DisplayName -InformationAction Continue Write-UpdateAllTheThingsProgress -CurrentOperation $CurrentOperation -PercentComplete $PercentComplete if ($PSCmdlet.ShouldProcess($TargetName, $ActionName)) { & $UpdateCommand } } function Update-ChocolateyPackage { <# .SYNOPSIS Updates Chocolatey and installed Chocolatey packages. .DESCRIPTION Optionally configures Chocolatey features when elevated, then updates Chocolatey and all installed packages. .PARAMETER Include Enables Chocolatey package updates. .OUTPUTS None #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] [switch]$Include ) if ((-not $Include) -or (-not (Test-PSPreworkoutCommand -Name 'choco'))) { Write-Information '[9] Skipping Chocolatey' -InformationAction Continue return } Write-UpdateAllTheThingsProgress -CurrentOperation 'Updating Chocolatey Packages' -PercentComplete 98 Write-Information '[9] Updating Chocolatey Packages' -InformationAction Continue if (Test-IsElevated) { if ($PSCmdlet.ShouldProcess('Chocolatey features', 'Enable global confirmation and disable non-elevated warnings')) { Invoke-UpdateNativeCommand -Name 'choco' -ArgumentList @( 'feature', 'enable', '-n=allowGlobalConfirmation' ) -FailureMessage 'Chocolatey global confirmation configuration' Invoke-UpdateNativeCommand -Name 'choco' -ArgumentList @( 'feature', 'disable', '--name=showNonElevatedWarnings' ) -FailureMessage 'Chocolatey non-elevated warning configuration' } } else { Write-Verbose "Run once as an administrator to disable Chocolatey's showNonElevatedWarnings." -Verbose } if ($PSCmdlet.ShouldProcess('Chocolatey packages', 'Upgrade Chocolatey and all packages')) { $UpgradeArguments = @('-y', '--limit-output', '--accept-license', '--no-color') $SelfUpdateArguments = @('upgrade', 'chocolatey') + $UpgradeArguments $PackageUpdateArguments = @('upgrade', 'all') + $UpgradeArguments Invoke-UpdateNativeCommand -Name 'choco' -ArgumentList $SelfUpdateArguments -FailureMessage 'Chocolatey self-update' Invoke-UpdateNativeCommand -Name 'choco' -ArgumentList $PackageUpdateArguments -FailureMessage 'Chocolatey package update' } Write-Information ' ' -InformationAction Continue } function Test-IsElevated { <# .SYNOPSIS Check if you are running an elevated shell with administrator or root privileges. .DESCRIPTION Check if you are running an elevated shell with administrator or root privileges. .EXAMPLE Test-IsElevated .OUTPUTS Boolean #> [CmdletBinding(HelpUri = 'https://day3bits.com/PSPreworkout/Test-IsElevated')] [Alias('isadmin', 'isroot')] [OutputType([bool])] param () # The standalone Update-AllTheThings script does not include module telemetry. if (Get-Command -Name Write-PSPreworkoutTelemetry -CommandType Function -ErrorAction SilentlyContinue) { Write-PSPreworkoutTelemetry -EventName $MyInvocation.MyCommand.Name -ParameterNamesOnly $MyInvocation.BoundParameters.Keys } if (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) { $CurrentUser = [Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent()) return $CurrentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } else { # Unix-like systems (Linux, macOS, etc.) # Method 1: Try id command with error handling try { $userId = & id -u 2>$null if ($LASTEXITCODE -eq 0 -and $null -ne $userId) { return 0 -eq [int]$userId } } catch { Write-Debug "id command failed: $($_.Exception.Message)" } # Method 2: Check username via .NET try { if ([System.Environment]::UserName -eq 'root') { return $true } } catch { Write-Debug ".NET username check failed: $($_.Exception.Message)" } # Method 3: Check for macOS admin group membership try { # Check if we're on macOS and if user is in admin or wheel groups if ($IsMacOS -or (Get-Command 'sw_vers' -ErrorAction SilentlyContinue)) { # Method 3a: Use groups command to check admin group membership $groups = & groups 2>$null if ($LASTEXITCODE -eq 0 -and $groups) { $groupList = $groups -split '\s+' if ($groupList -contains 'admin' -or $groupList -contains 'wheel') { return $true } } # Method 3b: Use id command to check group IDs $groupIds = & id -G 2>$null if ($LASTEXITCODE -eq 0 -and $groupIds) { $gidList = $groupIds -split '\s+' | ForEach-Object { [int]$_ } if ($gidList -contains 80 -or $gidList -contains 0) { # 80=admin, 0=wheel return $true } } } } catch { Write-Debug "macOS admin group check failed: $($_.Exception.Message)" } # Method 4: Check effective user ID via /proc (Linux-specific) try { if (Test-Path '/proc/self/status') { $statusContent = Get-Content '/proc/self/status' -ErrorAction SilentlyContinue $uidLine = $statusContent | Where-Object { $_ -match '^Uid:' } if ($uidLine -and $uidLine -match '\s+(\d+)\s+') { return 0 -eq [int]$matches[1] } } } catch { Write-Debug "/proc status check failed: $($_.Exception.Message)" } # All methods failed Write-Warning 'Unable to determine elevation status on this Unix-like system. All detection methods failed.' return $false } } function Update-AllTheThings { <# .SYNOPSIS Update all the things! .DESCRIPTION A script to automatically update all PowerShell modules, PowerShell Help, GitHub CLI extensions, GitHub Copilot CLI, and packages (apt, brew, Chocolatey, WinGet). .PARAMETER SkipModules Skip the step that updates PowerShell modules. .PARAMETER SkipScripts Skip the step that updates PowerShell scripts. .PARAMETER SkipHelp Skip the step that updates PowerShell help. .PARAMETER SkipWinGet Skip the step that updates WinGet packages. .PARAMETER IncludeChocolatey Include Chocolatey package updates. .PARAMETER AcceptPrompts Automatically accept prompts to install updates in Linux (apt, dnf) and continue WinGet updates on Windows Server without showing the extra server confirmation prompt. .EXAMPLE Update-AllTheThings Updates all of the things it can! .EXAMPLE Update-AllTheThings -AcceptPrompts Updates all of the things and automatically accepts Linux package upgrade prompts and the additional WinGet confirmation prompt on Windows Server. .NOTES Author: Sam Erde Version: 0.6.0 #> [CmdletBinding( SupportsShouldProcess, ConfirmImpact = 'Medium', HelpUri = 'https://day3bits.com/PSPreworkout/Update-AllTheThings' )] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'This is what we do.')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Interactive Use')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'WhatIf and Confirm are forwarded to private helpers that call ShouldProcess.')] [Alias('uatt')] param ( # Skip the step that updates PowerShell modules [Parameter()] [switch]$SkipModules, # Skip the step that updates PowerShell scripts [Parameter()] [switch]$SkipScripts, # Skip the step that updates PowerShell help [Parameter()] [switch]$SkipHelp, # Skip the step that updates WinGet packages [Parameter()] [switch]$SkipWinGet, # Include Chocolatey package updates [Parameter()] [Alias('SkipChoco')] [switch]$IncludeChocolatey, # Automatically accept prompts to install updates [Parameter()] [switch]$AcceptPrompts ) begin { if (Test-PSPreworkoutCommand -Name 'Write-PSPreworkoutTelemetry') { Write-PSPreworkoutTelemetry -EventName $MyInvocation.MyCommand.Name -ParameterNamesOnly $MyInvocation.BoundParameters.Keys } $Banner = @" __ __ __ __ ___ ____ / / / /__ ___/ /__ _/ /____ / _ | / / / / /_/ / _ \/ _ / _ `/ __/ -_) / __ |/ / / \____/ .__/\_,_/\_,_/\__/\__/ /_/ |_/_/_/ ___/_/__ ________ _ /_ __/ / ___ /_ __/ / (_)__ ___ ____ / / / _ \/ -_) / / / _ \/ / _ \/ _ `(_-< /_/ /_//_/\__/ /_/ /_//_/_/_//_/\_, /___/ /___/ v0.6.0 "@ Write-Host $Banner } process { $CommonParameters = @{} foreach ($CommonParameterName in 'WhatIf', 'Confirm') { if ($PSBoundParameters.ContainsKey($CommonParameterName)) { $CommonParameters[$CommonParameterName] = $PSBoundParameters[$CommonParameterName] } } $Platform = Get-PSPreworkoutPlatform Set-UpdateRepositoryTrust @CommonParameters Update-PowerShellArtifact -SkipModules:$SkipModules -SkipScripts:$SkipScripts -SkipHelp:$SkipHelp @CommonParameters if ($Platform -eq 'Windows') { Update-WinGetPackage -Skip:$SkipWinGet -AcceptPrompts:$AcceptPrompts @CommonParameters } else { Write-Verbose '[4] Not Windows. Skipping WinGet.' } if ($Platform -eq 'Linux') { Update-LinuxPackage -AcceptPrompts:$AcceptPrompts @CommonParameters } else { Write-Verbose '[5] Not Linux. Skipping section.' } if ($Platform -eq 'macOS') { Update-MacOSPackage @CommonParameters } else { Write-Verbose '[6] Not macOS. Skipping section.' } $OptionalCliUpdates = @( @{ CommandName = 'gh' DisplayName = '[7] Updating GitHub CLI Extensions' CurrentOperation = 'Updating GitHub CLI Extensions' TargetName = 'GitHub CLI extensions' ActionName = 'Upgrade all installed extensions' PercentComplete = 90 UpdateCommand = { Invoke-GitHubCliExtensionUpgrade } } @{ CommandName = 'copilot' DisplayName = '[8] Updating GitHub Copilot CLI' CurrentOperation = 'Updating GitHub Copilot CLI' TargetName = 'GitHub Copilot CLI' ActionName = 'Update installed CLI' PercentComplete = 95 UpdateCommand = { Invoke-GitHubCopilotCliUpdate } } ) foreach ($CliUpdate in $OptionalCliUpdates) { Update-OptionalCli @CliUpdate @CommonParameters } Update-ChocolateyPackage -Include:$IncludeChocolatey @CommonParameters } end { Write-Host 'Done.' Write-UpdateAllTheThingsProgress -CurrentOperation 'Finished' -PercentComplete 100 Write-UpdateAllTheThingsProgress -Completed } } |