diff --git a/README.md b/README.md index 8377d6a..c38cff3 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ You can easily translate toast notifications by creating your locale xml config By default, scripts and components will be placed in ProgramData location (inside a Winget-AutoUpdate folder). You can change this with script argument (Not Recommended). ### When does the script run? -From version 1.9.0 (on new installations) WAU runs everyday at 6AM. You can now configure the frequency with `-UpdatesInterval` option (Daily, Weekly, Biweekly or Monthly). You can also add `-UpdatesAtLogon` parameter to run at user logon and keep this option activated like previous versions (recommanded). +From version 1.9.0 (on new installations) WAU runs everyday at 6AM. You can now configure the frequency with `-UpdatesInterval` option (Daily, BiDaily, Weekly, BiWeekly or Monthly). You can also add `-UpdatesAtLogon` parameter to run at user logon and keep this option activated like previous versions (recommanded). ### Log location You can find logs in install location, in logs folder. @@ -86,7 +86,20 @@ Disable Winget-AutoUpdate update checking. By default, WAU auto updates if new v Use White List instead of Black List. This setting will not create the "excluded_apps.txt" but "included_apps.txt" **-ListPath** -Get Black/White List from Path (URL/UNC/Local) (copy/download to Winget-AutoUpdate installation location if external list is newer). +Get Black/White List from Path (URL/UNC/Local) (download/copy to Winget-AutoUpdate installation location if external list is newer). + +**-ModsPath** +Get Mods from Path (URL/UNC/Local) (download/copy to `mods` in Winget-AutoUpdate installation location if external mods are newer). +For URL: This requires a site directory with `Options +Indexes` in `.htaccess` and no index page overriding the listing of files. +Or an index page with href listing of all the Mods to be downloaded: +``` + +``` **-InstallUserContext** Install WAU with system and **user** context executions (From version 1.15.3) @@ -110,7 +123,7 @@ Specify the Notification level: Full (Default, displays all notification), Succe Set WAU to run at user logon. **-UpdatesInterval** -Specify the update frequency: Daily (Default), Weekly, Biweekly, Monthly or Never. Can be set to 'Never' in combination with '-UpdatesAtLogon' for instance +Specify the update frequency: Daily (Default), BiDaily, Weekly, BiWeekly, Monthly or Never. Can be set to 'Never' in combination with '-UpdatesAtLogon' for instance **-UpdatesAtTime** Specify the time of the update interval execution time. Default 6AM. (From version 1.15.0) @@ -125,15 +138,19 @@ Remove scheduled tasks and scripts. See https://github.com/Romanitho/Winget-AutoUpdate/discussions/88 ## Custom scripts (Mods feature) -From version 1.8.0, the Mods feature allows you to run an additional script when upgrading or installing an app. -Just put the script in question with the App ID followed by the `-upgrade` or `-install` suffix in the "mods" folder. -WAU will call `AppID-upgrade.ps1` and/or `AppID-install.ps1` (if they differs, otherwise the "-install" mod will be used for upgrades too) if it exists in the "mods" folder just after the upgrade/install. +From version 1.8.0, the Mods feature allows you to run additional scripts when upgrading or installing an app. +Just put the scripts in question with the **AppID** followed by the `-preinstall`, `-upgrade`, `-install` or `-installed` suffix in the **mods** folder. +> Runs before upgrade/install: `AppID-preinstall.ps1` +> Runs during upgrade/install (before install check): `AppID-upgrade.ps1`/`AppID-install.ps1` +> Runs after upgrade/install has been confirmed: `AppID-installed.ps1` -> Example: -If you want to run a script that removes the shortcut from "%PUBLIC%\Desktop" (we don't want to fill the desktop with shortcuts our users can't delete) just after installing "Acrobat Reader DC" (32-bit), prepare a powershell script that removes the Public Desktop shortcut "Acrobat Reader DC.lnk" and name your script like this: -`Adobe.Acrobat.Reader.32-bit-install.ps1` and put it in the "mods" folder. +The **-install** mod will be used for upgrades too if **-upgrade** doesn't exist -You can find more information on Winget-Install Repo, as it's a related feature +> Example: +If you want to run a script that removes the shortcut from **%PUBLIC%\Desktop** (we don't want to fill the desktop with shortcuts our users can't delete) just after installing **Acrobat Reader DC** (32-bit), prepare a powershell script that removes the Public Desktop shortcut **Acrobat Reader DC.lnk** and name your script like this: +`Adobe.Acrobat.Reader.32-bit-installed.ps1` and put it in the **mods** folder. + +You can find more information on [Winget-Install Repo](https://github.com/Romanitho/Winget-Install#custom-mods), as it's a related feature ## Help In some cases, you need to "unblock" the `install.bat` file (Windows Defender SmartScreen). Right click, properties and unblock. Then, you'll be able to run it. diff --git a/Winget-AutoUpdate-Install.ps1 b/Winget-AutoUpdate-Install.ps1 index 4173e0f..2c2a1e0 100644 --- a/Winget-AutoUpdate-Install.ps1 +++ b/Winget-AutoUpdate-Install.ps1 @@ -25,6 +25,9 @@ Use White List instead of Black List. This setting will not create the "exclude_ .PARAMETER ListPath Get Black/White List from Path (URL/UNC/Local) +.PARAMETER ModsPath +Get mods from Path (URL/UNC/Local) + .PARAMETER Uninstall Remove scheduled tasks and scripts. @@ -44,7 +47,7 @@ Specify the Notification level: Full (Default, displays all notification), Succe Set WAU to run at user logon. .PARAMETER UpdatesInterval -Specify the update frequency: Daily (Default), Weekly, Biweekly, Monthly or Never +Specify the update frequency: Daily (Default), BiDaily, Weekly, BiWeekly, Monthly or Never .PARAMETER UpdatesAtTime Specify the time of the update interval execution time. Default 6AM @@ -65,7 +68,10 @@ Configure WAU to bypass the Black/White list when run in user context .\Winget-AutoUpdate-Install.ps1 -Silent -UseWhiteList .EXAMPLE -.\Winget-AutoUpdate-Install.ps1 -Silent -ListPath https://www.domain.com/WAULists -StartMenuShortcut +.\Winget-AutoUpdate-Install.ps1 -Silent -ListPath https://www.domain.com/WAULists -StartMenuShortcut -UpdatesInterval BiDaily + +.EXAMPLE +.\Winget-AutoUpdate-Install.ps1 -Silent -ModsPath https://www.domain.com/WAUMods -DesktopShortcut -UpdatesInterval Weekly .EXAMPLE .\Winget-AutoUpdate-Install.ps1 -Silent -UpdatesAtLogon -UpdatesInterval Weekly @@ -80,6 +86,7 @@ param( [Parameter(Mandatory = $False)] [Alias('S')] [Switch] $Silent = $false, [Parameter(Mandatory = $False)] [Alias('Path')] [String] $WingetUpdatePath = "$env:ProgramData\Winget-AutoUpdate", [Parameter(Mandatory = $False)] [Alias('List')] [String] $ListPath, + [Parameter(Mandatory = $False)] [Alias('Mods')] [String] $ModsPath, [Parameter(Mandatory = $False)] [Switch] $DoNotUpdate = $false, [Parameter(Mandatory = $False)] [Switch] $DisableWAUAutoUpdate = $false, [Parameter(Mandatory = $False)] [Switch] $RunOnMetered = $false, @@ -90,7 +97,7 @@ param( [Parameter(Mandatory = $False)] [Switch] $UseWhiteList = $false, [Parameter(Mandatory = $False)] [ValidateSet("Full", "SuccessOnly", "None")] [String] $NotificationLevel = "Full", [Parameter(Mandatory = $False)] [Switch] $UpdatesAtLogon = $false, - [Parameter(Mandatory = $False)] [ValidateSet("Daily", "Weekly", "BiWeekly", "Monthly", "Never")] [String] $UpdatesInterval = "Daily", + [Parameter(Mandatory = $False)] [ValidateSet("Daily", "BiDaily", "Weekly", "BiWeekly", "Monthly", "Never")] [String] $UpdatesInterval = "Daily", [Parameter(Mandatory = $False)] [DateTime] $UpdatesAtTime = ("06am"), [Parameter(Mandatory = $False)] [Switch] $BypassListForUsers = $false, [Parameter(Mandatory = $False)] [Switch] $InstallUserContext = $false @@ -265,6 +272,9 @@ function Install-WingetAutoUpdate { if ($UpdatesInterval -eq "Daily") { $tasktriggers += New-ScheduledTaskTrigger -Daily -At $UpdatesAtTime } + elseif ($UpdatesInterval -eq "BiDaily") { + $tasktriggers += New-ScheduledTaskTrigger -Daily -At $UpdatesAtTime -DaysInterval 2 + } elseif ($UpdatesInterval -eq "Weekly") { $tasktriggers += New-ScheduledTaskTrigger -Weekly -At $UpdatesAtTime -DaysOfWeek 2 } @@ -339,6 +349,9 @@ function Install-WingetAutoUpdate { if ($ListPath) { New-ItemProperty $regPath -Name WAU_ListPath -Value $ListPath -Force | Out-Null } + if ($ModsPath) { + New-ItemProperty $regPath -Name WAU_ModsPath -Value $ModsPath -Force | Out-Null + } if ($BypassListForUsers) { New-ItemProperty $regPath -Name WAU_BypassListForUsers -Value 1 -PropertyType DWord -Force | Out-Null } diff --git a/Winget-AutoUpdate/Winget-Upgrade.ps1 b/Winget-AutoUpdate/Winget-Upgrade.ps1 index 75d4517..cf79a12 100644 --- a/Winget-AutoUpdate/Winget-Upgrade.ps1 +++ b/Winget-AutoUpdate/Winget-Upgrade.ps1 @@ -76,7 +76,7 @@ if (Test-Network) { Write-Log "WAU uses External Lists from: $($WAUConfig.WAU_ListPath)" $NewList = Test-ListPath $WAUConfig.WAU_ListPath $WAUConfig.WAU_UseWhiteList $WAUConfig.InstallLocation if ($NewList) { - Write-Log "Newer List copied/downloaded to local path: $($WAUConfig.InstallLocation)" "Yellow" + Write-Log "Newer List downloaded/copied to local path: $($WAUConfig.InstallLocation)" "Yellow" } else { if ((Test-Path "$WorkingDir\included_apps.txt") -or (Test-Path "$WorkingDir\excluded_apps.txt")) { @@ -88,6 +88,26 @@ if (Test-Network) { } } } + + #Get External ModsPath if System + if ($WAUConfig.WAU_ModsPath) { + Write-Log "WAU uses External Mods from: $($WAUConfig.WAU_ModsPath)" + $NewMods, $DeletedMods = Test-ModsPath $WAUConfig.WAU_ModsPath $WAUConfig.InstallLocation + if ($NewMods -gt 0) { + Write-Log "$NewMods newer Mods downloaded/copied to local path: $($WAUConfig.InstallLocation)\mods" "Yellow" + } + else { + if (Test-Path "$WorkingDir\mods\*.ps1") { + Write-Log "Mods are up to date." "Green" + } + else { + Write-Log "No Mods are implemented..." "Yellow" + } + } + if ($DeletedMods -gt 0) { + Write-Log "$DeletedMods Mods deleted (not externally managed) from local path: $($WAUConfig.InstallLocation)\mods" "Red" + } + } } #Get White or Black list diff --git a/Winget-AutoUpdate/functions/Test-Mods.ps1 b/Winget-AutoUpdate/functions/Test-Mods.ps1 index b161847..1496ea2 100644 --- a/Winget-AutoUpdate/functions/Test-Mods.ps1 +++ b/Winget-AutoUpdate/functions/Test-Mods.ps1 @@ -3,11 +3,16 @@ function Test-Mods ($app) { #Takes care of a null situation - $ModsInstall = $null + $ModsPreInstall = $null $ModsUpgrade = $null + $ModsInstall = $null + $ModsInstalled = $null $Mods = "$WorkingDir\mods" if (Test-Path "$Mods\$app-*") { + if (Test-Path "$Mods\$app-preinstall.ps1") { + $ModsPreInstall = "$Mods\$app-preinstall.ps1" + } if (Test-Path "$Mods\$app-install.ps1") { $ModsInstall = "$Mods\$app-install.ps1" $ModsUpgrade = "$Mods\$app-install.ps1" @@ -15,8 +20,11 @@ function Test-Mods ($app) { if (Test-Path "$Mods\$app-upgrade.ps1") { $ModsUpgrade = "$Mods\$app-upgrade.ps1" } + if (Test-Path "$Mods\$app-installed.ps1") { + $ModsInstalled = "$Mods\$app-installed.ps1" + } } - return $ModsInstall, $ModsUpgrade + return $ModsPreInstall, $ModsUpgrade, $ModsInstall, $ModsInstalled } diff --git a/Winget-AutoUpdate/functions/Test-ModsPath.ps1 b/Winget-AutoUpdate/functions/Test-ModsPath.ps1 new file mode 100644 index 0000000..2ebc50d --- /dev/null +++ b/Winget-AutoUpdate/functions/Test-ModsPath.ps1 @@ -0,0 +1,102 @@ +#Function to check Mods External Path + +function Test-ModsPath ($ModsPath, $WingetUpdatePath) { + # URL, UNC or Local Path + # Get local and external Mods paths + $LocalMods = -join ($WingetUpdatePath, "\", "mods") + $ExternalMods = "$ModsPath" + + #Get File Names Locally + $InternalModsNames = Get-ChildItem -Path $LocalMods -Name -Recurse -Include *.ps1 + + # If path is URL + if ($ExternalMods -like "http*") { + $wc = New-Object System.Net.WebClient + + # enable TLS 1.2 and TLS 1.1 protocols + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12, [Net.SecurityProtocolType]::Tls11 + #Get Index of $ExternalMods (or index page with href listing of all the Mods) + try { + $WebResponse = Invoke-WebRequest -Uri $ExternalMods -UseBasicParsing + } + catch { + return $False + } + + # Collect the external list of href links + $ModLinks = $WebResponse.Links | Select-Object -ExpandProperty href + #Delete Local Mods that don't exist Externally + foreach ($Mod in $InternalModsNames) { + If ($Mod -notin $ModLinks) { + Remove-Item $LocalMods\$Mod -Force -ErrorAction SilentlyContinue | Out-Null + $DeletedMods++ + } + } + + #Loop through all links + $WebResponse.Links | Select-Object -ExpandProperty href | ForEach-Object { + #Check for .ps1 in listing/HREF:s in an index page pointing to .ps1 + if ($_ -like "*.ps1") { + try { + $dateExternalMod = "" + $dateLocalMod ="" + $wc.OpenRead("$ExternalMods/$_").Close() | Out-Null + $dateExternalMod = ([DateTime]$wc.ResponseHeaders['Last-Modified']).ToString("yyyy-MM-dd HH:mm:ss") + if (Test-Path -Path $LocalMods"\"$_) { + $dateLocalMod = (Get-Item "$LocalMods\$_").LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") + } + + if ($dateExternalMod -gt $dateLocalMod) { + try { + $SaveMod = Join-Path -Path "$LocalMods\" -ChildPath $_ + $Mod = '{0}/{1}' -f $ModsPath.TrimEnd('/'), $_ + Invoke-WebRequest -Uri "$Mod" -OutFile $SaveMod -UseBasicParsing + $ModsUpdated++ + } + catch { + return $False + } + } + } + catch { + return $False + } + } + } + return $ModsUpdated, $DeletedMods + } + # If path is UNC or local + else { + if (Test-Path -Path $ExternalMods"\*.ps1") { + #Get File Names Externally + $ExternalModsNames = Get-ChildItem -Path $ExternalMods -Name -Recurse -Include *.ps1 + #Delete Local Mods that don't exist Externally + foreach ($Mod in $InternalModsNames){ + If($Mod -notin $ExternalModsNames ){ + Remove-Item $LocalMods\$Mod -Force -ErrorAction SilentlyContinue | Out-Null + $DeletedMods++ + } + } + try { + foreach ($Mod in $ExternalModsNames){ + $dateExternalMod = "" + $dateLocalMod ="" + if (Test-Path -Path $LocalMods"\"$Mod) { + $dateLocalMod = (Get-Item "$LocalMods\$Mod").LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") + } + $dateExternalMod = (Get-Item "$ExternalMods\$Mod").LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") + if ($dateExternalMod -gt $dateLocalMod) { + Copy-Item $ExternalMods\$Mod -Destination $LocalMods\$Mod -Force -ErrorAction SilentlyContinue | Out-Null + $ModsUpdated++ + } + } + + } + catch { + return $False + } + return $ModsUpdated, $DeletedMods + } + return $False + } +} diff --git a/Winget-AutoUpdate/functions/Update-App.ps1 b/Winget-AutoUpdate/functions/Update-App.ps1 index efedd64..b316b94 100644 --- a/Winget-AutoUpdate/functions/Update-App.ps1 +++ b/Winget-AutoUpdate/functions/Update-App.ps1 @@ -16,6 +16,15 @@ Function Update-App ($app) { $Balise = $($app.Name) Start-NotifTask -Title $Title -Message $Message -MessageType $MessageType -Balise $Balise -Button1Action $ReleaseNoteURL -Button1Text $Button1Text + #Check if mods exist for preinstall/install/upgrade + $ModsPreInstall, $ModsUpgrade, $ModsInstall, $ModsInstalled = Test-Mods $($app.Id) + + #If PreInstall script exist + if ($ModsPreInstall) { + Write-Log "Modifications for $($app.Id) before upgrade are being applied..." "Yellow" + & "$ModsPreInstall" + } + #Winget upgrade Write-Log "########## WINGET UPGRADE PROCESS STARTS FOR APPLICATION ID '$($App.Id)' ##########" "Gray" @@ -23,8 +32,10 @@ Function Update-App ($app) { Write-Log "-> Running: Winget upgrade --id $($app.Id) --accept-package-agreements --accept-source-agreements -h" & $Winget upgrade --id $($app.Id) --accept-package-agreements --accept-source-agreements -h | Tee-Object -file $LogFile -Append - #Set mods to apply as an upgrade - $ModsMode = "Upgrade" + if ($ModsUpgrade) { + Write-Log "Modifications for $($app.Id) during upgrade are being applied..." "Yellow" + & "$ModsUpgrade" + } #Check if application updated properly $CheckOutdated = Get-WingetOutdatedApps @@ -46,8 +57,10 @@ Function Update-App ($app) { Write-Log "-> Running: Winget install --id $($app.Id) --accept-package-agreements --accept-source-agreements -h" & $Winget install --id $($app.Id) --accept-package-agreements --accept-source-agreements -h | Tee-Object -file $LogFile -Append - #Set mods to apply as an install - $ModsMode = "Install" + if ($ModsInstall) { + Write-Log "Modifications for $($app.Id) during install are being applied..." "Yellow" + & "$ModsInstall" + } #Check if application installed properly $CheckOutdated2 = Get-WingetOutdatedApps @@ -60,17 +73,9 @@ Function Update-App ($app) { } if ($FailedToUpgrade -eq $false) { - - #Check if mods exist for install/upgrade - $ModsInstall, $ModsUpgrade = Test-Mods $($app.Id) - - if (($ModsUpgrade) -and ($ModsMode -eq "Upgrade")) { - Write-Log "Modifications for $($app.Id) after upgrade are being applied..." "Yellow" - & "$ModsUpgrade" - } - elseif (($ModsInstall) -and ($ModsMode -eq "Install")) { - Write-Log "Modifications for $($app.Id) after install are being applied..." "Yellow" - & "$ModsInstall" + if ($ModsInstalled) { + Write-Log "Modifications for $($app.Id) after upgrade/install are being applied..." "Yellow" + & "$ModsInstalled" } } diff --git a/Winget-AutoUpdate/mods/README.md b/Winget-AutoUpdate/mods/README.md index a779ec2..4242b09 100644 --- a/Winget-AutoUpdate/mods/README.md +++ b/Winget-AutoUpdate/mods/README.md @@ -1 +1 @@ -Post install custom scripts will be placed here +Pre/During/Post install custom scripts should be placed here