diff --git a/build/props/Base.props b/build/props/Base.props index 9300e775..c34922ca 100644 --- a/build/props/Base.props +++ b/build/props/Base.props @@ -2,9 +2,9 @@ 0.20.6.0 - 0.30.5.0 - 0.40.0.0 - 0.10.1 + 0.31.0.0 + 0.41.0.0 + 0.11.0.0 $(CoreVersion) Hendrik Mennen One Ware diff --git a/src/OneWare.Core/ModuleLogic/PackageDependency.cs b/src/OneWare.Core/ModuleLogic/PackageDependency.cs deleted file mode 100644 index 705941cc..00000000 --- a/src/OneWare.Core/ModuleLogic/PackageDependency.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace OneWare.Core.ModuleLogic; - -public class PackageDependency -{ - public string? Name { get; init; } - - public string? MinVersion { get; init; } - - public string? MaxVersion { get; init; } -} \ No newline at end of file diff --git a/src/OneWare.Core/ModuleLogic/PackageManifest.cs b/src/OneWare.Core/ModuleLogic/PackageManifest.cs deleted file mode 100644 index a34466ea..00000000 --- a/src/OneWare.Core/ModuleLogic/PackageManifest.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OneWare.Core.ModuleLogic; - -public class PackageManifest -{ - public PackageDependency[]? Dependencies { get; init; } -} \ No newline at end of file diff --git a/src/OneWare.Core/Services/PluginService.cs b/src/OneWare.Core/Services/PluginService.cs index 33f9036d..c7b02af0 100644 --- a/src/OneWare.Core/Services/PluginService.cs +++ b/src/OneWare.Core/Services/PluginService.cs @@ -1,8 +1,10 @@ using System.Text.Json; +using System.Text.Json.Nodes; using OneWare.Core.Models; using OneWare.Core.ModuleLogic; using OneWare.Essentials.Helpers; using OneWare.Essentials.Models; +using OneWare.Essentials.PackageManager.Compatibility; using OneWare.Essentials.Services; using Prism.Ioc; using Prism.Modularity; @@ -36,10 +38,11 @@ public IPlugin AddPlugin(string path) { var plugin = new Plugin(Path.GetFileName(path), path); InstalledPlugins.Add(plugin); - if (CheckCompatibility(path) is { compatible: false } test) + + if (PluginCompatibilityChecker.CheckCompatibilityPath(path) is { IsCompatible: false } test) { - plugin.CompatibilityReport = test.report; - ContainerLocator.Container.Resolve().Error($"Plugin {path} failed loading: \n {test.report}"); + plugin.CompatibilityReport = test.Report; + ContainerLocator.Container.Resolve().Error($"Plugin {path} failed loading: \n {test.Report}"); return plugin; } @@ -86,62 +89,4 @@ public void RemovePlugin(IPlugin plugin) ContainerLocator.Container.Resolve().Error(e.Message, e); } } - - private (bool compatible, string? report) CheckCompatibility(string path) - { - try - { - var pluginName = Path.GetFileName(path); - - var compatibilityIssues = string.Empty; - - //Dependency check - var depFilePath = Path.Combine(path, "oneware.json"); - - if (!File.Exists(depFilePath)) - { - compatibilityIssues += - $"Extension {pluginName} incompatible:\n\noneware.json not found in plugin folder\n"; - return (false, compatibilityIssues); - } - - var packageManifest = - JsonSerializer.Deserialize(File.ReadAllText(depFilePath), _jsonSerializerOptions); - - if (packageManifest?.Dependencies is { } deps) - foreach (var dep in deps) - { - var minVersion = Version.Parse(dep.MinVersion ?? "0"); - var maxVersion = Version.Parse(dep.MaxVersion ?? "10000"); - - var coreDep = AppDomain.CurrentDomain.GetAssemblies() - .SingleOrDefault(x => x.GetName().Name == dep.Name)?.GetName(); - - if (coreDep == null) - { - compatibilityIssues += $"Dependency {dep.Name} not found\n"; - continue; - } - - if (coreDep.Version < minVersion) - compatibilityIssues += - $"Studio {dep.Name} v{coreDep.Version} is older than min Plugin v{minVersion}\n"; - if (coreDep.Version > maxVersion) - compatibilityIssues += - $"Studio {dep.Name} v{coreDep.Version} is newer than max Plugin v{maxVersion}\n"; - } - - if (compatibilityIssues.Length > 0) - { - compatibilityIssues = $"Extension {pluginName} incompatible:\n" + compatibilityIssues; - return (false, compatibilityIssues); - } - } - catch (Exception e) - { - return (false, e.Message); - } - - return (true, null); - } } \ No newline at end of file diff --git a/src/OneWare.Essentials/Models/PackageModel.cs b/src/OneWare.Essentials/Models/PackageModel.cs index 9dee0334..277f3b0c 100644 --- a/src/OneWare.Essentials/Models/PackageModel.cs +++ b/src/OneWare.Essentials/Models/PackageModel.cs @@ -2,6 +2,7 @@ using OneWare.Essentials.Enums; using OneWare.Essentials.Helpers; using OneWare.Essentials.PackageManager; +using OneWare.Essentials.PackageManager.Compatibility; using OneWare.Essentials.Services; namespace OneWare.Essentials.Models; @@ -9,7 +10,6 @@ namespace OneWare.Essentials.Models; public abstract class PackageModel : ObservableObject { private readonly IApplicationStateService _applicationStateService; - private readonly IHttpService _httpService; private readonly ILogger _logger; private PackageVersion? _installedVersion; private Package _package; @@ -19,7 +19,9 @@ public abstract class PackageModel : ObservableObject private PackageStatus _status; - private string? _warningText; + private string? _installedVersionWarningText; + + protected readonly IHttpService HttpService; protected PackageModel(Package package, string packageType, @@ -29,7 +31,7 @@ protected PackageModel(Package package, IApplicationStateService applicationStateService) { _package = package; - _httpService = httpService; + HttpService = httpService; _logger = logger; _applicationStateService = applicationStateService; ExtractionFolder = extractionFolder; @@ -48,10 +50,10 @@ public PackageVersion? InstalledVersion } } - public string? WarningText + public string? InstalledVersionWarningText { - get => _warningText; - set => SetProperty(ref _warningText, value); + get => _installedVersionWarningText; + set => SetProperty(ref _installedVersionWarningText, value); } public PackageStatus Status @@ -104,7 +106,7 @@ public async Task UpdateAsync(PackageVersion version) var target = version.Targets?.FirstOrDefault(x => x.Target?.Replace("-", "") == currentTarget) ?? version.Targets?.FirstOrDefault(x => x.Target == "all"); return target; } - + public Task DownloadAsync(PackageVersion version) { var task = PerformDownloadAsync(version); @@ -121,9 +123,11 @@ private async Task PerformDownloadAsync(PackageVersion version) Status = PackageStatus.Installing; var target = SelectTarget(version); - - if (target is { Url: not null }) + + if (target is not null) { + var zipUrl = target.Url ?? $"{Package.SourceUrl}/{version.Version}/{Package.Id}_{version.Version}_{target.Target}.zip"; + var state = _applicationStateService.AddState($"Downloading {Package.Id}...", AppState.Loading); var progress = new Progress(x => @@ -138,7 +142,7 @@ private async Task PerformDownloadAsync(PackageVersion version) }); //Download - var result = await _httpService.DownloadAndExtractArchiveAsync(target.Url, ExtractionFolder, progress); + var result = await HttpService.DownloadAndExtractArchiveAsync(zipUrl, ExtractionFolder, progress); _applicationStateService.RemoveState(state); @@ -242,4 +246,9 @@ private void UpdateStatus() else Status = PackageStatus.Unavailable; } + + public virtual Task CheckCompatibilityAsync(PackageVersion version) + { + return Task.FromResult(new CompatibilityReport(true, null)); + } } \ No newline at end of file diff --git a/src/OneWare.Essentials/PackageManager/Compatibility/CompatibilityReport.cs b/src/OneWare.Essentials/PackageManager/Compatibility/CompatibilityReport.cs new file mode 100644 index 00000000..48f9c4d2 --- /dev/null +++ b/src/OneWare.Essentials/PackageManager/Compatibility/CompatibilityReport.cs @@ -0,0 +1,8 @@ +namespace OneWare.Essentials.PackageManager.Compatibility; + +public class CompatibilityReport(bool isCompatible, string? report = null) +{ + public bool IsCompatible { get; } = isCompatible; + + public string? Report { get; } = report; +} \ No newline at end of file diff --git a/src/OneWare.Essentials/PackageManager/Compatibility/PluginCompatibilityChecker.cs b/src/OneWare.Essentials/PackageManager/Compatibility/PluginCompatibilityChecker.cs new file mode 100644 index 00000000..344e15f5 --- /dev/null +++ b/src/OneWare.Essentials/PackageManager/Compatibility/PluginCompatibilityChecker.cs @@ -0,0 +1,82 @@ +namespace OneWare.Essentials.PackageManager.Compatibility; + +public class PluginCompatibilityChecker +{ + public static CompatibilityReport CheckCompatibilityPath(string path) + { + try + { + var pluginName = Path.GetFileName(path); + + var depFilePath = Path.Combine(path, "minimal-dependencies.txt"); + + var compatibilityIssues = ""; + + if (!File.Exists(depFilePath)) + { + compatibilityIssues += + $"Extension {pluginName} incompatible:\n\nminimal-dependencies.txt not found in plugin folder\n"; + return new CompatibilityReport(false, compatibilityIssues); + } + + return CheckCompatibility(File.ReadAllText(depFilePath)); + } + catch (Exception e) + { + return new CompatibilityReport(false, e.Message); + } + } + + public static CompatibilityReport CheckCompatibility(string? deps) + { + try + { + var compatibilityIssues = ""; + + if (deps == null) return new CompatibilityReport(false, "Error checking compatibility"); + + var depsList = deps.Trim().Split('\n'); + + foreach (var dep in depsList) + { + var parts = dep.Split(':'); + var dependencyName = parts[0].Trim(); + var versionString = parts[1].Trim(); + var dependencyVersion = Version.Parse(NormalizeVersion(versionString)); + + var coreDep = AppDomain.CurrentDomain.GetAssemblies() + .SingleOrDefault(x => x.GetName().Name == dependencyName)?.GetName(); + + if (coreDep == null) + { + compatibilityIssues += $"Dependency {dependencyName} not found\n"; + continue; + } + + if (coreDep.Version < dependencyVersion) + compatibilityIssues += + $"Studio {dependencyName} v{coreDep.Version} is older than Plugin v{dependencyVersion}\n"; + if (coreDep.Version > dependencyVersion) + compatibilityIssues += + $"Studio {dependencyName} v{coreDep.Version} is newer than Plugin v{dependencyVersion}\n"; + } + + return new CompatibilityReport(compatibilityIssues.Length == 0, compatibilityIssues); + } + catch (Exception e) + { + return new CompatibilityReport(false, e.Message); + } + } + + static string NormalizeVersion(string version) + { + string[] parts = version.Split('.'); + while (parts.Length < 4) + { + Array.Resize(ref parts, parts.Length + 1); + parts[^1] = "0"; + } + return string.Join('.', parts); + } +} \ No newline at end of file diff --git a/src/OneWare.Essentials/PackageManager/Package.cs b/src/OneWare.Essentials/PackageManager/Package.cs index be774540..ebe3d590 100644 --- a/src/OneWare.Essentials/PackageManager/Package.cs +++ b/src/OneWare.Essentials/PackageManager/Package.cs @@ -15,6 +15,8 @@ public class Package public string? License { get; init; } public string? IconUrl { get; init; } + + public string? SourceUrl { get; init; } public PackageTab[]? Tabs { get; init; } diff --git a/src/OneWare.Essentials/PackageManager/PackageTarget.cs b/src/OneWare.Essentials/PackageManager/PackageTarget.cs index 2c9cbfe7..71d973b0 100644 --- a/src/OneWare.Essentials/PackageManager/PackageTarget.cs +++ b/src/OneWare.Essentials/PackageManager/PackageTarget.cs @@ -5,6 +5,6 @@ public class PackageTarget public string? Target { get; init; } public string? Url { get; init; } - + public PackageAutoSetting[]? AutoSetting { get; init; } } \ No newline at end of file diff --git a/src/OneWare.Essentials/PackageManager/PackageVersion.cs b/src/OneWare.Essentials/PackageManager/PackageVersion.cs index f8badb91..ddb1b032 100644 --- a/src/OneWare.Essentials/PackageManager/PackageVersion.cs +++ b/src/OneWare.Essentials/PackageManager/PackageVersion.cs @@ -6,5 +6,5 @@ public class PackageVersion public PackageTarget[]? Targets { get; init; } - public bool TargetAll => Targets is [{ Target: "all" }]; + public string? DepsUrl { get; init; } } \ No newline at end of file diff --git a/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs b/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs index 078ebc09..91265480 100644 --- a/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs +++ b/src/OneWare.OssCadSuiteIntegration/OssCadSuiteIntegrationModule.cs @@ -70,8 +70,7 @@ public class OssCadSuiteIntegrationModule : IModule new PackageTarget { Target = "win-x64", - Url = - "https://github.com/HendrikMennen/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-windows-x64-20240727.tgz", + Url = "https://github.com/HendrikMennen/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-windows-x64-20240727.tgz", AutoSetting = [ new PackageAutoSetting @@ -84,8 +83,7 @@ public class OssCadSuiteIntegrationModule : IModule new PackageTarget { Target = "linux-x64", - Url = - "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-linux-x64-20240727.tgz", + Url = "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-linux-x64-20240727.tgz", AutoSetting = [ new PackageAutoSetting @@ -98,8 +96,7 @@ public class OssCadSuiteIntegrationModule : IModule new PackageTarget { Target = "osx-x64", - Url = - "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-darwin-x64-20240727.tgz", + Url = "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-darwin-x64-20240727.tgz", AutoSetting = [ new PackageAutoSetting @@ -112,8 +109,7 @@ public class OssCadSuiteIntegrationModule : IModule new PackageTarget { Target = "osx-arm64", - Url = - "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-darwin-arm64-20240727.tgz", + Url = "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-27/oss-cad-suite-darwin-arm64-20240727.tgz", AutoSetting = [ new PackageAutoSetting diff --git a/src/OneWare.PackageManager/Models/PackageVersionModel.cs b/src/OneWare.PackageManager/Models/PackageVersionModel.cs new file mode 100644 index 00000000..13172a9c --- /dev/null +++ b/src/OneWare.PackageManager/Models/PackageVersionModel.cs @@ -0,0 +1,20 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using OneWare.Essentials.PackageManager; +using OneWare.Essentials.PackageManager.Compatibility; + +namespace OneWare.PackageManager.Models; + +public class PackageVersionModel(PackageVersion version) : ObservableObject +{ + private CompatibilityReport? _compatibilityReport; + + public PackageVersion Version { get; } = version; + + public bool TargetAll => Version.Targets is [{ Target: "all" }]; + + public CompatibilityReport? CompatibilityReport + { + get => _compatibilityReport; + set => SetProperty(ref _compatibilityReport, value); + } +} \ No newline at end of file diff --git a/src/OneWare.PackageManager/Models/PluginPackageModel.cs b/src/OneWare.PackageManager/Models/PluginPackageModel.cs index 2b463af1..bd1aa4ab 100644 --- a/src/OneWare.PackageManager/Models/PluginPackageModel.cs +++ b/src/OneWare.PackageManager/Models/PluginPackageModel.cs @@ -1,6 +1,7 @@ using OneWare.Essentials.Enums; using OneWare.Essentials.Models; using OneWare.Essentials.PackageManager; +using OneWare.Essentials.PackageManager.Compatibility; using OneWare.Essentials.Services; namespace OneWare.PackageManager.Models; @@ -31,11 +32,11 @@ public IPlugin? Plugin SetProperty(ref _plugin, value); if (_plugin is not null) { - if (!_plugin.IsCompatible) WarningText = _plugin.CompatibilityReport; + if (!_plugin.IsCompatible) InstalledVersionWarningText = _plugin.CompatibilityReport; } else { - WarningText = null; + InstalledVersionWarningText = null; } } } @@ -77,4 +78,17 @@ protected override void Uninstall() Status = PackageStatus.Available; } } + + public override async Task CheckCompatibilityAsync(PackageVersion version) + { + if (version.DepsUrl != null || Package.SourceUrl != null) + { + var depsUrl = version.DepsUrl ?? $"{Package.SourceUrl}/{version.Version}/minimal-dependencies.txt"; + + var deps = await HttpService.DownloadTextAsync(depsUrl); + + return PluginCompatibilityChecker.CheckCompatibility(deps); + } + return PluginCompatibilityChecker.CheckCompatibility(null); + } } \ No newline at end of file diff --git a/src/OneWare.PackageManager/OneWare.PackageManager.csproj b/src/OneWare.PackageManager/OneWare.PackageManager.csproj index d0bbc47d..fbf160eb 100644 --- a/src/OneWare.PackageManager/OneWare.PackageManager.csproj +++ b/src/OneWare.PackageManager/OneWare.PackageManager.csproj @@ -7,4 +7,8 @@ + + + + \ No newline at end of file diff --git a/src/OneWare.PackageManager/ViewModels/PackageViewModel.cs b/src/OneWare.PackageManager/ViewModels/PackageViewModel.cs index 64956560..57e1efbb 100644 --- a/src/OneWare.PackageManager/ViewModels/PackageViewModel.cs +++ b/src/OneWare.PackageManager/ViewModels/PackageViewModel.cs @@ -34,7 +34,7 @@ public class PackageViewModel : ObservableObject private bool _resolveTabsStarted; - private PackageVersion? _selectedVersion; + private PackageVersionModel? _selectedVersionModel; protected PackageViewModel(PackageModel packageModel, IHttpService httpService) { @@ -44,16 +44,16 @@ protected PackageViewModel(PackageModel packageModel, IHttpService httpService) RemoveCommand = new AsyncRelayCommand(PackageModel.RemoveAsync, () => PackageModel.Status is PackageStatus.Installed or PackageStatus.UpdateAvailable); - InstallCommand = new AsyncRelayCommand(() => PackageModel.DownloadAsync(SelectedVersion!), + InstallCommand = new AsyncRelayCommand(() => PackageModel.DownloadAsync(SelectedVersionModel!.Version), () => PackageModel.Status is PackageStatus.Available); - UpdateCommand = new AsyncRelayCommand(() => PackageModel.UpdateAsync(SelectedVersion!), + UpdateCommand = new AsyncRelayCommand(() => PackageModel.UpdateAsync(SelectedVersionModel!.Version), () => PackageModel.Status is PackageStatus.UpdateAvailable); PackageModel.WhenValueChanged(x => x.Status).Subscribe(_ => UpdateStatus()); InitPackage(); } - + public bool IsTabsResolved { get => _isTabsResolved; @@ -76,16 +76,18 @@ public IImage? Image private set => SetProperty(ref _image, value); } + public ObservableCollection PackageVersionModels { get; } = new(); public ObservableCollection Tabs { get; } = []; public ObservableCollection Links { get; } = []; - public PackageVersion? SelectedVersion + public PackageVersionModel? SelectedVersionModel { - get => _selectedVersion; + get => _selectedVersionModel; set { - SetProperty(ref _selectedVersion, value); + SetProperty(ref _selectedVersionModel, value); UpdateStatus(); + _ = CheckSelectedVersionCompatibilityAsync(); } } @@ -112,13 +114,17 @@ public AsyncRelayCommand? MainButtonCommand public AsyncRelayCommand InstallCommand { get; } public AsyncRelayCommand UpdateCommand { get; } - + private void InitPackage() { Links.Clear(); if (PackageModel.Package.Links != null) Links.AddRange(PackageModel.Package.Links.Select(x => new LinkModel(x.Name ?? "Link", x.Url ?? ""))); - SelectedVersion = PackageModel.Package.Versions?.LastOrDefault(); + + PackageVersionModels.Clear(); + if(PackageModel.Package.Versions != null) + PackageVersionModels.AddRange(PackageModel.Package.Versions.Select(x => new PackageVersionModel(x))); + SelectedVersionModel = PackageVersionModels.LastOrDefault(); _resolveTabsStarted = false; _resolveImageStarted = false; UpdateStatus(); @@ -127,7 +133,7 @@ private void InitPackage() private void UpdateStatus() { - Version.TryParse(SelectedVersion?.Version ?? "", out var sV); + Version.TryParse(SelectedVersionModel?.Version.Version ?? "", out var sV); Version.TryParse(PackageModel.InstalledVersion?.Version ?? "", out var iV); MainButtonCommand = null; @@ -211,4 +217,12 @@ public async Task ResolveTabsAsync() IsTabsResolved = true; } + + public async Task CheckSelectedVersionCompatibilityAsync() + { + if(SelectedVersionModel == null) return; + + if(SelectedVersionModel.CompatibilityReport == null) + SelectedVersionModel.CompatibilityReport = await PackageModel.CheckCompatibilityAsync(SelectedVersionModel.Version); + } } \ No newline at end of file diff --git a/src/OneWare.PackageManager/Views/PackageManagerView.axaml b/src/OneWare.PackageManager/Views/PackageManagerView.axaml index 4914679d..e6e2dce0 100644 --- a/src/OneWare.PackageManager/Views/PackageManagerView.axaml +++ b/src/OneWare.PackageManager/Views/PackageManagerView.axaml @@ -127,8 +127,8 @@ + IsVisible="{Binding PackageModel.InstalledVersionWarningText, Converter={x:Static ObjectConverters.IsNotNull}}" + ToolTip.Tip="{Binding PackageModel.InstalledVersionWarningText}" /> diff --git a/src/OneWare.PackageManager/Views/PackageView.axaml b/src/OneWare.PackageManager/Views/PackageView.axaml index 841cd8a0..b9404e95 100644 --- a/src/OneWare.PackageManager/Views/PackageView.axaml +++ b/src/OneWare.PackageManager/Views/PackageView.axaml @@ -33,12 +33,12 @@ - - - + + @@ -60,15 +60,17 @@ + IsVisible="{Binding PackageModel.InstalledVersionWarningText, Converter={x:Static ObjectConverters.IsNotNull}}"> - + ToolTip.Tip="{Binding PackageModel.InstalledVersionWarningText}" /> + - + + @@ -81,7 +83,42 @@ - + + + + + + + + + + + + + + + + + + + + + + + ().RegisterPackageRepository($"https://raw.githubusercontent.com/one-ware/OneWare.PublicPackages/{Global.VersionCode}/oneware-packages.json"); + Container.Resolve().RegisterPackageRepository($"https://raw.githubusercontent.com/one-ware/OneWare.PublicPackages/main/oneware-packages.json"); var arguments = Environment.GetCommandLineArgs(); @@ -179,6 +179,7 @@ await Task.Factory.StartNew(() => var packageService = Container.Resolve(); packageService.LoadPackagesAsync().GetAwaiter().GetResult(); + var updatePackages = packageService.Packages .Where(x => x.Value.Status == PackageStatus.UpdateAvailable) .Select(x => x.Value)