Skip to content

Commit

Permalink
Merge #4023 Better version specific relationships at install and upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Mar 9, 2024
2 parents 55fb0ba + 0f7ca67 commit 1cd1901
Show file tree
Hide file tree
Showing 38 changed files with 1,304 additions and 612 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
- [Multiple] New Crowdin updates (#4019 by: Olympic1, vinix38; reviewed: HebaruSan)
- [Core] Support Windows KSP1 instances on Linux (#4044 by: HebaruSan)
- [GUI] I18n updates from Crowdin (#4050 by: HebaruSan)
- [Multiple] Better version specific relationships at install and upgrade (#4023 by: HebaruSan)

### Bugfixes

Expand Down
10 changes: 8 additions & 2 deletions Cmdline/Action/List.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

using log4net;

Expand Down Expand Up @@ -53,6 +54,11 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
if (exportFileType == null)
{
var installed = new SortedDictionary<string, ModuleVersion>(registry.Installed());
var upgradeable = registry
.CheckUpgradeable(instance.VersionCriteria(), new HashSet<string>())
[true]
.Select(m => m.identifier)
.ToHashSet();

foreach (KeyValuePair<string, ModuleVersion> mod in installed)
{
Expand Down Expand Up @@ -102,7 +108,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
}
}
}
else if (latest.version.IsEqualTo(current_version) && !registry.HasUpdate(mod.Key, instance.VersionCriteria()))
else if (!upgradeable.Contains(mod.Key))
{
// Up to date
log.InfoFormat("Latest {0} is {1}", mod.Key, latest.version);
Expand All @@ -119,7 +125,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
}
}
}
else if (latest.version.IsGreaterThan(mod.Value) || registry.HasUpdate(mod.Key, instance.VersionCriteria()))
else
{
// Upgradable
bullet = "^";
Expand Down
4 changes: 2 additions & 2 deletions Cmdline/Action/Prompt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ private string[] GetInstIdentifiers(string prefix)
CKAN.GameInstance inst = MainClass.GetGameInstance(manager);
var registry = RegistryManager.Instance(inst, repoData).registry;
return registry.Installed(false, false)
.Select(kvp => kvp.Key)
.Keys
.Where(ident => ident.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)
&& !registry.GetInstalledVersion(ident).IsDLC)
.ToArray();
Expand All @@ -214,7 +214,7 @@ private static bool WantsGameInstances(TypeInfo ti)

private string[] GetGameInstances(string prefix)
=> manager.Instances
.Select(kvp => kvp.Key)
.Keys
.Where(ident => ident.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))
.ToArray();

Expand Down
151 changes: 95 additions & 56 deletions Cmdline/Action/Upgrade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
using System.Transactions;

using Autofac;
using log4net;

using CKAN.Versioning;
using CKAN.Configuration;
using CKAN.Extensions;

namespace CKAN.CmdLine
{
Expand Down Expand Up @@ -114,38 +114,17 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
var registry = regMgr.registry;
if (options.upgrade_all)
{
var to_upgrade = new List<CkanModule>();

foreach (KeyValuePair<string, ModuleVersion> mod in registry.Installed(false))
var to_upgrade = registry
.CheckUpgradeable(instance.VersionCriteria(), new HashSet<string>())
[true];
if (to_upgrade.Count == 0)
{
try
{
// Check if upgrades are available
var latest = registry.LatestAvailable(mod.Key, instance.VersionCriteria());

// This may be an unindexed mod. If so,
// skip rather than crash. See KSP-CKAN/CKAN#841.
if (latest == null || latest.IsDLC)
{
continue;
}

if (latest.version.IsGreaterThan(mod.Value) || registry.HasUpdate(mod.Key, instance.VersionCriteria()))
{
// Upgradable
log.InfoFormat("New version {0} found for {1}",
latest.version, latest.identifier);
to_upgrade.Add(latest);
}

}
catch (ModuleNotFoundKraken)
{
log.InfoFormat("{0} is installed, but no longer in the registry",
mod.Key);
}
user.RaiseMessage(Properties.Resources.UpgradeAllUpToDate);
}
else
{
UpgradeModules(manager, user, instance, to_upgrade);
}
UpgradeModules(manager, user, instance, true, to_upgrade);
}
else
{
Expand Down Expand Up @@ -193,14 +172,18 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
/// <param name="user">IUser object for output</param>
/// <param name="instance">Game instance to use</param>
/// <param name="modules">List of modules to upgrade</param>
private void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, bool ConfirmPrompt, List<CkanModule> modules)
private void UpgradeModules(GameInstanceManager manager,
IUser user,
CKAN.GameInstance instance,
List<CkanModule> modules)
{
UpgradeModules(manager, user, instance, repoData,
UpgradeModules(
manager, user, instance, repoData,
(ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs) =>
installer.Upgrade(modules, downloader,
ref possibleConfigOnlyDirs, regMgr, true, true, ConfirmPrompt),
m => modules.Add(m)
);
ref possibleConfigOnlyDirs,
regMgr, true, true, true),
m => modules.Add(m));
}

/// <summary>
Expand All @@ -210,23 +193,80 @@ private void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameIn
/// <param name="user">IUser object for output</param>
/// <param name="instance">Game instance to use</param>
/// <param name="identsAndVersions">List of identifier[=version] to upgrade</param>
private void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameInstance instance, List<string> identsAndVersions)
private void UpgradeModules(GameInstanceManager manager,
IUser user,
CKAN.GameInstance instance,
List<string> identsAndVersions)
{
UpgradeModules(manager, user, instance, repoData,
UpgradeModules(
manager, user, instance, repoData,
(ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs) =>
installer.Upgrade(
identsAndVersions.Select(arg => CkanModule.FromIDandVersion(
regMgr.registry, arg,
instance.VersionCriteria()))
.ToList(),
downloader,
ref possibleConfigOnlyDirs,
regMgr,
true),
m => identsAndVersions.Add(m.identifier)
);
{
var crit = instance.VersionCriteria();
var registry = regMgr.registry;
// Installed modules we're NOT upgrading
var heldIdents = registry.Installed(false)
.Keys
.Except(identsAndVersions.Select(arg => UpToFirst(arg, '=')))
.ToHashSet();
// The modules we'll have after upgrading as aggressively as possible
var limiters = identsAndVersions.Select(req => CkanModule.FromIDandVersion(registry, req, crit)
?? DefaultIfThrows(
() => registry.LatestAvailable(req, crit))
?? registry.GetInstalledVersion(req))
.Concat(heldIdents.Select(ident => registry.GetInstalledVersion(ident)))
.Where(m => m != null)
.ToList();
// Modules allowed by THOSE modules' relationships
var upgradeable = registry
.CheckUpgradeable(crit, heldIdents, limiters)
[true]
.ToDictionary(m => m.identifier,
m => m);
// Substitute back in the ident=ver requested versions
var to_upgrade = new List<CkanModule>();
foreach (var request in identsAndVersions)
{
var module = CkanModule.FromIDandVersion(registry, request, crit)
?? (upgradeable.TryGetValue(request, out CkanModule m)
? m
: null);
if (module == null)
{
user.RaiseMessage(Properties.Resources.UpgradeAlreadyUpToDate, request);
}
else
{
to_upgrade.Add(module);
}
}
if (to_upgrade.Count > 0)
{
installer.Upgrade(to_upgrade, downloader, ref possibleConfigOnlyDirs, regMgr, true);
}
},
m => identsAndVersions.Add(m.identifier));
}

public static T DefaultIfThrows<T>(Func<T> func)
{
try
{
return func();
}
catch
{
return default;
}
}

private static string UpToFirst(string orig, char toFind)
=> UpTo(orig, orig.IndexOf(toFind));

private static string UpTo(string orig, int pos)
=> pos >= 0 && pos < orig.Length ? orig.Substring(0, pos)
: orig;

// Action<ref T> isn't allowed
private delegate void AttemptUpgradeAction(ModuleInstaller installer, NetAsyncModulesDownloader downloader, RegistryManager regMgr, ref HashSet<string> possibleConfigOnlyDirs);

Expand All @@ -241,11 +281,12 @@ private void UpgradeModules(GameInstanceManager manager, IUser user, CKAN.GameIn
/// <param name="instance">Game instance to use</param>
/// <param name="attemptUpgradeCallback">Function to call to try to perform the actual upgrade, may throw TooManyModsProvideKraken</param>
/// <param name="addUserChoiceCallback">Function to call when the user has requested a new module added to the change set in response to TooManyModsProvideKraken</param>
private void UpgradeModules(
GameInstanceManager manager, IUser user, CKAN.GameInstance instance,
RepositoryDataManager repoData,
AttemptUpgradeAction attemptUpgradeCallback,
Action<CkanModule> addUserChoiceCallback)
private void UpgradeModules(GameInstanceManager manager,
IUser user,
CKAN.GameInstance instance,
RepositoryDataManager repoData,
AttemptUpgradeAction attemptUpgradeCallback,
Action<CkanModule> addUserChoiceCallback)
{
using (TransactionScope transact = CkanTransaction.CreateTransactionScope()) {
var installer = new ModuleInstaller(instance, manager.Cache, user);
Expand Down Expand Up @@ -282,7 +323,5 @@ private void UpgradeModules(
private readonly IUser user;
private readonly GameInstanceManager manager;
private readonly RepositoryDataManager repoData;

private static readonly ILog log = LogManager.GetLogger(typeof(Upgrade));
}
}
2 changes: 2 additions & 0 deletions Cmdline/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ Try `ckan list` for a list of installed mods.</value></data>
<data name="UpgradeAlreadyHaveLatest" xml:space="preserve"><value>You already have the latest version</value></data>
<data name="UpgradeAborted" xml:space="preserve"><value>Upgrade aborted: {0}</value></data>
<data name="UpgradeNotFound" xml:space="preserve"><value>Module {0} not found</value></data>
<data name="UpgradeAlreadyUpToDate" xml:space="preserve"><value>Module {0} is already up to date</value></data>
<data name="UpgradeAllUpToDate" xml:space="preserve"><value>All modules are up to date</value></data>
<data name="UpgradeDLC" xml:space="preserve"><value>CKAN can't upgrade expansion '{0}' for you</value></data>
<data name="UpgradeDLCStorePage" xml:space="preserve"><value>To upgrade this expansion, download any updates from the store page from which you purchased it:
{0}</value></data>
Expand Down
28 changes: 20 additions & 8 deletions ConsoleUI/InstallScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
// Reset this so we stop unless an exception sets it to true
retry = false;

RegistryManager regMgr = RegistryManager.Instance(manager.CurrentInstance, repoData);
var regMgr = RegistryManager.Instance(manager.CurrentInstance, repoData);
var registry = regMgr.registry;

// GUI prompts user to choose recs/sugs,
// CmdLine assumes recs and ignores sugs
if (plan.Install.Count > 0) {
// Track previously rejected optional dependencies and don't prompt for them again.
DependencyScreen ds = new DependencyScreen(manager, regMgr.registry, plan, rejected, debug);
DependencyScreen ds = new DependencyScreen(manager, registry, plan, rejected, debug);
if (ds.HaveOptions()) {
LaunchSubScreen(theme, ds);
}
Expand All @@ -74,16 +75,27 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
}
NetAsyncModulesDownloader dl = new NetAsyncModulesDownloader(this, manager.Cache);
if (plan.Install.Count > 0) {
List<CkanModule> iList = new List<CkanModule>(plan.Install);
var iList = plan.Install
.Select(m => registry.LatestAvailable(m.identifier,
manager.CurrentInstance.VersionCriteria(),
null,
registry.InstalledModules
.Select(im => im.Module)
.ToArray(),
plan.Install))
.ToArray();
inst.InstallList(iList, resolvOpts, regMgr, ref possibleConfigOnlyDirs, dl);
plan.Install.Clear();
}
if (plan.Upgrade.Count > 0) {
inst.Upgrade(plan.Upgrade
.Select(ident => regMgr.registry.LatestAvailable(
ident, manager.CurrentInstance.VersionCriteria()))
.ToList(),
dl, ref possibleConfigOnlyDirs, regMgr);
var upgGroups = registry
.CheckUpgradeable(manager.CurrentInstance.VersionCriteria(),
// Hold identifiers not chosen for upgrading
registry.Installed(false)
.Keys
.Except(plan.Upgrade)
.ToHashSet());
inst.Upgrade(upgGroups[true], dl, ref possibleConfigOnlyDirs, regMgr);
plan.Upgrade.Clear();
}
if (plan.Replace.Count > 0) {
Expand Down
Loading

0 comments on commit 1cd1901

Please sign in to comment.