public ISnPatch[] GetExecutablePatches(IEnumerable <ISnPatch> candidates, SnComponentDescriptor[] installedComponents, PatchExecutionContext context, out SnComponentDescriptor[] componentsAfter) { var patches = candidates.ToArray(); var installedIds = installedComponents .Where(x => x.Version != null && x.Version > NullVersion) .Select(x => x.ComponentId) .ToArray(); var installers = patches .Where(x => x.Type == PackageType.Install && !installedIds.Contains(x.ComponentId)) .OrderBy(x => x.ComponentId) .ToArray(); var installerGroups = installers.GroupBy(x => x.ComponentId); var duplicates = installerGroups.Where(x => x.Count() > 1).Select(x => x.Key).ToArray(); if (duplicates.Length > 0) { // Duplicates are not allowed foreach (var id in duplicates) { var message = "There is a duplicated installer for the component " + id; var faultyPatches = installers.Where(inst => inst.ComponentId == id).ToArray(); var error = new PatchExecutionError(PatchExecutionErrorType.DuplicatedInstaller, faultyPatches, message); var logRecord = new PatchExecutionLogRecord(PatchExecutionEventType.DuplicatedInstaller, faultyPatches.First(), message); context.Errors.Add(error); context.LogCallback(logRecord); } // Set unchanged list as output componentsAfter = installedComponents.ToArray(); return(new ISnPatch[0]); } // Order patches by componentId and versions var orderedSnPatches = patches .Where(x => x.Type == PackageType.Patch) .OrderBy(x => x.ComponentId).ThenBy(x => x.Version) .ToArray(); // ------------------------------------------------------------ sorting by dependencies var inputList = installers.Union(orderedSnPatches).ToList(); var outputList = new List <ISnPatch>(); // patches in right order to execute var installed = new List <SnComponentDescriptor>(installedComponents); // all simulated components. var currentlyManaged = new List <ISnPatch>(); // temporary list in one iteration. while (true) { foreach (var item in inputList) { if (CheckPrerequisites(item, installed, out var skipExecution, out var error)) { currentlyManaged.Add(item); outputList.Add(item); if (item is SnPatch snPatch) { // Modify version and dependencies of the installed components. var patchedComponent = installed.Single(x => x.ComponentId == snPatch.ComponentId); patchedComponent.Version = (Version)snPatch.Version.Clone(); patchedComponent.Dependencies = snPatch.Dependencies?.ToArray(); } else if (item is ComponentInstaller installer) { // Add to installed components. installed.Add(new SnComponentDescriptor(installer.ComponentId, installer.Version, installer.Description, installer.Dependencies?.ToArray())); } else { throw new SnNotSupportedException(); } }
private void RecognizeErrors(List <ISnPatch> notExecutables, List <ISnPatch> candidates, List <SnComponentDescriptor> installed, bool onBefore) { // Recognize duplicated installers var installers = notExecutables .Where(x => x.Type == PackageType.Install) .OrderBy(x => x.ComponentId) .ToArray(); var installerGroups = installers.GroupBy(x => x.ComponentId); var duplicates = installerGroups.Where(x => x.Count() > 1).Select(x => x.Key).ToArray(); if (duplicates.Length > 0) { // Duplicates are not allowed foreach (var id in duplicates) { var message = "There is a duplicated installer for the component " + id; var faultyPatches = installers.Where(inst => inst.ComponentId == id).ToArray(); var error = new PatchExecutionError(PatchExecutionErrorType.DuplicatedInstaller, faultyPatches, message); var logRecord = new PatchExecutionLogRecord(PatchExecutionEventType.DuplicatedInstaller, faultyPatches.First(), message); _context.Errors.Add(error); _context.LogCallback(logRecord); notExecutables = notExecutables.Except(faultyPatches).ToList(); foreach (var item in faultyPatches) { // notExecutables and candidates maybe the same notExecutables.Remove(item); candidates.Remove(item); } } } // Recognize remaining items var toRemove = new List <ISnPatch>(); foreach (var patch in notExecutables) { if (!onBefore) { toRemove.Add(patch); } if (patch is SnPatch snPatch) { if (!installed.Any(comp => comp.ComponentId == snPatch.ComponentId && (comp.Version == null || comp.Version < patch.Version) && snPatch.Boundary.ContainsVersion(comp.Version))) { var component = installed.FirstOrDefault(c => c.ComponentId == patch.ComponentId); var message = component == null ? $"Cannot execute the patch [{patch}] {(onBefore ? "before" : "after")} " + "repository start because the component is not yet installed." : $"Cannot execute the patch [{patch}] {(onBefore ? "before" : "after")} " + $"repository start because the component's version is small: [{component}]."; _context.LogCallback( new PatchExecutionLogRecord(PatchExecutionEventType.CannotExecuteMissingVersion, patch, message)); _context.Errors.Add(new PatchExecutionError(PatchExecutionErrorType.MissingVersion, patch, message)); } } else { var message = $"Cannot execute the patch [{patch}] {(onBefore ? "before" : "after")} repository start."; _context.LogCallback(new PatchExecutionLogRecord( onBefore ? PatchExecutionEventType.CannotExecuteOnBefore : PatchExecutionEventType.CannotExecuteOnAfter, patch, message)); _context.Errors.Add(new PatchExecutionError( onBefore ? PatchExecutionErrorType.CannotExecuteOnBefore : PatchExecutionErrorType.CannotExecuteOnAfter, patch, message + $" [{patch}]")); } } foreach (var item in toRemove) { candidates.Remove(item); } }