diff --git a/.github/workflows/GA_Mega-linter.yml b/.github/workflows/GA_Mega-linter.yml index f4c80120..42fd86c4 100644 --- a/.github/workflows/GA_Mega-linter.yml +++ b/.github/workflows/GA_Mega-linter.yml @@ -35,7 +35,7 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 with: - token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 # MegaLinter @@ -43,12 +43,12 @@ jobs: id: ml # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.github.io/flavors/ - uses: oxsecurity/megalinter@146333030da68e2e58c6ff826633824fabe01eaf # v8.5.0 + uses: oxsecurity/megalinter@5a91fb06c83d0e69fbd23756d47438aa723b4a5a # v8.7.0 env: # All available variables are described in documentation # https://megalinter.github.io/configuration/ VALIDATE_ALL_CODEBASE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} # Validates all source when push on main, else just the git diff with main. Override with true if you always want to lint all sources - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} SPELL_CSPELL_CONFIG_FILE: .github/cspell.json POWERSHELL_POWERSHELL_CONFIG_FILE: .github/.powershell-psscriptanalyzer.psd1 DISABLE_ERRORS_LINTERS: REPOSITORY_DEVSKIM,REPOSITORY_CHECKOV,REPOSITORY_GIT_DIFF,SPELL_CSPELL,SPELL_PROSELINT,COPYPASTE_JSCPD,MARKDOWN_MARKDOWN_LINK_CHECK @@ -90,7 +90,7 @@ jobs: run: sudo chown -Rc $UID .git/ - name: Commit and push applied linter fixes if: steps.ml.outputs.has_updated_sources == 1 && (env.APPLY_FIXES_EVENT == 'all' || env.APPLY_FIXES_EVENT == github.event_name) && env.APPLY_FIXES_MODE == 'commit' && github.ref != 'refs/heads/main' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && !contains(github.event.head_commit.message, 'skip fix') - uses: stefanzweifel/git-auto-commit-action@e348103e9026cc0eee72ae06630dbe30c8bf7a79 # v5.1.0 + uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5.2.0 with: branch: ${{ github.event.pull_request.head.ref || github.head_ref || github.ref }} commit_message: "[MegaLinter] Apply linters fixes" diff --git a/.github/workflows/GitFlow_Nightly-builds.yml b/.github/workflows/GitFlow_Nightly-builds.yml index 9ff90f8a..c919ad26 100644 --- a/.github/workflows/GitFlow_Nightly-builds.yml +++ b/.github/workflows/GitFlow_Nightly-builds.yml @@ -18,11 +18,11 @@ jobs: build: name: Create Nightly Build runs-on: windows-latest - + env: # Define the branch as a variable BRANCH: develop - + steps: # Step 1: Checkout the develop branch for nightly builds - name: Checkout code @@ -39,7 +39,7 @@ jobs: shell: powershell run: | # Find the latest tag of any type - $LATEST_TAG = git tag --sort=-v:refname | Select-Object -First 1 + $LATEST_TAG = git for-each-ref --sort=-creatordate --format '%(refname:short)' refs/tags | Select-Object -First 1 Write-Host "Latest tag: $LATEST_TAG" # Get merged PRs since last tag using Git directly diff --git a/Sources/Winget-AutoUpdate/mods/_AppID-template.ps1 b/Sources/Winget-AutoUpdate/mods/_AppID-template.ps1 index b7fa5f2f..265f87c4 100644 --- a/Sources/Winget-AutoUpdate/mods/_AppID-template.ps1 +++ b/Sources/Winget-AutoUpdate/mods/_AppID-template.ps1 @@ -1,89 +1,100 @@ +<# A mods template for apps +Possible use cases: +"$Mods\AppID-preinstall.ps1" +"$Mods\AppID-install.ps1" +"$Mods\AppID-upgrade.ps1" +"$Mods\AppID-installed.ps1" +"$Mods\AppID-notinstalled.ps1" +#> + +<# FUNCTIONS #> +. $PSScriptRoot\_Mods-Functions.ps1 + + <# ARRAYS/VARIABLES #> -#App to Run (as SYSTEM) -#$RunWait = $False if it shouldn't be waited for completion. Example: -#$RunSystem = "$PSScriptRoot\bins\MsiZap.exe" -#$RunSwitch = "tw! {GUID}" +# App to Run (as SYSTEM) +# $RunWait = $False if it shouldn't be waited for completion. Example: +# $RunSystem = "$PSScriptRoot\bins\MsiZap.exe" +# $RunSwitch = "tw! {GUID}" $RunSystem = "" $RunSwitch = "" $RunWait = $True -#Beginning of Process Name to Stop - optional wildcard (*) after, without .exe, multiple: "proc1","proc2" +# Beginning of Process Name to Stop - optional wildcard (*) after, without .exe, multiple: "proc1*","proc2" $Proc = @("") -#Beginning of Service Name to Stop - multiple: "service1.exe","service2.exe" +# Beginning of Service Name to Stop - - optional wildcard (*) after, multiple: "service1*","service2" $Svc = @("") -#Beginning of Process Name to Wait for to End - optional wildcard (*) after, without .exe, multiple: "proc1","proc2" +# Beginning of Process Name to Wait for to End - optional wildcard (*) after, without .exe, multiple: "proc1*","proc2" $Wait = @("") -#Install App from Winget Repo, multiple: "appID1","appID2". Example: -#$WingetIDInst = @("Microsoft.PowerToys") +# Install App from Winget Repo, multiple: "appID1","appID2". Example: +# $WingetIDInst = @("Microsoft.PowerToys") $WingetIDInst = @("") -#WingetID to uninstall in default manifest mode (silent if supported) -#Multiple: "ID1","ID2". Example: -#$WingetIDUninst = @("Microsoft.PowerToys") +# WingetID to uninstall in default manifest mode (silent if supported) +# Multiple: "ID1","ID2". Example: +# $WingetIDUninst = @("Microsoft.PowerToys") $WingetIDUninst = @("") -#Beginning of App Name string to Silently Uninstall (MSI/NSIS/INNO/EXE with defined silent uninstall in registry) -#Multiple: "app1*","app2*", required wildcard (*) after; search is done with "-like"! -#Uninstall all versions if there exist several? +# The partial beginning (with *) or full display name of App to Silently Uninstall (MSI/NSIS/INNO/EXE with defined silent uninstall in registry) +# Multiple: "app1*","app2" ; search is done with "-like"! +# Uninstall all versions if there exist several? $AppUninst = @("") $AllVersions = $False -#Beginning of Desktop Link Name to Remove - optional wildcard (*) after, without .lnk, multiple: "lnk1","lnk2" +# Beginning of Desktop Link Name to Remove - optional wildcard (*) after, without .lnk, multiple: "lnk1*","lnk2" $Lnk = @("") -#Registry _value_ (DWord/String) to add in existing registry Key (Key created if not existing). Example: -#$AddKey = "HKLM:\SOFTWARE\Romanitho\Winget-AutoUpdate" -#$AddValue = "WAU_BypassListForUsers" -#$AddTypeData = "1" -#$AddType = "DWord" +# Registry _value_ (DWord/String) to add in existing registry Key (Key created if not existing). Example: +# $AddKey = "HKLM:\SOFTWARE\Romanitho\Winget-AutoUpdate" +# $AddValue = "WAU_BypassListForUsers" +# $AddTypeData = "1" +# $AddType = "DWord" $AddKey = "" $AddValue = "" $AddTypeData = "" $AddType = "" -#Registry _value_ to delete in existing registry Key. -#Value can be omitted for deleting entire Key!. Example: -#$DelKey = "HKLM:\SOFTWARE\Romanitho\Winget-AutoUpdate" -#$DelValue = "WAU_BypassListForUsers" +# Registry _value_ to delete in existing registry Key. +# Value can be omitted for deleting entire Key!. Example: +# $DelKey = "HKLM:\SOFTWARE\Romanitho\Winget-AutoUpdate" +# $DelValue = "WAU_BypassListForUsers" $DelKey = "" $DelValue = "" -#Remove file/directory, multiple: "file1","file2" Example: -#$DelFile = @("${env:ProgramFiles}\PowerToys\PowerToys.Update.exe") +# Remove file/directory, multiple: "file1","file2" Example: +# $DelFile = @("${env:ProgramFiles}\PowerToys\PowerToys.Update.exe") $DelFile = @("") -#Rename file/directory. Example: -#$RenFile = "${env:ProgramFiles}\PowerToys\PowerToys.Update.exe" -#$NewName = "PowerToys.Update.org" +# Rename file/directory. Example: +# $RenFile = "${env:ProgramFiles}\PowerToys\PowerToys.Update.exe" +# $NewName = "PowerToys.Update.org" $RenFile = "" $NewName = "" -#Copy file/directory. Example: -#$CopyFile = "C:\Logfiles" -#$CopyTo = "C:\Drawings\Logs" +# Copy file/directory. Example: +# $CopyFile = "C:\Logfiles" +# $CopyTo = "C:\Drawings\Logs" $CopyFile = "" $CopyTo = "" -#Find/Replace text in file. Example: -#$File = "C:\dummy.txt" -#$FindText = 'brown fox' -#$ReplaceText = 'white fox' +# Find/Replace text in file. Example: +# $File = "C:\dummy.txt" +# $FindText = 'brown fox' +# $ReplaceText = 'white fox' $File = "" $FindText = '' $ReplaceText = '' -#Grant "Modify" for directory/file to "Authenticated Users" - multiple: "dir1","dir2" +# Grant "Modify" for directory/file to "Authenticated Users" - multiple: "dir1","dir2" $GrantPath = @("") -#App to Run (as current logged-on user) +# App to Run (as current logged-on user) $RunUser = "" $User = $True -<# FUNCTIONS #> -. $PSScriptRoot\_Mods-Functions.ps1 <# MAIN #> if ($RunSystem) { @@ -135,4 +146,5 @@ if ($RunUser) { Invoke-ModsApp $RunUser "" "" $User } + <# EXTRAS #> diff --git a/Sources/Winget-AutoUpdate/mods/_Mods-Functions.ps1 b/Sources/Winget-AutoUpdate/mods/_Mods-Functions.ps1 index 01955c7d..430b2775 100644 --- a/Sources/Winget-AutoUpdate/mods/_Mods-Functions.ps1 +++ b/Sources/Winget-AutoUpdate/mods/_Mods-Functions.ps1 @@ -56,17 +56,17 @@ function Uninstall-WingetID ($WingetIDUninst) { function Uninstall-ModsApp ($AppUninst, $AllVersions) { foreach ($app in $AppUninst) { # we start from scanning the x64 node in registry, if something was found, then we set x64=TRUE - [bool]$app_was_x64 = Process-installedSoftware -app $app -x64 $true; + [bool]$app_was_x64 = Get-InstalledSoftware -app $app -x64 $true; # if nothing was found in x64 node, then we repeat that action in x86 node if (!$app_was_x64) { - Process-installedSoftware -app $app | Out-Null; + Get-InstalledSoftware -app $app | Out-Null; } } Return } -Function Process-installedSoftware() { +Function Get-InstalledSoftware() { [OutputType([Bool])] Param( [parameter(Mandatory = $true)] [string]$app, @@ -84,48 +84,71 @@ Function Process-installedSoftware() { foreach ($obj in $InstalledSoftware) { if ($obj.GetValue('DisplayName') -like $App) { $UninstallString = $obj.GetValue('UninstallString') - $CleanedUninstallString = $UninstallString.Trim([char]0x0022) + $CleanedUninstallString = $UninstallString.Replace('"', '') + $ExeString = $CleanedUninstallString.Substring(0, $CleanedUninstallString.IndexOf('.exe') + 4) if ($UninstallString -like "MsiExec.exe*") { $ProductCode = Select-String "{.*}" -inputobject $UninstallString $ProductCode = $ProductCode.matches.groups[0].value - #MSI x64 Installer - $Exec = Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList "/x$ProductCode REBOOT=R /qn" -PassThru -Wait - #Stop Hard Reboot (if bad MSI!) + # MSI Installer + $Exec = Start-Process "$env:SystemRoot\System32\msiexec.exe" -ArgumentList "/x$ProductCode REBOOT=R /qn" -PassThru -Wait + # Stop Hard Reboot (if bad MSI!) if ($Exec.ExitCode -eq 1641) { - Start-Process "C:\Windows\System32\shutdown.exe" -ArgumentList "/a" + Start-Process "$env:SystemRoot\System32\shutdown.exe" -ArgumentList "/a" } } else { $QuietUninstallString = $obj.GetValue('QuietUninstallString') if ($QuietUninstallString) { - $QuietUninstallString = Select-String "(\x22.*\x22) +(.*)" -inputobject $QuietUninstallString + $QuietUninstallString = Select-String '("[^"]*") +(.*)' -inputobject $QuietUninstallString $Command = $QuietUninstallString.matches.groups[1].value $Parameter = $QuietUninstallString.matches.groups[2].value - #All EXE x64 Installers (already defined silent uninstall) + # All EXE Installers (already defined silent uninstall) Start-Process $Command -ArgumentList $Parameter -Wait } else { - if ((Test-Path $CleanedUninstallString)) { - $NullSoft = Select-String -Path $CleanedUninstallString -Pattern "Nullsoft" - } - if ($NullSoft) { - #NSIS x64 Installer - Start-Process $UninstallString -ArgumentList "/S" -Wait - } - else { - if ((Test-Path $CleanedUninstallString)) { - $Inno = Select-String -Path $CleanedUninstallString -Pattern "Inno Setup" - } - if ($Inno) { - #Inno x64 Installer - Start-Process $UninstallString -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-" -Wait + # Improved detection logic + if ((Test-Path $ExeString -ErrorAction SilentlyContinue)) { + try { + # Read the whole file to find installer signatures + $fileContent = Get-Content -Path $ExeString -Raw -ErrorAction Stop + # Executes silent uninstallation based on installer type + if ($fileContent -match "\bNullsoft\b" -or $fileContent -match "\bNSIS\b") { + # Nullsoft (NSIS) Uninstaller + Start-Process $ExeString -ArgumentList "/NCRC /S" -Wait + } + elseif ($fileContent -match "\bInno Setup\b") { + # Inno Uninstaller + Start-Process $ExeString -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-" -Wait + } + elseif ($fileContent -match "\bWise Solutions\b") { + # Wise Uninstaller (Unwise32.exe) + # Find the Install.log path parameter in the UninstallString + $ArgString = $CleanedUninstallString.Substring($CleanedUninstallString.IndexOf('.exe') + 4).Trim() + # Copy files to temp folder so that Unwise32.exe can find Install.log (very, very old system) + Copy-Item -Path $ExeString -Destination $env:TEMP -Force + $ExeString = Join-Path $env:TEMP (Split-Path $ExeString -Leaf) + Copy-Item -Path $ArgString -Destination $env:TEMP -Force + $ArgString = Join-Path $env:TEMP (Split-Path $ArgString -Leaf) + # Execute the uninstaller with the copied Unwise32.exe + Start-Process $ExeString -ArgumentList "/s $ArgString" -Wait + # Remove the copied Unwise32.exe from temp folder (Install.log gets deleted by Unwise32.exe) + Remove-Item -Path $ExeString -Force -ErrorAction SilentlyContinue + } + else { + Write-Host "$(if($true -eq $x64) {'x64'} else {'x86'}) Uninstaller unknown, trying the UninstallString from registry..." + $NativeUninstallString = Select-String "(\x22.*\x22) +(.*)" -inputobject $UninstallString + $Command = $NativeUninstallString.matches.groups[1].value + $Parameter = $NativeUninstallString.matches.groups[2].value + Start-Process $Command -ArgumentList $Parameter -Wait + } } - else { - Write-Host "$(if($true -eq $x64) {'x64'} else {'x86'}) Uninstaller unknown, trying the UninstallString from registry..." + catch { + Write-Warning "Could not read installer file: $_" + # Fallback to standard method + Write-Host "Failed to inspect installer, trying UninstallString directly..." $NativeUninstallString = Select-String "(\x22.*\x22) +(.*)" -inputobject $UninstallString $Command = $NativeUninstallString.matches.groups[1].value $Parameter = $NativeUninstallString.matches.groups[2].value - #All EXE x64 Installers (native defined uninstall) Start-Process $Command -ArgumentList $Parameter -Wait } } @@ -141,10 +164,15 @@ Function Process-installedSoftware() { } function Remove-ModsLnk ($Lnk) { + $removedCount = 0 foreach ($link in $Lnk) { - Remove-Item -Path "${env:Public}\Desktop\$link.lnk" -Force -ErrorAction SilentlyContinue | Out-Null + $linkPath = "${env:Public}\Desktop\$link.lnk" + if (Test-Path $linkPath) { + Remove-Item -Path $linkPath -Force -ErrorAction SilentlyContinue | Out-Null + $removedCount++ + } } - Return + Return $removedCount } function Add-ModsReg ($AddKey, $AddValue, $AddTypeData, $AddType) { diff --git a/Sources/Winget-AutoUpdate/mods/_WAU-mods-postsys-template.ps1 b/Sources/Winget-AutoUpdate/mods/_WAU-mods-postsys-template.ps1 index df9dcd1d..b2668970 100644 --- a/Sources/Winget-AutoUpdate/mods/_WAU-mods-postsys-template.ps1 +++ b/Sources/Winget-AutoUpdate/mods/_WAU-mods-postsys-template.ps1 @@ -6,11 +6,21 @@ Make sure your Functions have unique names! #> <# FUNCTIONS #> +. $PSScriptRoot\_Mods-Functions.ps1 <# ARRAYS/VARIABLES #> +# Example: +# Beginning of Desktop Link Name to Remove - optional wildcard (*) after, without .lnk, multiple: "lnk1","lnk2" +# The function Remove-ModsLnk returns the number of removed links. +# $Lnk = @("Acrobat Read*","Bitwarden","calibre*") <# MAIN #> +# Example: +# if ($Lnk) { +# $removedCount = Remove-ModsLnk $Lnk +# Write-ToLog "-> Removed $($removedCount) Public Desktop Links!" "Green" +# } -Write-ToLog "...nothing to do!" "Green" +Write-ToLog "-> ...Mods (postsys) for WAU has nothing more to do!" "Green" diff --git a/Sources/Winget-AutoUpdate/mods/_WAU-mods-template.ps1 b/Sources/Winget-AutoUpdate/mods/_WAU-mods-template.ps1 index 5de6d0d4..c85e6707 100644 --- a/Sources/Winget-AutoUpdate/mods/_WAU-mods-template.ps1 +++ b/Sources/Winget-AutoUpdate/mods/_WAU-mods-template.ps1 @@ -7,6 +7,7 @@ Exit 1 to Re-run WAU from this script (beware of loops)! #> <# FUNCTIONS #> +. $PSScriptRoot\_Mods-Functions.ps1 <# ARRAYS/VARIABLES #> diff --git a/Sources/Winget-AutoUpdate/mods/_WAU-notinstalled-template.ps1 b/Sources/Winget-AutoUpdate/mods/_WAU-notinstalled-template.ps1 index 9f552299..4812121d 100644 --- a/Sources/Winget-AutoUpdate/mods/_WAU-notinstalled-template.ps1 +++ b/Sources/Winget-AutoUpdate/mods/_WAU-notinstalled-template.ps1 @@ -10,20 +10,22 @@ This all-purpose mod will be overridden by any specific: <# FUNCTIONS #> . $PSScriptRoot\_Mods-Functions.ps1 + <# ARRAYS/VARIABLES #> <# MAIN #> -if ($($app.Id) -eq "Microsoft.SQLServerManagementStudio") { - if ($ConfirmInstall -eq $false) { - try { - Write-ToLog "...successfully done something" "Green" - } - catch { - Write-ToLog "...failed to do something" "Red" - } - } -} -else { - Write-ToLog "...nothing defined for $($app.Id)" "Yellow" -} +# Example: +# if ($($app.Id) -eq "Microsoft.SQLServerManagementStudio") { +# if ($ConfirmInstall -eq $false) { +# try { +# Write-ToLog "...successfully done something" "Green" +# } +# catch { +# Write-ToLog "...failed to do something" "Red" +# } +# } +# } +# else { +# Write-ToLog "...nothing defined for $($app.Id)" "Yellow" +# }