Skip to content

Commit

Permalink
feat: initial commit of list command, wip tests are missing
Browse files Browse the repository at this point in the history
  • Loading branch information
iadonkey committed Jul 31, 2024
1 parent 931365d commit 60f65f0
Show file tree
Hide file tree
Showing 11 changed files with 316 additions and 6 deletions.
64 changes: 64 additions & 0 deletions TwinpackCli/Commands/ListCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using CommandLine;
using NLog.Fluent;
using NuGet.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;
using Twinpack.Core;
using Twinpack.Models;
using Twinpack.Protocol;

namespace Twinpack.Commands
{
[Verb("list", HelpText = @"Searches all uses packages sources defined ./Zeugwerk/config.json or in the first solution found in the current directory")]
public class ListCommand : Command
{
[Value(0, MetaName = "search term", Required = false)]
public string? SearchTerm { get; set; }

[Option("take", Required = false, Default = null, HelpText = "Limit the number of results to return")]
public int? Take { get; set; }

[Option("plc", Required = false, Default = null, HelpText = "Filter for specific plcs, by default all plcs are considered")]
public IEnumerable<string> PlcFilter { get; set; }

[Option("outdated", Required = false, Default = null, HelpText = "Only show outdated packages")]
public bool Outdated { get; set; }

public override async Task<int> ExecuteAsync()
{
await PackagingServerRegistry.InitializeAsync();
_twinpack = new TwinpackService(PackagingServerRegistry.Servers);

await _twinpack.LoginAsync();

var rootPath = Environment.CurrentDirectory;
var config = ConfigFactory.Load(rootPath);

if (config == null)
{
config = await ConfigFactory.CreateFromSolutionFileAsync(
rootPath,
continueWithoutSolution: false,
packageServers: PackagingServerRegistry.Servers.Where(x => x.Connected),
plcTypeFilter: null);
}

foreach (var project in config.Projects)
{
project.Plcs = project.Plcs.Where(x => !PlcFilter.Any() || PlcFilter.Contains(x.Name)).ToList();
}

var packages = await _twinpack.RetrieveInstalledPackagesAsync(config, SearchTerm);
foreach (var package in packages.Where(x => !Outdated || x.IsUpdateable))
{
Console.WriteLine($"{package.Name} {package.InstalledVersion} {(package.IsUpdateable ? $"-> {package.UpdateVersion}" : "")}");
}

return 0;
}
}
}
3 changes: 2 additions & 1 deletion TwinpackCli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ static async Task<int> Main(string[] args)

try
{
return Parser.Default.ParseArguments<SearchCommand, PullCommand, PushCommand>(args)
return Parser.Default.ParseArguments<SearchCommand, ListCommand, PullCommand, PushCommand>(args)
.MapResult(
(SearchCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(),
(ListCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(),
(PullCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(),
(PushCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(),
errs => 1
Expand Down
1 change: 1 addition & 0 deletions TwinpackCli/TwinpackCli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Commands\Command.cs" />
<Compile Include="Commands\ListCommand.cs" />
<Compile Include="Commands\SearchCommand.cs" />
<Compile Include="Commands\PullCommand.cs" />
<Compile Include="Commands\PushCommand.cs" />
Expand Down
120 changes: 120 additions & 0 deletions TwinpackShared/Core/AutomationInterfaceUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using EnvDTE80;
using System;
using TCatSysManagerLib;

namespace Twinpack.Core
{
public class AutomationInterfaceService
{
DTE2 _dte;

public AutomationInterfaceService(DTE2 dte)
{
_dte = dte;
}

public string ResolveEffectiveVersion(string projectName, string placeholderName)
{
ResolvePlaceholder(LibraryManager(projectName), placeholderName, out _, out string effectiveVersion);

return effectiveVersion;
}

private static ITcPlcLibrary ResolvePlaceholder(ITcPlcLibraryManager libManager, string placeholderName, out string distributorName, out string effectiveVersion)
{
// Try to remove the already existing reference
foreach (ITcPlcLibRef item in libManager.References)
{
string itemPlaceholderName;
ITcPlcLibrary plcLibrary;

try
{
ITcPlcPlaceholderRef2 plcPlaceholder; // this will throw if the library is currently not installed
plcPlaceholder = (ITcPlcPlaceholderRef2)item;

itemPlaceholderName = plcPlaceholder.PlaceholderName;

if (plcPlaceholder.EffectiveResolution != null)
plcLibrary = plcPlaceholder.EffectiveResolution;
else
plcLibrary = plcPlaceholder.DefaultResolution;

effectiveVersion = plcLibrary.Version;
distributorName = plcLibrary.Distributor;
}
catch
{
plcLibrary = (ITcPlcLibrary)item;
effectiveVersion = null;
itemPlaceholderName = plcLibrary.Name.Split(',')[0];
distributorName = plcLibrary.Distributor;
}

if (string.Equals(itemPlaceholderName, placeholderName, StringComparison.InvariantCultureIgnoreCase))
return plcLibrary;
}

distributorName = null;
effectiveVersion = null;
return null;
}

private ITcSysManager SystemManager(string projectName = null)
{
var ready = false;
while (!ready)
{
ready = true;
foreach (EnvDTE.Project project in _dte.Solution.Projects)
{
if (project == null)
ready = false;
else if ((projectName == null || project?.Name == projectName) && project.Object as ITcSysManager != null)
return project.Object as ITcSysManager;
}

if (!ready)
System.Threading.Thread.Sleep(1000);
}

return null;
}

private ITcPlcLibraryManager LibraryManager(string projectName = null)
{
var systemManager = SystemManager(projectName);

if (projectName == null)
{
var plcConfiguration = systemManager.LookupTreeItem("TIPC");
for (var j = 1; j <= plcConfiguration.ChildCount; j++)
{
var plc = (plcConfiguration.Child[j] as ITcProjectRoot)?.NestedProject;
for (var k = 1; k <= (plc?.ChildCount ?? 0); k++)
{
if (plc.Child[k] as ITcPlcLibraryManager != null)
{
return plc.Child[k] as ITcPlcLibraryManager;
}
}
}
}
else
{
var projectRoot = systemManager.LookupTreeItem($"TIPC^{projectName}");
var plc = (projectRoot as ITcProjectRoot)?.NestedProject;
for (var k = 1; k <= (plc?.ChildCount ?? 0); k++)
{
if (plc.Child[k] as ITcPlcLibraryManager != null)
{
return plc.Child[k] as ITcPlcLibraryManager;
}
}

}

return null;
}
}
}
62 changes: 61 additions & 1 deletion TwinpackShared/Core/PackageServerCollection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NLog;
using EnvDTE;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -58,6 +59,65 @@ public async IAsyncEnumerable<CatalogItem> SearchAsync(string filter=null, int?
}
}

public async Task<CatalogItem> ResolvePackageAsync(ConfigProject project, ConfigPlcPackage item, AutomationInterfaceService automationInterface, CancellationToken token = default)
{
var catalogItem = new CatalogItem(item);

foreach (var packageServer in this)
{
catalogItem.PackageServer = packageServer;


// try to get the installed package, if we can't find it at least try to resolve it
var packageVersion = await packageServer.GetPackageVersionAsync(new PlcLibrary { DistributorName = item.DistributorName, Name = item.Name, Version = item.Version },
item.Branch, item.Configuration, item.Target,
cancellationToken: token);

if (packageVersion != null && item.Version == null)
{
if (automationInterface != null)
{
var effectiveVersion = automationInterface.ResolveEffectiveVersion(project.Name, packageVersion.Title);
packageVersion = await packageServer.GetPackageVersionAsync(new PlcLibrary { DistributorName = item.DistributorName, Name = item.Name, Version = effectiveVersion },
item.Branch, item.Configuration, item.Target,
cancellationToken: token);
}
else
{
_logger.Warn($"Cannot resolve wildcard reference '{packageVersion.Name}' without automation interface");
}
}

var packageVersionLatest = await packageServer.GetPackageVersionAsync(new PlcLibrary { DistributorName = item.DistributorName, Name = item.Name },
item.Branch, item.Configuration, item.Target,
cancellationToken: token);

// force the packageVersion references version even if the version was not found
if (packageVersion.Name != null)
{
catalogItem = new CatalogItem(packageServer, packageVersion);
catalogItem.Installed = packageVersion;
catalogItem.IsPlaceholder = item.Version == null;
}

// a package might be updateable but not available on Twinpack
if (packageVersionLatest.Name != null)
{
catalogItem.Update = packageVersionLatest;
catalogItem.PackageServer = packageServer;
}

catalogItem.Configuration = item;

if (packageVersion.Name != null)
return catalogItem;
}

catalogItem.Configuration = item;

return catalogItem;
}

public async Task PullAsync(Config config, bool skipInternalPackages = false, IEnumerable<ConfigPlcPackage> filter = null, string cachePath = null, CancellationToken cancellationToken = default)
{
_logger.Info($"Pulling from Package Server(s) (skip internal packages: {(skipInternalPackages ? "true" : "false")})");
Expand Down
38 changes: 37 additions & 1 deletion TwinpackShared/Core/TwinpackService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using EnvDTE;
using EnvDTE80;
using NLog;
using NLog.Filters;
using System;
Expand All @@ -21,12 +23,15 @@ public class TwinpackService : INotifyPropertyChanged
private PackageServerCollection _packageServers;
private IAsyncEnumerator<CatalogItem> _availablePackagesIt;
private string _searchTerm;
private Config _config;
private AutomationInterfaceService _automationInterfaceService;

public event PropertyChangedEventHandler PropertyChanged;

public TwinpackService(PackageServerCollection packageServers)
public TwinpackService(PackageServerCollection packageServers, AutomationInterfaceService automationInterfaceService=null)
{
_packageServers = packageServers;
_automationInterfaceService = automationInterfaceService;
}

public void InvalidateCache()
Expand Down Expand Up @@ -79,5 +84,36 @@ public async Task<IEnumerable<CatalogItem>> RetrieveAvailablePackagesAsync(strin
x.Name.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) >= 0)
;
}

public async Task<IEnumerable<CatalogItem>> RetrieveInstalledPackagesAsync(Config config, string searchTerm = null, CancellationToken token = default)
{
var installedPackages = new List<CatalogItem>();

foreach (var project in config.Projects)
{
var packages = project.Plcs.SelectMany(x => x.Packages).Where(x => searchTerm == null || x.Name?.IndexOf(searchTerm) > 0);

foreach (var package in packages)
{
CatalogItem catalogItem = await _packageServers.ResolvePackageAsync(project, package, _automationInterfaceService, token);

installedPackages.RemoveAll(x => !string.IsNullOrEmpty(x.Name) && x.Name == catalogItem.Name);
installedPackages.Add(catalogItem);

if (catalogItem.PackageServer == null)
_logger.Warn($"Package {package.Name} {package.Version} (distributor: {package.DistributorName}) referenced in the configuration can not be found on any package server");
else
_logger.Info($"Package {package.Name} {package.Version} (distributor: {package.DistributorName}) located on {catalogItem.PackageServer.UrlBase}");
}
}

return installedPackages
.Where(x =>
searchTerm == null ||
x.DisplayName?.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) >= 0 ||
x.DistributorName?.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) >= 0 ||
x.Name.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) >= 0)
;
}
}
}
6 changes: 5 additions & 1 deletion TwinpackShared/Models/Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public CatalogItem(ConfigPlcPackage package)
Repository = package.Version;
DistributorName = package.DistributorName;
DisplayName = Name;
Configuration = package;

IsPlaceholder = package.Version == null;
}
Expand All @@ -47,8 +48,11 @@ public CatalogItem(ConfigPlcPackage package)
public string InstalledBranch { get { return Installed?.Branch; } }
public string InstalledTarget { get { return Installed?.Target; } }
public string InstalledConfiguration { get { return Installed?.Configuration; } }
public PackageVersionGetResponse Update { get; set; }

PackageVersionGetResponse _update;
public PackageVersionGetResponse Update{ get; set; }
public PackageVersionGetResponse Installed { get; set; }
public ConfigPlcPackage Configuration { get; set; }

public bool IsUpdateable
{
Expand Down
5 changes: 3 additions & 2 deletions TwinpackShared/Models/ConfigFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ public static async Task<Config> CreateFromSolutionAsync(EnvDTE.Solution solutio
return config;
}

public static async Task<Config> CreateFromSolutionFileAsync(string path = ".", bool continueWithoutSolution = false, Protocol.IPackageServer packageServer = null, IEnumerable<ConfigPlcProject.PlcProjectType> plcTypeFilter=null, CancellationToken cancellationToken = default)
public static async Task<Config> CreateFromSolutionFileAsync(string path=".", bool continueWithoutSolution=false, IEnumerable<Protocol.IPackageServer> packageServers=null, IEnumerable<ConfigPlcProject.PlcProjectType> plcTypeFilter=null, CancellationToken cancellationToken = default)
{
packageServers = packageServers == null ? new List<Protocol.IPackageServer>() : packageServers;

Config config = new Config();
var solutions = Directory.GetFiles(path, "*.sln", SearchOption.AllDirectories);
Expand Down Expand Up @@ -190,7 +191,7 @@ public static async Task<Config> CreateFromSolutionFileAsync(string path = ".",

foreach (var plc in project.Plcs)
{
var plcConfig = await ConfigPlcProjectFactory.CreateAsync(plc.FilePath, packageServer, cancellationToken);
var plcConfig = await ConfigPlcProjectFactory.CreateAsync(plc.FilePath, packageServers, cancellationToken);

if(plcTypeFilter == null || plcTypeFilter.Contains(plcConfig.PlcType))
projectConfig.Plcs.Add(plcConfig);
Expand Down
1 change: 1 addition & 0 deletions TwinpackShared/TwinpackShared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<Import_RootNamespace>TwinpackShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)core\AutomationInterfaceUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)models\PlcVersion.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\SourceRepositories.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\PackagingServer.cs" />
Expand Down
1 change: 1 addition & 0 deletions TwinpackTests/ConfigFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Threading.Tasks;
using Twinpack.Models;
using Twinpack.Protocol;

namespace TwinpackTests
{
Expand Down
Loading

0 comments on commit 60f65f0

Please sign in to comment.