fix: parse project rosters with legacy-compatible semantics

This commit is contained in:
codex
2026-08-06 15:14:08 +03:00
parent f460e87253
commit 2c578576d7
8 changed files with 118 additions and 25 deletions
+25 -7
View File
@@ -17,6 +17,7 @@ public partial class MainWindow : System.Windows.Window
private LauncherProduct? selected;
private string? projectPath;
private string? manualRosterPath;
private string? selectedRosterSnapshotPath;
private string stage = "ProductSelection";
private string code = "PRODUCT_NOT_SELECTED";
private string detail = "";
@@ -105,10 +106,10 @@ public partial class MainWindow : System.Windows.Window
pattern = localProfile is not null && selected.Id == "plant3d" ? localProfile.PatternName : product.Pattern;
expectedRoster = manualRosterPath ?? LegacyProductSelections.AutomaticRoster(product, projectPath);
Transition("AutomaticRosterPathResolved", "AUTOMATIC_ROSTER_PATH_RESOLVED", "Поиск списка плагинов", expectedRoster);
var plan = LegacyProductSelections.ReadRoster(product, projectPath, manualRosterPath, adapter);
if (localProfile is not null && selected.Id == "plant3d" && !string.Equals(Path.GetFullPath(plan.RosterPath), Path.GetFullPath(localProfile.DownloadListPath), StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("LOCAL_PARITY_ROSTER_PROFILE_MISMATCH");
var plan = LegacyProductSelections.ReadRoster(product, projectPath, manualRosterPath, adapter, localProfile?.Root);
orderedIds = string.Join(", ", plan.OrderedIds);
selectedRosterSnapshotPath = plan.SnapshotPath ?? plan.RosterPath;
if (plan.SnapshotSha256 is not null) detail = $"Roster snapshot SHA-256: {plan.SnapshotSha256}";
Plugins.ItemsSource = plan.OrderedIds;
Plugins.IsEnabled = false;
PluginStatus.Text = "Источник списка: " + (manualRosterPath is null ? "Автоматически" : "Выбран вручную");
@@ -126,7 +127,14 @@ public partial class MainWindow : System.Windows.Window
Transition("PackageMappingFailed", "PACKAGE_MAPPING_NOT_FOUND", "Для выбранных плагинов отсутствуют пакеты 2026");
return;
}
ValidateLocalProfileDefinitions(plan);
var unmapped = EvaluateLocalProfileDefinitions(plan);
if (unmapped.Count > 0)
{
mappingState = "Known legacy plugin, package 2026 absent: " + string.Join(", ", unmapped);
StartButton.IsEnabled = false;
Transition("PackageMappingFailed", "PACKAGE_MAPPING_NOT_FOUND", "Для части плагинов проекта пока отсутствуют пакеты версии 2026");
return;
}
mappingState = "Mapped";
StartButton.IsEnabled = true;
Transition("Ready", "READY", "Готово к запуску");
@@ -134,13 +142,22 @@ public partial class MainWindow : System.Windows.Window
catch (Exception ex) { ReportFailure(ex, "RosterResolutionFailed"); }
}
private void ValidateLocalProfileDefinitions(LegacyPluginPlan plan)
private IReadOnlyList<string> EvaluateLocalProfileDefinitions(LegacyPluginPlan plan)
{
var settings = adapter.ParseSettings(File.ReadAllText(localProfile!.SettingsPath));
var selectedPattern = settings.Patterns.SingleOrDefault(x => string.Equals(x.Name, localProfile.PatternName, StringComparison.Ordinal));
if (selectedPattern is null) throw new InvalidOperationException("LOCAL_PARITY_PATTERN_NOT_FOUND");
var overlay = LegacyCompatibilityOverlayParser.Parse(File.ReadAllText(localProfile.OverlayPath));
adapter.Join(selectedPattern, new LegacyDownloadSelection(plan.OrderedIds), overlay);
var missing = new List<string>();
foreach (var id in plan.OrderedIds.Distinct(StringComparer.OrdinalIgnoreCase))
{
var definitions = selectedPattern.Plugins.Where(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)).ToArray();
if (definitions.Length == 0) throw new InvalidOperationException("LEGACY_PLUGIN_ID_NOT_DEFINED");
if (definitions.Length > 1) throw new InvalidOperationException("AMBIGUOUS_LEGACY_PLUGIN_DEFINITION");
if (!definitions[0].IsAllowedOn(Environment.MachineName)) { missing.Add(id + " (machine restricted)"); continue; }
if (!overlay.Plugins.Any(x => string.Equals(x.LegacyId, id, StringComparison.OrdinalIgnoreCase))) missing.Add(id);
}
return missing;
}
private async void Start_Click(object sender, System.Windows.RoutedEventArgs e)
@@ -151,7 +168,7 @@ public partial class MainWindow : System.Windows.Window
{
preparationState = "InProgress";
Transition("PackagePreparation", "PACKAGE_PREPARING", "Подготовка плагинов");
var profile = manualRosterPath is null ? localProfile : localProfile with { DownloadListPath = manualRosterPath };
var profile = localProfile with { DownloadListPath = selectedRosterSnapshotPath ?? localProfile.DownloadListPath };
var prepared = await localService.PrepareAsync(profile, CancellationToken.None);
preparationState = "Succeeded";
activationState = prepared.Activation.Outcome;
@@ -174,6 +191,7 @@ public partial class MainWindow : System.Windows.Window
Plugins.IsEnabled = false;
StartButton.IsEnabled = false;
pattern = expectedRoster = orderedIds = detail = "";
selectedRosterSnapshotPath = null;
mappingState = preparationState = activationState = bootstrapState = processState = "NotStarted";
processId = null;
packageLoadMarker = "NotStarted";