Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better version specific relationships at install and upgrade #4023

Merged
merged 2 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
9 changes: 7 additions & 2 deletions ConsoleUI/ModInfoScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ private int addDependencies(int top = 8)
const int lblW = 16;
int nameW = midL - 2 - lblW - 2 - 1;
int depsH = (h - 2) * numDeps / (numDeps + numConfs);
var upgradeableGroups = registry
.CheckUpgradeable(manager.CurrentInstance.VersionCriteria(),
new HashSet<string>());

AddObject(new ConsoleFrame(
1, top, midL, top + h - 1,
Expand All @@ -290,7 +293,8 @@ private int addDependencies(int top = 8)
foreach (RelationshipDescriptor rd in mod.depends) {
tb.AddLine(ScreenObject.TruncateLength(
// Show install status
ModListScreen.StatusSymbol(plan.GetModStatus(manager, registry, rd.ToString()))
ModListScreen.StatusSymbol(plan.GetModStatus(manager, registry, rd.ToString(),
upgradeableGroups[true]))
+ rd.ToString(),
nameW
));
Expand All @@ -315,7 +319,8 @@ private int addDependencies(int top = 8)
foreach (RelationshipDescriptor rd in mod.conflicts) {
tb.AddLine(ScreenObject.TruncateLength(
// Show install status
ModListScreen.StatusSymbol(plan.GetModStatus(manager, registry, rd.ToString()))
ModListScreen.StatusSymbol(plan.GetModStatus(manager, registry, rd.ToString(),
upgradeableGroups[true]))
+ rd.ToString(),
nameW
));
Expand Down
Loading
Loading