Exemple #1
0
        /// <summary>
        /// Find installed modules that have different metadata in their equivalent available module
        /// </summary>
        /// <param name="registry">Registry to scan</param>
        /// <returns>
        /// List of CkanModules that are available and have changed metadata
        /// </returns>
        private static List <CkanModule> GetChangedInstalledModules(Registry registry)
        {
            List <CkanModule> metadataChanges = new List <CkanModule>();

            foreach (InstalledModule installedModule in registry.InstalledModules)
            {
                string identifier = installedModule.identifier;

                ModuleVersion installedVersion = registry.InstalledVersion(identifier);
                if (!(registry.available_modules.ContainsKey(identifier)))
                {
                    log.InfoFormat("UpdateRegistry, module {0}, version {1} not in registry", identifier, installedVersion);
                    continue;
                }

                if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion))
                {
                    continue;
                }

                // if the mod is installed and the metadata is different we have to reinstall it
                CkanModule metadata = registry.available_modules[identifier].module_version[installedVersion];

                CkanModule oldMetadata = registry.InstalledModule(identifier).Module;

                if (!MetadataEquals(metadata, oldMetadata))
                {
                    metadataChanges.Add(registry.available_modules[identifier].module_version[installedVersion]);
                }
            }
            return(metadataChanges);
        }
Exemple #2
0
        /// <summary>
        /// This function returns a changeset based on the selections of the user.
        /// Currently returns null if a conflict is detected.
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="changeSet"></param>
        /// <param name="installer">A module installer for the current KSP install</param>
        /// <param name="version">The version of the current KSP install</param>
        public async Task<IEnumerable<KeyValuePair<GUIMod, GUIModChangeType>>> ComputeChangeSetFromModList(
            Registry registry, HashSet<KeyValuePair<GUIMod, GUIModChangeType>> changeSet, ModuleInstaller installer,
            KSPVersion version)
        {
            var modules_to_install = new HashSet<CkanModule>();
            var modules_to_remove = new HashSet<Module>();
            var options = new RelationshipResolverOptions
            {
                without_toomanyprovides_kraken = false,
                with_recommends = false
            };

            foreach (var change in changeSet)
            {
                switch (change.Value)
                {
                    case GUIModChangeType.None:
                        break;
                    case GUIModChangeType.Install:
                        modules_to_install.Add(change.Key.ToCkanModule());
                        break;
                    case GUIModChangeType.Remove:
                        modules_to_remove.Add(change.Key);
                        break;
                    case GUIModChangeType.Update:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
            var installed_modules =
                registry.InstalledModules.Select(imod => imod.Module).ToDictionary(mod => mod.identifier, mod => mod);


            bool handled_all_to_many_provides = false;
            while (!handled_all_to_many_provides)
            {
                //Can't await in catch clause - doesn't seem to work in mono. Hence this flag
                TooManyModsProvideKraken kraken;
                try
                {
                    new RelationshipResolver(modules_to_install.ToList(), options, registry, version);
                    handled_all_to_many_provides = true;
                    continue;
                }
                catch (TooManyModsProvideKraken k)
                {
                    kraken = k;
                }
                catch (ModuleNotFoundKraken k)
                {
                    //We shouldn't need this. However the relationship provider will throw TMPs with incompatible mods.
                    user.RaiseError("Module {0} has not been found. This may be because it is not compatible " +
                                    "with the currently installed version of KSP", k.module);
                    return null;
                }
                //Shouldn't get here unless there is a kraken.
                var mod = await too_many_provides(kraken);
                if (mod != null)
                {
                    modules_to_install.Add(mod);
                }
                else
                {
                    //TODO Is could be a new type of Kraken.
                    throw kraken;
                }
            }


            foreach (var dependency in modules_to_remove.
                Select(mod => installer.FindReverseDependencies(mod.identifier)).
                SelectMany(reverse_dependencies => reverse_dependencies))
            {
                //TODO This would be a good place to have a event that alters the row's graphics to show it will be removed
                Module module_by_version = registry.GetModuleByVersion(installed_modules[dependency].identifier,
                    installed_modules[dependency].version) ?? registry.InstalledModule(dependency).Module;
                changeSet.Add(
                    new KeyValuePair<GUIMod, GUIModChangeType>(
                        new GUIMod(module_by_version, registry, version), GUIModChangeType.Remove));
            }
            //May throw InconsistentKraken
            var resolver = new RelationshipResolver(options, registry, version);
            resolver.RemoveModsFromInstalledList(
                changeSet.Where(change => change.Value.Equals(GUIModChangeType.Remove)).Select(m => m.Key.ToModule()));
            resolver.AddModulesToInstall(modules_to_install.ToList());
            changeSet.UnionWith(
                resolver.ModList()
                    .Select(
                        mod =>
                            new KeyValuePair<GUIMod, GUIModChangeType>(new GUIMod(mod, registry, version),
                                GUIModChangeType.Install)));


            return changeSet;
        }
Exemple #3
0
        /// <summary>
        /// Updates the supplied registry from the URL given.
        /// This does not *save* the registry. For that, you probably want Repo.Update
        /// </summary>
        internal static void UpdateRegistry(Uri repo, Registry registry, KSP ksp, IUser user, Boolean clear = true)
        {
            // Use this opportunity to also update the build mappings... kind of hacky
            ServiceLocator.Container.Resolve <IKspBuildMap>().Refresh();

            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo);
            }
            catch (System.Net.WebException)
            {
                user.RaiseMessage("Connection to {0} could not be established.", repo);
                return;
            }

            // Clear our list of known modules.
            if (clear)
            {
                registry.ClearAvailable();
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            switch (type)
            {
            case FileType.TarGz:
                UpdateRegistryFromTarGz(repo_file, registry);
                break;

            case FileType.Zip:
                UpdateRegistryFromZip(repo_file, registry);
                break;

            default:
                break;
            }

            List <CkanModule> metadataChanges = new List <CkanModule>();

            foreach (var installedModule in registry.InstalledModules)
            {
                var identifier = installedModule.identifier;

                var installedVersion = registry.InstalledVersion(identifier);
                if (!(registry.available_modules.ContainsKey(identifier)))
                {
                    log.InfoFormat("UpdateRegistry, module {0}, version {1} not in repository ({2})", identifier, installedVersion, repo);
                    continue;
                }

                if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion))
                {
                    continue;
                }

                // if the mod is installed and the metadata is different we have to reinstall it
                var metadata = registry.available_modules[identifier].module_version[installedVersion];

                var oldMetadata = registry.InstalledModule(identifier).Module;

                bool same = true;
                if ((metadata.install == null) != (oldMetadata.install == null) ||
                    (metadata.install != null && metadata.install.Length != oldMetadata.install.Length))
                {
                    same = false;
                }
                else
                {
                    if (metadata.install != null)
                    {
                        for (int i = 0; i < metadata.install.Length; i++)
                        {
                            if (metadata.install[i].file != oldMetadata.install[i].file)
                            {
                                same = false;
                                break;
                            }

                            if (metadata.install[i].install_to != oldMetadata.install[i].install_to)
                            {
                                same = false;
                                break;
                            }

                            if (metadata.install[i].@as != oldMetadata.install[i].@as)
                            {
                                same = false;
                                break;
                            }

                            if ((metadata.install[i].filter == null) != (oldMetadata.install[i].filter == null))
                            {
                                same = false;
                                break;
                            }


                            if (metadata.install[i].filter != null)
                            {
                                if (!metadata.install[i].filter.SequenceEqual(oldMetadata.install[i].filter))
                                {
                                    same = false;
                                    break;
                                }
                            }

                            if ((metadata.install[i].filter_regexp == null) != (oldMetadata.install[i].filter_regexp == null))
                            {
                                same = false;
                                break;
                            }

                            if (metadata.install[i].filter_regexp != null)
                            {
                                if (!metadata.install[i].filter_regexp.SequenceEqual(oldMetadata.install[i].filter_regexp))
                                {
                                    same = false;
                                    break;
                                }
                            }
                        }
                    }
                }

                if (!RelationshipsAreEquivalent(metadata.conflicts, oldMetadata.conflicts))
                {
                    same = false;
                }

                if (!RelationshipsAreEquivalent(metadata.depends, oldMetadata.depends))
                {
                    same = false;
                }

                if (!RelationshipsAreEquivalent(metadata.recommends, oldMetadata.recommends))
                {
                    same = false;
                }

                if (metadata.provides != oldMetadata.provides)
                {
                    if (metadata.provides == null || oldMetadata.provides == null)
                    {
                        same = false;
                    }
                    else if (!metadata.provides.OrderBy(i => i).SequenceEqual(oldMetadata.provides.OrderBy(i => i)))
                    {
                        same = false;
                    }
                }

                if (!same)
                {
                    metadataChanges.Add(registry.available_modules[identifier].module_version[installedVersion]);
                }
            }

            if (metadataChanges.Any())
            {
                var sb = new StringBuilder();

                for (var i = 0; i < metadataChanges.Count; i++)
                {
                    var module = metadataChanges[i];

                    sb.AppendLine(string.Format("- {0} {1}", module.identifier, module.version));
                }

                if (user.RaiseYesNoDialog(string.Format(@"The following mods have had their metadata changed since last update:

{0}
You should reinstall them in order to preserve consistency with the repository.

Do you wish to reinstall now?", sb)))
                {
                    ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser());
                    // New upstream metadata may break the consistency of already installed modules
                    // e.g. if user installs modules A and B and then later up A is made to conflict with B
                    // This is perfectly normal and shouldn't produce an error, therefore we skip enforcing
                    // consistency. However, we will show the user any inconsistencies later on.

                    // Use the identifiers so we use the overload that actually resolves relationships
                    // Do each changed module one at a time so a failure of one doesn't cause all the others to fail
                    foreach (var changedIdentifier in metadataChanges.Select(i => i.identifier))
                    {
                        try
                        {
                            installer.Upgrade(
                                new[] { changedIdentifier },
                                new NetAsyncModulesDownloader(new NullUser()),
                                enforceConsistency: false
                                );
                        }
                        // Thrown when a dependency couldn't be satisfied
                        catch (ModuleNotFoundKraken)
                        {
                            log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier);
                            user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier);
                        }
                        // Thrown when a conflicts relationship is violated
                        catch (InconsistentKraken)
                        {
                            log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier);
                            user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier);
                        }
                    }
                }
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);
        }
Exemple #4
0
        /// <summary>
        /// Updates the supplied registry from the URL given.
        /// This does not *save* the registry. For that, you probably want Repo.Update
        /// </summary>
        internal static void UpdateRegistry(Uri repo, Registry registry, KSP ksp, IUser user, Boolean clear = true)
        {
            // Use this opportunity to also update the build mappings... kind of hacky
            ServiceLocator.Container.Resolve<IKspBuildMap>().Refresh();

            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;
            try
            {
                repo_file = Net.Download(repo);
            }
            catch (System.Net.WebException)
            {
                user.RaiseMessage("Connection to {0} could not be established.", repo);
                return;
            }

            // Clear our list of known modules.
            if (clear)
            {
                registry.ClearAvailable();
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            switch (type)
            {
            case FileType.TarGz:
                UpdateRegistryFromTarGz (repo_file, registry);
                break;
            case FileType.Zip:
                UpdateRegistryFromZip (repo_file, registry);
                break;
            default:
                break;
            }

            List<CkanModule> metadataChanges = new List<CkanModule>();

            foreach (var installedModule in registry.InstalledModules)
            {
                var identifier = installedModule.identifier;

                var installedVersion = registry.InstalledVersion(identifier);
                if (!(registry.available_modules.ContainsKey(identifier)))
                {
                    log.InfoFormat("UpdateRegistry, module {0}, version {1} not in repository ({2})", identifier, installedVersion, repo);
                    continue;
                }

                if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion))
                {
                    continue;
                }

                // if the mod is installed and the metadata is different we have to reinstall it
                var metadata = registry.available_modules[identifier].module_version[installedVersion];

                var oldMetadata = registry.InstalledModule(identifier).Module;

                bool same = true;
                if ((metadata.install == null) != (oldMetadata.install == null) ||
                    (metadata.install != null && metadata.install.Length != oldMetadata.install.Length))
                {
                    same = false;
                }
                else
                {
                    if(metadata.install != null)
                    for (int i = 0; i < metadata.install.Length; i++)
                    {
                        if (metadata.install[i].file != oldMetadata.install[i].file)
                        {
                            same = false;
                            break;
                        }

                        if (metadata.install[i].install_to != oldMetadata.install[i].install_to)
                        {
                            same = false;
                            break;
                        }

                        if (metadata.install[i].@as != oldMetadata.install[i].@as)
                        {
                            same = false;
                            break;
                        }

                        if ((metadata.install[i].filter == null) != (oldMetadata.install[i].filter == null))
                        {
                            same = false;
                            break;
                        }

                        if(metadata.install[i].filter != null)
                        if (!metadata.install[i].filter.SequenceEqual(oldMetadata.install[i].filter))
                        {
                            same = false;
                            break;
                        }

                        if ((metadata.install[i].filter_regexp == null) != (oldMetadata.install[i].filter_regexp == null))
                        {
                            same = false;
                            break;
                        }

                        if(metadata.install[i].filter_regexp != null)
                        if (!metadata.install[i].filter_regexp.SequenceEqual(oldMetadata.install[i].filter_regexp))
                        {
                            same = false;
                            break;
                        }
                    }
                }

                if (!RelationshipsAreEquivalent(metadata.conflicts, oldMetadata.conflicts))
                    same = false;

                if (!RelationshipsAreEquivalent(metadata.depends, oldMetadata.depends))
                    same = false;

                if (!RelationshipsAreEquivalent(metadata.recommends, oldMetadata.recommends))
                    same = false;

                if (metadata.provides != oldMetadata.provides)
                {
                    if (metadata.provides == null || oldMetadata.provides == null)
                        same = false;
                    else if (!metadata.provides.OrderBy(i => i).SequenceEqual(oldMetadata.provides.OrderBy(i => i)))
                        same = false;
                }

                if (!same)
                {
                    metadataChanges.Add(registry.available_modules[identifier].module_version[installedVersion]);
                }
            }

            if (metadataChanges.Any())
            {
                var sb = new StringBuilder();

                for (var i = 0; i < metadataChanges.Count; i++)
                {
                    var module = metadataChanges[i];

                    sb.AppendLine(string.Format("- {0} {1}", module.identifier, module.version));
                }

                if(user.RaiseYesNoDialog(string.Format(@"The following mods have had their metadata changed since last update:

            {0}
            You should reinstall them in order to preserve consistency with the repository.

            Do you wish to reinstall now?", sb)))
                {
                    ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser());
                    // New upstream metadata may break the consistency of already installed modules
                    // e.g. if user installs modules A and B and then later up A is made to conflict with B
                    // This is perfectly normal and shouldn't produce an error, therefore we skip enforcing
                    // consistency. However, we will show the user any inconsistencies later on.

                    // Use the identifiers so we use the overload that actually resolves relationships
                    // Do each changed module one at a time so a failure of one doesn't cause all the others to fail
                    foreach (var changedIdentifier in metadataChanges.Select(i => i.identifier))
                    {
                        try
                        {
                            installer.Upgrade(
                                new[] { changedIdentifier },
                                new NetAsyncModulesDownloader(new NullUser()),
                                enforceConsistency: false
                            );
                        }
                        // Thrown when a dependency couldn't be satisfied
                        catch(ModuleNotFoundKraken)
                        {
                            log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier);
                            user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier);
                        }
                        // Thrown when a conflicts relationship is violated
                        catch (InconsistentKraken)
                        {
                            log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier);
                            user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier);
                        }
                    }
                }
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);
        }