docs: Update README and documentation for lxs multi-tool across Linux and Windows platforms.
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
<#
|
||||
LXS - Update Windows
|
||||
Description: Install pending Windows updates and upgrade every winget
|
||||
package. Mirror of linux/tools/update-server.sh.
|
||||
Repo: https://git.hyko.cx/hykocx/lxs
|
||||
|
||||
Usage: update-windows.ps1 [-Yes] [-NoWinget] [-NoWindowsUpdate] [-Help]
|
||||
#>
|
||||
|
||||
# Load LXS common library (colors, UI helpers, spinner, loggers, guards).
|
||||
# Prefers the sibling ..\lib\common.ps1 (repo checkout or installed layout) and
|
||||
# only hits the network when this script is run standalone.
|
||||
$LxsRawBase = if ($env:LXS_RAW_BASE) { $env:LXS_RAW_BASE } else { 'https://git.hyko.cx/hykocx/lxs/raw/branch/main' }
|
||||
if (-not $env:LXS_RAW_PLATFORM_BASE) { $env:LXS_RAW_PLATFORM_BASE = "$LxsRawBase/windows" }
|
||||
$LxsLibPath = if ($PSScriptRoot) { Join-Path $PSScriptRoot '..\lib\common.ps1' } else { $null }
|
||||
if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
|
||||
. $LxsLibPath
|
||||
} else {
|
||||
try {
|
||||
$LxsLibSource = Invoke-RestMethod -Uri "$env:LXS_RAW_PLATFORM_BASE/lib/common.ps1" -UseBasicParsing -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Error 'Failed to fetch lib/common.ps1'
|
||||
exit 1
|
||||
}
|
||||
. ([scriptblock]::Create($LxsLibSource))
|
||||
}
|
||||
|
||||
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_update_windows.log'
|
||||
|
||||
$LxsOpts = Read-LxsFlags -Arguments $args -Known @(
|
||||
'-Yes', '-y', '-NoWinget', '-NoWindowsUpdate', '-Help', '-h'
|
||||
)
|
||||
if (Show-LxsUnknownFlags $LxsOpts) { exit 1 }
|
||||
$AssumeYes = Test-LxsFlag $LxsOpts @('-Yes', '-y')
|
||||
$NoWinget = Test-LxsFlag $LxsOpts @('-NoWinget')
|
||||
$NoWindowsUpdate = Test-LxsFlag $LxsOpts @('-NoWindowsUpdate')
|
||||
|
||||
if (Test-LxsFlag $LxsOpts @('-Help', '-h')) {
|
||||
Write-Host @'
|
||||
Usage: update-windows.ps1 [options]
|
||||
|
||||
Options:
|
||||
-Yes, -y Skip the confirmation prompt
|
||||
-NoWinget Skip `winget upgrade --all`
|
||||
-NoWindowsUpdate Skip Windows Update
|
||||
-Help, -h Show this help
|
||||
'@
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not (Assert-LxsWindows)) { exit 1 }
|
||||
if (-not $NoWindowsUpdate) { Assert-LxsAdmin -ScriptPath $PSCommandPath -Arguments $args }
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Windows Update via the built-in COM API. Deliberately not PSWindowsUpdate:
|
||||
# that module has to be pulled from the PowerShell Gallery first, which fails
|
||||
# on locked-down or offline machines. The COM surface ships with Windows.
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function Invoke-LxsWindowsUpdate {
|
||||
Write-Host ''
|
||||
Show-LxsBoxMid 'WINDOWS UPDATE'
|
||||
Write-Host ''
|
||||
|
||||
try {
|
||||
$session = New-Object -ComObject Microsoft.Update.Session
|
||||
$searcher = $session.CreateUpdateSearcher()
|
||||
} catch {
|
||||
Write-LxsErr "Windows Update API unavailable: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-LxsInfo 'Searching for updates (this can take several minutes)...'
|
||||
try {
|
||||
$result = $searcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
|
||||
} catch {
|
||||
Write-LxsErr "Search failed: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
$updates = @($result.Updates)
|
||||
if ($updates.Count -eq 0) {
|
||||
Write-LxsOk 'No pending updates'
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-LxsOk "$($updates.Count) update(s) available:"
|
||||
Write-Host ''
|
||||
foreach ($u in $updates) {
|
||||
$sizeMB = [math]::Round($u.MaxDownloadSize / 1MB, 1)
|
||||
Write-Host " $($script:Gray)-$($script:NC) $($u.Title) $($script:Gray)(${sizeMB} MB)$($script:NC)"
|
||||
}
|
||||
Write-Host ''
|
||||
|
||||
if (-not (Confirm-LxsAction -Question 'Download and install these updates?' -DefaultYes -AssumeYes:$AssumeYes)) {
|
||||
Write-LxsInfo 'Skipped.'
|
||||
return $true
|
||||
}
|
||||
|
||||
# EULAs must be accepted per-update before an unattended install.
|
||||
$toInstall = New-Object -ComObject Microsoft.Update.UpdateColl
|
||||
foreach ($u in $updates) {
|
||||
if (-not $u.EulaAccepted) {
|
||||
try { $u.AcceptEula() } catch { Write-LxsWarn "Could not accept the EULA for: $($u.Title)" }
|
||||
}
|
||||
[void]$toInstall.Add($u)
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-LxsInfo 'Downloading...'
|
||||
try {
|
||||
$downloader = $session.CreateUpdateDownloader()
|
||||
$downloader.Updates = $toInstall
|
||||
$dlResult = $downloader.Download()
|
||||
if ($dlResult.ResultCode -ne 2) {
|
||||
Write-LxsWarn "Download finished with result code $($dlResult.ResultCode) (2 = success)."
|
||||
} else {
|
||||
Write-LxsOk 'Download complete'
|
||||
}
|
||||
} catch {
|
||||
Write-LxsErr "Download failed: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-LxsInfo 'Installing...'
|
||||
try {
|
||||
$installer = $session.CreateUpdateInstaller()
|
||||
$installer.Updates = $toInstall
|
||||
$instResult = $installer.Install()
|
||||
} catch {
|
||||
Write-LxsErr "Install failed: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
for ($i = 0; $i -lt $toInstall.Count; $i++) {
|
||||
$code = $instResult.GetUpdateResult($i).ResultCode
|
||||
$title = $toInstall.Item($i).Title
|
||||
if ($code -eq 2) { Write-LxsOk $title } else { Write-LxsWarn "$title (result code $code)" }
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
if ($instResult.RebootRequired) {
|
||||
Write-LxsWarn 'A reboot is required to finish installing these updates.'
|
||||
} else {
|
||||
Write-LxsOk 'Windows Update finished'
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# winget
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function Invoke-LxsWingetUpgrade {
|
||||
Write-Host ''
|
||||
Show-LxsBoxMid 'WINGET'
|
||||
Write-Host ''
|
||||
|
||||
if (-not (Assert-LxsWinget)) { return $false }
|
||||
|
||||
Write-LxsInfo 'Packages with an available upgrade:'
|
||||
Write-Host ''
|
||||
& winget upgrade --include-unknown --accept-source-agreements | Out-String | Write-Host
|
||||
|
||||
if (-not (Confirm-LxsAction -Question 'Upgrade all of them?' -DefaultYes -AssumeYes:$AssumeYes)) {
|
||||
Write-LxsInfo 'Skipped.'
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
& winget upgrade --all --include-unknown --silent `
|
||||
--accept-package-agreements --accept-source-agreements --disable-interactivity
|
||||
$code = $LASTEXITCODE
|
||||
|
||||
Write-Host ''
|
||||
if ($code -eq 0) {
|
||||
Write-LxsOk 'winget upgrade completed'
|
||||
return $true
|
||||
}
|
||||
# -1978335189 = no applicable upgrade found; nothing actually went wrong.
|
||||
if ($code -eq -1978335189) {
|
||||
Write-LxsOk 'Everything is already up to date'
|
||||
return $true
|
||||
}
|
||||
Write-LxsWarn "winget exited with code $code"
|
||||
return $false
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Run
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Clear-Host
|
||||
Show-LxsBoxTop -Title 'UPDATE WINDOWS'
|
||||
Write-Host ''
|
||||
Write-Host 'The following will run on this machine:'
|
||||
if (-not $NoWindowsUpdate) { Write-Host ' - Windows Update: search, download and install pending updates' }
|
||||
if (-not $NoWinget) { Write-Host ' - winget: upgrade every package with a newer version' }
|
||||
Write-Host ''
|
||||
Show-LxsSeparator
|
||||
|
||||
if (-not (Confirm-LxsAction -Question 'Continue?' -DefaultYes -AssumeYes:$AssumeYes)) {
|
||||
Write-LxsInfo 'Cancelled.'
|
||||
exit 0
|
||||
}
|
||||
|
||||
$failed = $false
|
||||
if (-not $NoWindowsUpdate) { if (-not (Invoke-LxsWindowsUpdate)) { $failed = $true } }
|
||||
if (-not $NoWinget) { if (-not (Invoke-LxsWingetUpgrade)) { $failed = $true } }
|
||||
|
||||
Write-Host ''
|
||||
Show-LxsSeparator
|
||||
Write-Host ''
|
||||
if ($failed) {
|
||||
Write-LxsWarn 'Update pass finished with warnings.'
|
||||
exit 1
|
||||
}
|
||||
Write-LxsOk 'Update pass finished'
|
||||
exit 0
|
||||
Reference in New Issue
Block a user