Skip to content

Commit

Permalink
Ability to update repos without a game instance
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Aug 15, 2024
1 parent 2cde58b commit 1794ae9
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 43 deletions.
154 changes: 119 additions & 35 deletions Cmdline/Action/Update.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

using CommandLine;

using CKAN.Versioning;
using CKAN.Games;

namespace CKAN.CmdLine
{
public class Update : ICommand
// Does not always need an instance, so this is not an ICommand
public class Update
{
/// <summary>
/// Initialize the update command object
/// </summary>
/// <param name="mgr">GameInstanceManager containing our instances</param>
/// <param name="user">IUser object for interaction</param>
public Update(RepositoryDataManager repoData, IUser user)
public Update(RepositoryDataManager repoData, IUser user, GameInstanceManager manager)
{
this.repoData = repoData;
this.user = user;
this.manager = manager;
}

/// <summary>
Expand All @@ -26,22 +33,66 @@ public Update(RepositoryDataManager repoData, IUser user)
/// <returns>
/// Exit code for shell environment
/// </returns>
public int RunCommand(CKAN.GameInstance instance, object raw_options)
public int RunCommand(object raw_options)
{
UpdateOptions options = (UpdateOptions) raw_options;

List<CkanModule> compatible_prior = null;

if (options.list_changes)
{
// Get a list of compatible modules prior to the update.
var registry = RegistryManager.Instance(instance, repoData).registry;
compatible_prior = registry.CompatibleModules(instance.VersionCriteria()).ToList();
}

try
{
UpdateRepository(instance);
if (options.repositoryURLs != null || options.game != null)
{
var game = options.game == null ? KnownGames.knownGames.First()
: KnownGames.GameByShortName(options.game);
if (game == null)
{
user.RaiseError(Properties.Resources.UpdateBadGame,
options.game,
string.Join(", ", KnownGames.AllGameShortNames()));
return Exit.BADOPT;
}
var repos = (options.repositoryURLs ?? Enumerable.Empty<string>())
.Select((url, i) =>
new Repository(string.Format(Properties.Resources.UpdateURLRepoName,
i + 1),
getUri(url)))
.DefaultIfEmpty(Repository.DefaultGameRepo(game))
.ToArray();
List<CkanModule> availablePrior = null;
if (options.list_changes)
{
availablePrior = repoData.GetAllAvailableModules(repos)
.Select(am => am.Latest())
.ToList();
}
UpdateRepositories(game, repos, options.force);
if (options.list_changes)
{
PrintChanges(availablePrior,
repoData.GetAllAvailableModules(repos)
.Select(am => am.Latest())
.ToList());
}
}
else
{
var instance = MainClass.GetGameInstance(manager);
Registry registry = null;
List<CkanModule> compatible_prior = null;
GameVersionCriteria crit = null;
if (options.list_changes)
{
// Get a list of compatible modules prior to the update.
registry = RegistryManager.Instance(instance, repoData).registry;
crit = instance.VersionCriteria();
compatible_prior = registry.CompatibleModules(crit).ToList();
}
UpdateRepositories(instance, options.force);
if (options.list_changes)
{
PrintChanges(compatible_prior,
registry.CompatibleModules(crit).ToList());
}
}
}
catch (MissingCertificateKraken kraken)
{
Expand All @@ -50,12 +101,6 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
return Exit.ERROR;
}

if (options.list_changes)
{
var registry = RegistryManager.Instance(instance, repoData).registry;
PrintChanges(compatible_prior, registry.CompatibleModules(instance.VersionCriteria()).ToList());
}

return Exit.OK;
}

Expand All @@ -64,22 +109,25 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
/// </summary>
/// <param name="modules_prior">List of the compatible modules prior to the update.</param>
/// <param name="modules_post">List of the compatible modules after the update.</param>
private void PrintChanges(List<CkanModule> modules_prior, List<CkanModule> modules_post)
private void PrintChanges(List<CkanModule> modules_prior,
List<CkanModule> modules_post)
{
var prior = new HashSet<CkanModule>(modules_prior, new NameComparer());
var post = new HashSet<CkanModule>(modules_post, new NameComparer());

var post = new HashSet<CkanModule>(modules_post, new NameComparer());

var added = new HashSet<CkanModule>(post.Except(prior, new NameComparer()));
var added = new HashSet<CkanModule>(post.Except(prior, new NameComparer()));
var removed = new HashSet<CkanModule>(prior.Except(post, new NameComparer()));


// Default compare includes versions
var unchanged = post.Intersect(prior);
var updated = post.Except(unchanged).Except(added).Except(removed).ToList();
var updated = post.Except(unchanged)
.Except(added)
.Except(removed)
.ToList();

// Print the changes.
user.RaiseMessage(Properties.Resources.UpdateChangesSummary, added.Count(), removed.Count(), updated.Count());
user.RaiseMessage(Properties.Resources.UpdateChangesSummary,
added.Count(), removed.Count(), updated.Count());

if (added.Count > 0)
{
Expand Down Expand Up @@ -126,28 +174,64 @@ private void PrintModules(string message, IEnumerable<CkanModule> modules)
}

/// <summary>
/// Updates the repository.
/// Updates repositories for a game instance
/// </summary>
/// <param name="instance">The KSP instance to work on.</param>
/// <param name="instance">The game instance to work on.</param>
/// <param name="repository">Repository to update. If null all repositories are used.</param>
private void UpdateRepository(CKAN.GameInstance instance)
private void UpdateRepositories(CKAN.GameInstance instance, bool force = false)
{
RegistryManager registry_manager = RegistryManager.Instance(instance, repoData);

repoData.Update(registry_manager.registry.Repositories.Values.ToArray(),
instance.game, false, new NetAsyncDownloader(user), user);
var registry = RegistryManager.Instance(instance, repoData).registry;
var result = repoData.Update(registry.Repositories.Values.ToArray(),
instance.game, force,
new NetAsyncDownloader(user), user);
if (result == RepositoryDataManager.UpdateResult.Updated)
{
user.RaiseMessage(Properties.Resources.UpdateSummary,
registry.CompatibleModules(instance.VersionCriteria()).Count());
}
}

user.RaiseMessage(Properties.Resources.UpdateSummary, registry_manager.registry.CompatibleModules(instance.VersionCriteria()).Count());
/// <summary>
/// Updates repositories
/// </summary>
/// <param name="game">The game for which the URLs contain metadata</param>
/// <param name="repos">Repositories to update</param>
private void UpdateRepositories(IGame game, Repository[] repos, bool force = false)
{
var result = repoData.Update(repos, game, force, new NetAsyncDownloader(user), user);
if (result == RepositoryDataManager.UpdateResult.Updated)
{
user.RaiseMessage(Properties.Resources.UpdateSummary,
repoData.GetAllAvailableModules(repos).Count());
}
}

private Uri getUri(string arg)
=> Uri.IsWellFormedUriString(arg, UriKind.Absolute)
? new Uri(arg)
: File.Exists(arg)
? new Uri(Path.GetFullPath(arg))
: throw new FileNotFoundKraken(arg);

private readonly RepositoryDataManager repoData;
private readonly IUser user;
private readonly GameInstanceManager manager;
}

internal class UpdateOptions : InstanceSpecificOptions
{
[Option("list-changes", DefaultValue = false, HelpText = "List new and removed modules")]
[Option('l', "list-changes", DefaultValue = false, HelpText = "List new and removed modules")]
public bool list_changes { get; set; }

// Can't specify DefaultValue here because we want to fall back to instance-based updates when omitted
[Option('g', "game", HelpText = "Game for which to update repositories")]
public string game { set; get; }

[OptionArray('u', "urls", HelpText = "URLs of repositories to update")]
public string[] repositoryURLs { get; set; }

[Option('f', "force", DefaultValue = false, HelpText = "Download and parse metadata even if it hasn't changed")]
public bool force { get; set; }
}

}
4 changes: 1 addition & 3 deletions Cmdline/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,11 @@ private static int RunSimpleAction(Options cmdline, CommonOptions options, strin
return Version(user);

case "update":
return (new Update(repoData, user)).RunCommand(GetGameInstance(manager), cmdline.options);
return (new Update(repoData, user, manager)).RunCommand(cmdline.options);

case "available":
return (new Available(repoData, user)).RunCommand(GetGameInstance(manager), cmdline.options);

case "add":
case "install":
Scan(GetGameInstance(manager), user, cmdline.action);
return (new Install(manager, repoData, user)).RunCommand(GetGameInstance(manager), cmdline.options);
Expand All @@ -247,7 +246,6 @@ private static int RunSimpleAction(Options cmdline, CommonOptions options, strin
case "search":
return (new Search(repoData, user)).RunCommand(GetGameInstance(manager), options);

case "uninstall":
case "remove":
return (new Remove(manager, repoData, user)).RunCommand(GetGameInstance(manager), cmdline.options);

Expand Down
6 changes: 4 additions & 2 deletions Cmdline/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,13 @@ Try `ckan list` for a list of installed mods.</value></data>
<data name="ShowFileName" xml:space="preserve"><value>Filename: {0}</value></data>
<data name="ShowVersionHeader" xml:space="preserve"><value>Version</value></data>
<data name="ShowGameVersionsHeader" xml:space="preserve"><value>Game Versions</value></data>
<data name="UpdateChangesSummary" xml:space="preserve"><value>Found {0} new modules, {1} removed modules and {2} updated modules</value></data>
<data name="UpdateBadGame" xml:space="preserve"><value>Game {0} not found! Valid options are: {1}</value></data>
<data name="UpdateURLRepoName" xml:space="preserve"><value>URL #{0}</value></data>
<data name="UpdateChangesSummary" xml:space="preserve"><value>Found {0} new modules, {1} removed modules, and {2} updated modules.</value></data>
<data name="UpdateAddedHeader" xml:space="preserve"><value>New modules [Name (CKAN identifier)]:</value></data>
<data name="UpdateRemovedHeader" xml:space="preserve"><value>Removed modules [Name (CKAN identifier)]:</value></data>
<data name="UpdateUpdatedHeader" xml:space="preserve"><value>Updated modules [Name (CKAN identifier)]:</value></data>
<data name="UpdateSummary" xml:space="preserve"><value>Updated information on {0} compatible modules</value></data>
<data name="UpdateSummary" xml:space="preserve"><value>Updated information on {0} modules.</value></data>
<data name="UpgradeCannotCombineFlags" xml:space="preserve"><value>The `--dev-build` and `--stable-release` flags cannot be combined!</value></data>
<data name="UpgradeSwitchingToDevBuilds" xml:space="preserve"><value>Switching to dev builds</value></data>
<data name="UpgradeSwitchingToStableReleases" xml:space="preserve"><value>Switching to stable releases</value></data>
Expand Down
2 changes: 1 addition & 1 deletion Core/Relationships/RelationshipResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ private void AddReason(CkanModule module, SelectionReason reason)
/// Depends relationships with suppress_recommendations=true,
/// to be applied to all recommendations and suggestions
/// </summary>
private HashSet<RelationshipDescriptor> suppressedRecommenders = new HashSet<RelationshipDescriptor>();
private readonly HashSet<RelationshipDescriptor> suppressedRecommenders = new HashSet<RelationshipDescriptor>();

private readonly IRegistryQuerier registry;
private readonly GameVersionCriteria versionCrit;
Expand Down
6 changes: 6 additions & 0 deletions Core/Repositories/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using Newtonsoft.Json;

using CKAN.Games;

namespace CKAN
{
public class Repository : IEquatable<Repository>
Expand Down Expand Up @@ -53,5 +55,9 @@ public override int GetHashCode()

public override string ToString()
=> string.Format("{0} ({1}, {2})", name, priority, uri);

public static Repository DefaultGameRepo(IGame game)
=> new Repository($"{game.ShortName}-{default_ckan_repo_name}",
game.DefaultRepositoryURL);
}
}
3 changes: 1 addition & 2 deletions Core/Repositories/RepositoryDataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,7 @@ public UpdateResult Update(Repository[] repos,

// Check if any ETags have changed, quit if not
user.RaiseProgress(Properties.Resources.NetRepoCheckingForUpdates, 0);
var toUpdate = repos.DefaultIfEmpty(new Repository($"{game.ShortName}-{Repository.default_ckan_repo_name}",
game.DefaultRepositoryURL))
var toUpdate = repos.DefaultIfEmpty(Repository.DefaultGameRepo(game))
.DistinctBy(r => r.uri)
.Where(r => r.uri != null
&& (r.uri.IsFile
Expand Down

0 comments on commit 1794ae9

Please sign in to comment.