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

Fix installation of metapackages from repositories #4192

Merged
merged 3 commits into from
Sep 23, 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
93 changes: 47 additions & 46 deletions Core/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ public static string CachedOrDownload(CkanModule module, string? userAgent, NetM
/// </summary>
private void DownloadModules(IEnumerable<CkanModule> mods, IDownloader downloader)
{
List<CkanModule> downloads = mods.Where(module => !Cache.IsCached(module)).ToList();

var downloads = mods.Where(module => !module.IsMetapackage && !Cache.IsCached(module))
.ToList();
if (downloads.Count > 0)
{
downloader.DownloadModules(downloads);
Expand Down Expand Up @@ -166,7 +166,9 @@ public void InstallList(ICollection<CkanModule> modules,
foreach (var module in modsToInstall)
{
User.RaiseMessage(" * {0}", Cache.DescribeAvailability(module));
if (!Cache.IsMaybeCachedZip(module))
// Alert about attempts to install DLC before downloading or installing anything
CheckKindInstallationKraken(module);
if (!module.IsMetapackage && !Cache.IsMaybeCachedZip(module))
{
downloads.Add(module);
}
Expand Down Expand Up @@ -242,8 +244,7 @@ private void Install(CkanModule module,
bool autoInstalled,
Registry registry,
ref HashSet<string>? possibleConfigOnlyDirs,
IProgress<int>? progress,
string? filename = null)
IProgress<int>? progress)
{
CheckKindInstallationKraken(module);
var version = registry.InstalledVersion(module.identifier);
Expand All @@ -256,15 +257,19 @@ private void Install(CkanModule module,
return;
}

// Find ZIP in the cache if we don't already have it.
filename ??= Cache.GetCachedFilename(module);

// If we *still* don't have a file, then kraken bitterly.
if (filename == null)
string? filename = null;
if (!module.IsMetapackage)
{
throw new FileNotFoundKraken(null,
string.Format(Properties.Resources.ModuleInstallerZIPNotInCache,
module));
// Find ZIP in the cache if we don't already have it.
filename ??= Cache.GetCachedFilename(module);

// If we *still* don't have a file, then kraken bitterly.
if (filename == null)
{
throw new FileNotFoundKraken(null,
string.Format(Properties.Resources.ModuleInstallerZIPNotInCache,
module));
}
}

using (var transaction = CkanTransaction.CreateTransactionScope())
Expand All @@ -285,19 +290,14 @@ private void Install(CkanModule module,

// Fire our callback that we've installed a module, if we have one.
onReportModInstalled?.Invoke(module);

}

/// <summary>
/// Check if the given module is a metapackage:
/// Check if the given module is a DLC:
/// if it is, throws a BadCommandKraken.
/// </summary>
private static void CheckKindInstallationKraken(CkanModule module)
{
if (module.IsMetapackage)
{
throw new BadCommandKraken(Properties.Resources.ModuleInstallerMetapackage);
}
if (module.IsDLC)
{
throw new BadCommandKraken(Properties.Resources.ModuleInstallerDLC);
Expand All @@ -313,14 +313,18 @@ private static void CheckKindInstallationKraken(CkanModule module)
/// Propagates a FileExistsKraken if we were going to overwrite a file.
/// </summary>
private List<string> InstallModule(CkanModule module,
string zip_filename,
string? zip_filename,
Registry registry,
ref HashSet<string>? possibleConfigOnlyDirs,
IProgress<int>? moduleProgress)
{
CheckKindInstallationKraken(module);
var createdPaths = new List<string>();

if (module.IsMetapackage || zip_filename == null)
{
// It's OK to include metapackages in changesets,
// but there's no work to do for them
return createdPaths;
}
using (ZipFile zipfile = new ZipFile(zip_filename))
{
var filters = ServiceLocator.Container.Resolve<IConfiguration>().GlobalInstallFilters
Expand Down Expand Up @@ -1057,23 +1061,22 @@ public HashSet<string> AddParentDirectories(HashSet<string> directories)
/// This *will* save the registry.
/// </summary>
/// <param name="add">Modules to add</param>
/// <param name="autoInstalled">true or false for each item in `add`</param>
/// <param name="remove">Modules to remove</param>
/// <param name="newModulesAreAutoInstalled">true if newly installed modules should be marked auto-installed, false otherwise</param>
private void AddRemove(ref HashSet<string>? possibleConfigOnlyDirs,
RegistryManager registry_manager,
IEnumerable<CkanModule> add,
IEnumerable<InstalledModule> remove,
bool enforceConsistency,
bool newModulesAreAutoInstalled)
ICollection<CkanModule> add,
ICollection<bool> autoInstalled,
ICollection<InstalledModule> remove,
bool enforceConsistency)
{
// TODO: We should do a consistency check up-front, rather than relying
// upon our registry catching inconsistencies at the end.

using (var tx = CkanTransaction.CreateTransactionScope())
{
remove = remove.Memoize();
add = add.Memoize();
int totSteps = remove.Count() + add.Count();
int totSteps = remove.Count + add.Count;
int step = 0;
foreach (InstalledModule instMod in remove)
{
Expand All @@ -1085,7 +1088,7 @@ private void AddRemove(ref HashSet<string>? possibleConfigOnlyDirs,
Uninstall(instMod.Module.identifier, ref possibleConfigOnlyDirs, registry_manager.registry);
}

foreach (CkanModule module in add)
foreach ((CkanModule module, bool autoInst) in add.Zip(autoInstalled))
{
var previous = remove?.FirstOrDefault(im => im.Module.identifier == module.identifier);
int percent_complete = (step++ * 70) / totSteps;
Expand All @@ -1095,7 +1098,7 @@ private void AddRemove(ref HashSet<string>? possibleConfigOnlyDirs,
// For upgrading, new modules are dependencies and should be marked auto-installed,
// for replacing, new modules are the replacements and should not be marked auto-installed
Install(module,
previous?.AutoInstalled ?? newModulesAreAutoInstalled,
previous?.AutoInstalled ?? autoInst,
registry_manager.registry,
ref possibleConfigOnlyDirs,
null);
Expand All @@ -1116,16 +1119,16 @@ private void AddRemove(ref HashSet<string>? possibleConfigOnlyDirs,
/// Will *re-install* or *downgrade* (with a warning) as well as upgrade.
/// Throws ModuleNotFoundKraken if a module is not installed.
/// </summary>
public void Upgrade(IEnumerable<CkanModule> modules,
public void Upgrade(ICollection<CkanModule> modules,
IDownloader netAsyncDownloader,
ref HashSet<string>? possibleConfigOnlyDirs,
RegistryManager registry_manager,
bool enforceConsistency = true,
bool resolveRelationships = false,
bool ConfirmPrompt = true)
{
modules = modules.Memoize();
var registry = registry_manager.registry;
var autoInstalled = modules.ToDictionary(m => m, m => true);

if (resolveRelationships)
{
Expand All @@ -1135,9 +1138,9 @@ public void Upgrade(IEnumerable<CkanModule> modules,
.OfType<CkanModule>(),
RelationshipResolverOptions.DependsOnlyOpts(),
registry,
instance.VersionCriteria()
);
modules = resolver.ModList().Memoize();
instance.VersionCriteria());
modules = resolver.ModList().ToArray();
autoInstalled = modules.ToDictionary(m => m, resolver.IsAutoInstalled);
}

User.RaiseMessage(Properties.Resources.ModuleInstallerAboutToUpgrade);
Expand Down Expand Up @@ -1258,9 +1261,9 @@ public void Upgrade(IEnumerable<CkanModule> modules,
AddRemove(ref possibleConfigOnlyDirs,
registry_manager,
modules,
modules.Select(m => autoInstalled[m]).ToArray(),
to_remove,
enforceConsistency,
true);
enforceConsistency);
User.RaiseProgress(Properties.Resources.ModuleInstallerDone, 100);
}

Expand Down Expand Up @@ -1353,14 +1356,12 @@ public void Replace(IEnumerable<ModuleReplacement> replacements,
}
var resolver = new RelationshipResolver(modsToInstall, null, options, registry_manager.registry, instance.VersionCriteria());
var resolvedModsToInstall = resolver.ModList().ToList();
AddRemove(
ref possibleConfigOnlyDirs,
registry_manager,
resolvedModsToInstall,
modsToRemove,
enforceConsistency,
false
);
AddRemove(ref possibleConfigOnlyDirs,
registry_manager,
resolvedModsToInstall,
resolvedModsToInstall.Select(m => false).ToArray(),
modsToRemove,
enforceConsistency);
User.RaiseProgress(Properties.Resources.ModuleInstallerDone, 100);
}

Expand Down
5 changes: 0 additions & 5 deletions Core/Relationships/RelationshipResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,11 +421,6 @@ private string conflictingModDescription(CkanModule? mod, CkanModule? parent)
/// </summary>
private void Add(CkanModule module, SelectionReason reason)
{
if (module.IsMetapackage)
{
AddReason(module, reason);
return;
}
if (module.IsDLC)
{
throw new ModuleIsDLCKraken(module);
Expand Down
6 changes: 6 additions & 0 deletions GUI/Controls/ManageMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,12 @@ private void guiModule_PropertyChanged(object? sender, PropertyChangedEventArgs?
RegistryManager.Instance(currentInstance, repoData).registry);
});
break;

case "IsAutoInstalled":
// Update the changeset
UpdateChangeSetAndConflicts(currentInstance,
RegistryManager.Instance(currentInstance, repoData).registry);
break;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion GUI/Main/MainChangeset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private void Changeset_OnConfirmChanges(List<ModChange> changeset)
{
Wait.StartWaiting(InstallMods, PostInstallMods, true,
new InstallArgument(
// Only pass along user requested mods, so auto-installed can be determined
// Only pass along user requested mods, so auto-installed can be determined
changeset.Where(ch => ch.Reasons.Any(r => r is SelectionReason.UserRequested)
// Include all removes and upgrades
|| ch.ChangeType != GUIModChangeType.Install)
Expand Down
5 changes: 2 additions & 3 deletions GUI/Model/GUIMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private void OnPropertyChanged([CallerMemberName] string? name = null)

public string Name { get; private set; }
public bool IsInstalled { get; private set; }
public bool IsAutoInstalled { get; private set; }
public bool IsAutoInstalled => InstalledMod?.AutoInstalled ?? false;
public bool HasUpdate { get; set; }
public bool HasReplacement { get; private set; }
public bool IsIncompatible { get; private set; }
Expand Down Expand Up @@ -128,7 +128,6 @@ public GUIMod(InstalledModule instMod,
InstalledMod = instMod;
selectedMod = registry.GetModuleByVersion(instMod.identifier, instMod.Module.version)
?? instMod.Module;
IsAutoInstalled = instMod.AutoInstalled;
InstallDate = instMod.InstallTime;

InstalledVersion = instMod.Module.version.ToString(hideEpochs, hideV);
Expand Down Expand Up @@ -342,8 +341,8 @@ public void SetAutoInstallChecked(DataGridViewRow row, DataGridViewColumn col, b
var old_value = (bool) auto_cell.Value;

bool value = set_value_to ?? old_value;
IsAutoInstalled = value;
InstalledMod.AutoInstalled = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsAutoInstalled"));

if (old_value != value)
{
Expand Down
1 change: 0 additions & 1 deletion GUI/Model/ModList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ public Tuple<IEnumerable<ModChange>, Dictionary<CkanModule, string>, List<string
.Union(resolver.ModList()
// Changeset already contains changes for these
.Except(extraInstalls)
.Where(m => !m.IsMetapackage)
.Select(m => new ModChange(m, GUIModChangeType.Install, resolver.ReasonsFor(m)))),
resolver.ConflictList,
resolver.ConflictDescriptions.ToList());
Expand Down
8 changes: 8 additions & 0 deletions Netkan/Transformers/GithubTransformer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ private Metadata TransformOne(Metadata metadata,
json.SafeAdd("author", () => GetAuthors(ghRepo, ghRelease));
json.Remove("$kref");
json.SafeAdd("download", ghAsset.Download.ToString());
if (ghRef.UseSourceArchive)
{
// https://docs.github.com/en/rest/repos/contents#download-a-repository-archive-zip
// GitHub requires clients to specify JSON format
// to download a source ZIP (!!!)
json.SafeAdd("download_content_type",
"application/vnd.github+json");
}
json.SafeAdd(Metadata.UpdatedPropertyName, ghAsset.Updated);

if (ghRef.Project.Contains('_'))
Expand Down
3 changes: 2 additions & 1 deletion Tests/Core/ModuleInstallerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1226,7 +1226,8 @@ public void Upgrade_WithAutoInst_RemovesAutoRemovable(string[] regularMods,
// Act
installer.Upgrade(upgradeIdentifiers.Select(ident =>
registry.LatestAvailable(ident, inst.KSP.VersionCriteria()))
.OfType<CkanModule>(),
.OfType<CkanModule>()
.ToArray(),
downloader, ref possibleConfigOnlyDirs, regMgr, false);

// Assert
Expand Down