diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a56e46ad..edf454cac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ ## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop) +### BREAKING CHANGE + +- **scoop-install|update**: `-k`option that skipped cache and forced redownload is replaced by `-d`. `-k` now keeps older cache after app update + +### Features + +- **scoop-cache|update**: New tidy command that removes older packages from cache. Automatically run it after app update ([#6068](https://github.com/ScoopInstaller/Scoop/issues/6068)) + ### Bug Fixes - **scoop-download|install|update:** Fallback to default downloader when aria2 fails ([#4292](https://github.com/ScoopInstaller/Scoop/issues/4292)) diff --git a/lib/cache.ps1 b/lib/cache.ps1 new file mode 100644 index 0000000000..908d38eadd --- /dev/null +++ b/lib/cache.ps1 @@ -0,0 +1,34 @@ +# Remove older cache of the given app +function tidy_cache([String] $app, [bool] $log = $false) { + if ($app -like "*.json") { return } + + # Dependencies of the format "bucket/dependency" install in a directory of form + # "dependency". So we need to extract the bucket from the name and only use the app + # name + $app = ($app -split '/|\\')[-1] + + $files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match | Sort-Object LastWriteTime ) + if ($files.Count -le 2) { + return + } + + # do not remove last two files + $files = $files[0..($files.Count - 3)] + # remove only items more than one month old + $files = $files| Where-Object {$_.LastWriteTime -lt (Get-Date).AddMonths(-1)} + if ($files.Count -le 0) { + return + } + Write-Host -f yellow "Removing older cache for $app`:" -NoNewline + + $totalLength = ($files | Measure-Object -Property Length -Sum).Sum + $files | ForEach-Object { + Write-Host " $(($_ -split '#')[1])" -NoNewline + Remove-Item $_.FullName + } + if ($log) { + Write-Host "Deleted: $($files.Count) $(pluralize $files.Count 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow + } + + Write-Host '' +} diff --git a/libexec/scoop-cache.ps1 b/libexec/scoop-cache.ps1 index 30e8354fca..5307f6f13d 100644 --- a/libexec/scoop-cache.ps1 +++ b/libexec/scoop-cache.ps1 @@ -1,4 +1,4 @@ -# Usage: scoop cache show|rm [app(s)] +# Usage: scoop cache show|rm|tidy [app(s)] # Summary: Show or clear the download cache # Help: Scoop caches downloads so you don't need to download the same files # when you uninstall and re-install the same version of an app. @@ -7,13 +7,19 @@ # scoop cache show # to see what's in the cache, and # scoop cache rm to remove downloads for a specific app. +# Or to only remove downloads that older, +# scoop cache tidy removes ones older than a month, keeping the two most recent # # To clear everything in your cache, use: # scoop cache rm * +# To remove older items for all apps, use: +# scoop cache tidy * # You can also use the `-a/--all` switch in place of `*` here param($cmd) +. "$PSScriptRoot\..\lib\cache.ps1" + function cacheinfo($file) { $app, $version, $url = $file.Name -split '#' New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length } @@ -58,10 +64,42 @@ function cacheremove($app) { Write-Host "Deleted: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow } +function cache_remove_older($app) { + if (!$app) { + 'ERROR: missing' + my_usage + exit 1 + } elseif ($app -eq '*' -or $app -eq '-a' -or $app -eq '--all') { + $files = @(Get-ChildItem $cachedir) + $totalLength = ($files | Measure-Object -Property Length -Sum).Sum + # Get all apps with cache, and remove their older packages + $apps = @() + $files | ForEach-Object { + $apps += ($_ -split '#', 2)[0] + } + $apps = $apps | Select-Object -Unique + + $apps | ForEach-Object { + tidy_cache $_ + } + + $filesAfter = @(Get-ChildItem $cachedir) + $totalLengthAfter = ($filesAfter | Measure-Object -Property Length -Sum).Sum + $filesRemoved = $files.Length - $filesAfter.length + $sizeRemoved = $totalLength - $totalLengthAfter + Write-Host "Deleted: $($filesRemoved) $(pluralize $filesRemoved 'file' 'files'), $(filesize $sizeRemoved)" -ForegroundColor Yellow + } else { + tidy_cache $app $true + } +} + switch($cmd) { 'rm' { cacheremove $Args } + 'tidy' { + cache_remove_older $Args + } 'show' { cacheshow $Args } diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index 0fadcff09d..bc2d70f043 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -22,7 +22,7 @@ # Options: # -g, --global Install the app globally # -i, --independent Don't install dependencies automatically -# -k, --no-cache Don't use the download cache +# -d, --no-cache Force redownload by not using the download cache # -s, --skip-hash-check Skip hash validation (use with caution!) # -u, --no-update-scoop Don't update Scoop before installing if it's outdated # -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it @@ -49,7 +49,7 @@ if ($err) { "scoop install: $err"; exit 1 } $global = $opt.g -or $opt.global $check_hash = !($opt.s -or $opt.'skip-hash-check') $independent = $opt.i -or $opt.independent -$use_cache = !($opt.k -or $opt.'no-cache') +$use_cache = !($opt.d -or $opt.'no-cache') $architecture = Get-DefaultArchitecture try { $architecture = Format-ArchitectureString ($opt.a + $opt.arch) diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index 9d41906eca..e3189810b4 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -9,7 +9,8 @@ # -f, --force Force update even when there isn't a newer version # -g, --global Update a globally installed app # -i, --independent Don't install dependencies automatically -# -k, --no-cache Don't use the download cache +# -d, --no-cache Force redownload by not using the download cache +# -k, --keep-cache Don't remove older cached install packages # -s, --skip-hash-check Skip hash validation (use with caution!) # -q, --quiet Hide extraneous messages # -a, --all Update all apps (alternative to '*') @@ -25,6 +26,7 @@ . "$PSScriptRoot\..\lib\depends.ps1" . "$PSScriptRoot\..\lib\install.ps1" . "$PSScriptRoot\..\lib\download.ps1" +. "$PSScriptRoot\..\lib\cache.ps1" if (get_config USE_SQLITE_CACHE) { . "$PSScriptRoot\..\lib\database.ps1" } @@ -34,7 +36,8 @@ if ($err) { "scoop update: $err"; exit 1 } $global = $opt.g -or $opt.global $force = $opt.f -or $opt.force $check_hash = !($opt.s -or $opt.'skip-hash-check') -$use_cache = !($opt.k -or $opt.'no-cache') +$use_cache = !($opt.d -or $opt.'no-cache') +$keep_cache = $use_cache -and ($opt.k -or $opt.'keep-cache') $quiet = $opt.q -or $opt.quiet $independent = $opt.i -or $opt.independent $all = $opt.a -or $opt.all @@ -258,7 +261,7 @@ function Sync-Bucket { } } -function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) { +function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $keep_cache = $false, $check_hash = $true) { $old_version = Select-CurrentVersion -AppName $app -Global:$global $old_manifest = installed_manifest $app $old_version $global $install = install_info $app $old_version $global @@ -383,6 +386,11 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c ensure_none_failed $apps $apps.Where({ !(installed $_) }) + $app | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } } + + # remove older cache after update + if (!$keep_cache) { + tidy_cache $app $true + } } if (-not ($apps -or $all)) { @@ -462,7 +470,7 @@ if (-not ($apps -or $all)) { $suggested = @{} # $outdated is a list of ($app, $global) tuples - $outdated | ForEach-Object { update @_ $quiet $independent $suggested $use_cache $check_hash } + $outdated | ForEach-Object { update @_ $quiet $independent $suggested $use_cache $keep_cache $check_hash } } exit 0