private bool CheckVersionRequirements(ProfileManifest16 manifest, out IList <StatusReportItem> problems) { List <StatusReportItem> report = new List <StatusReportItem>(); if (manifest.VersionsRequired == null) { problems = report; return(true); } foreach (VersionRequired versionRequired in manifest.VersionsRequired) { if (!FetchVersion(versionRequired.Product, out Version version)) { report.Add(new StatusReportItem { Status = $"Helios does not recognize product '{versionRequired.Product}' which is required by this archive", Recommendation = "find a version of this archive for the Helios distribution that you have or manually edit the archive contents at your own risk", Severity = StatusReportItem.SeverityCode.Warning, Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate }); continue; } if (versionRequired.Minimum != null && version < versionRequired.Minimum) { report.Add(new StatusReportItem { Status = $"This archive requires {versionRequired.Product} version {versionRequired.Minimum} or higher. You have version {version}.", Recommendation = $"upgrade {versionRequired.Product} or find a version of this archive for the {versionRequired.Product} version you have", Severity = StatusReportItem.SeverityCode.Warning, Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate }); continue; } if (versionRequired.Maximum != null && version > versionRequired.Maximum) { report.Add(new StatusReportItem { Status = $"This archive requires {versionRequired.Product} version {versionRequired.Maximum} or lower. You have version {version}.", Recommendation = $"find a version of this archive for the {versionRequired.Product} version you have", Severity = StatusReportItem.SeverityCode.Warning, Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate }); } } // return success only if we reported nothing problems = report; return(!report.Any()); }
private bool ShowWelcome(IInstallationCallbacks2 callbacks, ProfileManifest16 manifest) { if (string.IsNullOrEmpty(manifest.Description) && (manifest.Info == null || !manifest.Info.Any())) { // nothing to present, continue return(true); } List <StructuredInfo> info = new List <StructuredInfo>(); // add info that has its own properties if (manifest.Authors != null) { info.AddRange(manifest.Authors.Select(manifestAuthor => new StructuredInfo("Author", manifestAuthor))); } if (!string.IsNullOrEmpty(manifest.License)) { info.Add(new StructuredInfo("License", manifest.License)); } if (!string.IsNullOrEmpty(manifest.Version)) { info.Add(new StructuredInfo("Version", manifest.Version)); } // gather structured info if (null != manifest.Info) { info.AddRange(manifest.Info); } return(callbacks.DangerPrompt( $"{ArchivePath} Installation", $"Helios is about to install: {manifest.Description ?? ArchivePath}", info, new List <StatusReportItem>()) == InstallationPromptResult.Ok); }
public InstallationResult Install(IInstallationCallbacks2 callbacks) { ProfileManifest16 manifest = null; try { using (ZipArchive archive = ZipFile.Open(_archivePath, ZipArchiveMode.Read)) { ZipArchiveEntry manifestEntry = archive.Entries.FirstOrDefault(entry => entry.FullName.Equals(MANIFEST_PATH, StringComparison.CurrentCultureIgnoreCase)); if (manifestEntry != null) { manifest = LoadManifest(manifestEntry); if (manifest == null) { // this cannot happen because we would get a parse failure? maybe empty file? throw new Exception($"JSON manifest {MANIFEST_PATH} could not be parsed"); } // see if we are allowed to install this package if (!CheckVersionRequirements(manifest, out IList <StatusReportItem> problems)) { if (RunningVersion.IsDevelopmentPrototype || ConfigManager.SettingsManager.LoadSetting("ArchiveInstall", "VersionOverride", false)) { InstallationPromptResult result = callbacks.DangerPrompt($"{ArchivePath} Installation", "Installation requirements are not met. Do you want to install anyway?", problems); if (result == InstallationPromptResult.Cancel) { return(InstallationResult.Canceled); } } else { callbacks.Failure($"{ArchivePath} cannot be installed", "Installation requirements are not met", problems); return(InstallationResult.Canceled); } } // present welcome screen, if applicable if (!ShowWelcome(callbacks, manifest)) { callbacks.Failure($"{ArchivePath} installation canceled", "Installation canceled by user", new List <StatusReportItem>()); return(InstallationResult.Canceled); } // process selection and build exclusion list foreach (Choice choice in manifest.Choices) { // filter options based on version requirements List <StatusReportItem> details = new List <StatusReportItem>(); foreach (Option option in choice.Options) { if (!CheckVersionRequirements(option, out IList <StatusReportItem> optionDetails)) { // this option is not allowed details.AddRange(optionDetails); option.IsValid = false; option.ValidityNarrative = "Version requirements not met"; } } // check if we still have viable choices if (!choice.Options.Any()) { callbacks.Failure($"{ArchivePath} cannot be installed", "None of the options for a required choice are valid for your installation.", details); return(InstallationResult.Fatal); } // fix up dialog if not specified choice.Message = choice.Message ?? "The Profile Archive being installed contains multiple versions of some of its files. Please choose one version to install:"; // wait for response Option selected = PresentChoice(choice); // check if dialog closed or canceled if (selected == null) { return(InstallationResult.Canceled); } // process results if (selected?.PathExclusions != null) { foreach (string path in selected.PathExclusions) { _pathExclusions.Add(path); } } } } // first check if we have a whole lot of writable files, in which case ask the user if the overwrite strategy should be changed CheckForFirstArchiveInstall(archive); // now unpack everything IList <StatusReportItem> report = archive.Entries .Where(NotFilteredVerbose) .Select(Unpack) .Where(item => item != null) .ToList(); callbacks.Success($"Installed {ArchivePath}", $"Files were installed into {Anonymizer.Anonymize(ConfigManager.DocumentPath)}", report); } return(InstallationResult.Success); } catch (Exception ex) { callbacks.Failure($"Failed to install {ArchivePath}", $"Attempt to install Helios Profile from '{Anonymizer.Anonymize(_archivePath)}' failed", ReportException(ex)); return(InstallationResult.Fatal); } }