Skip to content

Commit

Permalink
Support multiple .NET frameworks
Browse files Browse the repository at this point in the history
  • Loading branch information
asperpint committed Mar 13, 2024
1 parent ba8d419 commit 0cdf612
Show file tree
Hide file tree
Showing 35 changed files with 401 additions and 666 deletions.
4 changes: 2 additions & 2 deletions .github/package_version.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PI_MAJOR_VERSION=5
PI_MINOR_VERSION=6
PI_PATCH_VERSION=4
PI_MINOR_VERSION=8
PI_PATCH_VERSION=3
PI_DEPENDENCIES_SUFFIX=-*
18 changes: 12 additions & 6 deletions .github/workflows/build-test-cross.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ jobs:
PI_GITHUB_USERNAME_ENV: ${{ secrets.PI_GITHUB_USERNAME }}
PI_GITHUB_PAT_ENV: ${{ secrets.PI_GITHUB_PAT }}
PI_CI_REFERENCE: cross
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_PKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_PKEY_DEMO_LIC }}
PI_CLI_TEST_SKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_SKEY_DEMO_LIC }}

steps:

Expand Down Expand Up @@ -68,8 +70,10 @@ jobs:
PI_GITHUB_USERNAME_ENV: ${{ secrets.PI_GITHUB_USERNAME }}
PI_GITHUB_PAT_ENV: ${{ secrets.PI_GITHUB_PAT }}
PI_CI_REFERENCE: cross
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_PKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_PKEY_DEMO_LIC }}
PI_CLI_TEST_SKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_SKEY_DEMO_LIC }}

steps:

Expand Down Expand Up @@ -115,8 +119,10 @@ jobs:
PI_GITHUB_USERNAME_ENV: ${{ secrets.PI_GITHUB_USERNAME }}
PI_GITHUB_PAT_ENV: ${{ secrets.PI_GITHUB_PAT }}
PI_CI_REFERENCE: cross
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_PKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_PKEY_DEMO_LIC }}
PI_CLI_TEST_SKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_SKEY_DEMO_LIC }}

steps:

Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/build-test-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ jobs:
PI_NUGET_PAT_ENV: ${{ secrets.PI_NUGET_API_KEY }}
PI_PUBLISH_REGISTRY: github
PI_CI_REFERENCE: package
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_GITHUB_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_ONLINE_LIC: ${{ secrets.PI_CLI_TEST_ONLINE_LIC }}
PI_CLI_TEST_OFFLINE_LIC: ${{ secrets.PI_CLI_TEST_OFFLINE_LIC }}
PI_CLI_TEST_PKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_PKEY_DEMO_LIC }}
PI_CLI_TEST_SKEY_DEMO_LIC: ${{ secrets.PI_CLI_TEST_SKEY_DEMO_LIC }}

steps:

Expand Down
5 changes: 5 additions & 0 deletions CHANGE_LOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 5.8.3-rc*
> Breaking Change
- Support multi-targets net481, net8.0, netstandard2.0 and netstandard2.1
- Simplify licensing based on tenant

# 5.6.4-rc*
> Breaking Change
- Use `OneImlx.Shared` and `OneImlx.Test` pacakges.
Expand Down
1 change: 1 addition & 0 deletions OneImlx.Terminal.Solution.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{1A8779C7-3B84-405C-86D7-F000E5EA9AD8}"
ProjectSection(SolutionItems) = preProject
CHANGE_LOG.md = CHANGE_LOG.md
global.json = global.json
build\props\Package.props = build\props\Package.props
.github\package_version.env = .github\package_version.env
build\props\Test.props = build\props\Test.props
Expand Down
17 changes: 9 additions & 8 deletions build/props/Test.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,25 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
<RootNamespace>$(MSBuildProjectName.Replace(".Tests", ""))</RootNamespace>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.2.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
<PackageReference Include="coverlet.collector" Version="6.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.6.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand All @@ -49,7 +50,7 @@
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="OneImlx.Test" Version="5.6.4" />
<PackageReference Include="OneImlx.Test" Version="5.8.3" />
</ItemGroup>
</Otherwise>
</Choose>
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "7.0.302",
"version": "8.0.101",
"allowPrerelease": false,
"rollForward": "latestMajor"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
<Import Project="..\..\build\props\PackageLicense.props" />

<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
<Description>
The flexible cross-platform framework for modern CLI terminals. Use microservices architecture principles, dependency injection, and options pattern to build modern and secured CLI applications similar to Github CLI, .NET CLI, Stripe CLI, or CLI terminals with custom formats. Take your app or service to the command line with Unicode support and develop CLI terminals in any language. Configure your server, provide your self-hosting implementations for stores, and host in an environment of your choice, e.g., Windows, Linux, macOS, Docker, Kubernetes, etc.
</Description>
<PackageTags>terminal cli options flags oneimlx unicode pi-cli msal identity oauth openid-connect oidc</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Graph.Core" Version="3.1.3" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.58.0" />
<PackageReference Include="Microsoft.Graph.Core" Version="3.1.8" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.59.0" />
</ItemGroup>

<!--
Expand Down
14 changes: 2 additions & 12 deletions src/OneImlx.Terminal/Commands/Runners/LicenseInfoRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

using OneImlx.Shared.Extensions;
using OneImlx.Shared.Licensing;
using OneImlx.Terminal.Licensing;
using OneImlx.Terminal.Runtime;
using System;
Expand Down Expand Up @@ -43,24 +42,15 @@ public override async Task<CommandRunnerResult> RunCommandAsync(CommandRunnerCon
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "plan={0}", license.Plan);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "usage={0}", license.Usage);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "handler={0}", license.Handler);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "provider_id={0}", license.ProviderId);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "key_source={0}", license.LicenseKeySource);

if (license.LicenseKeySource == LicenseSources.JsonFile)
{
// Only display key file path
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "key_file={0}", license.LicenseKey);
}
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "key_file={0}", license.LicenseKey);
}

{
// Print Claims
await terminalConsole.WriteLineColorAsync(ConsoleColor.Yellow, "Claims");
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "name={0}", license.Claims.Name);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "name={0}", license.Claims.TenantName);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "tenant_id={0}", license.Claims.TenantId);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "tenant_country={0}", license.Claims.TenantCountry);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "object_id={0}", license.Claims.ObjectId ?? "-");
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "object_country={0}", license.Claims.ObjectCountry ?? "-");
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "authorized_party={0}", license.Claims.AuthorizedParty);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "audience={0}", license.Claims.Audience);
await terminalConsole.WriteLineColorAsync(ConsoleColor.Cyan, "issuer={0}", license.Claims.Issuer);
Expand Down
15 changes: 3 additions & 12 deletions src/OneImlx.Terminal/Configuration/Options/LicensingOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,22 @@ public sealed class LicensingOptions
/// <summary>
/// The authorized application id. This is also the <c>auth_apps</c> claim from your license key.
/// </summary>
public string? AuthorizedApplicationId { get; set; }
public string? Application { get; set; }

/// <summary>
/// The license consumer tenant id.
/// </summary>
public string? ConsumerTenantId { get; set; }

/// <summary>
/// The license key source. Defaults to <see cref="LicenseSources.JsonFile"/>.
/// The license key file containing license key.
/// </summary>
public string LicenseKeySource { get; set; } = LicenseSources.JsonFile;

/// <summary>
/// The license key or the file containing license key.
/// </summary>
/// <remarks>
/// If <see cref="LicenseKeySource"/> is set to <see cref="LicenseSources.JsonFile"/>, then this option value must be a
/// valid JSON file path containing license key.
/// </remarks>
public string? LicenseKey { get; set; }

/// <summary>
/// The subject or a licensing context to check the license. Your subscription id establishes your licensing context.
/// </summary>
public string? Subject { get; set; }
public string? Id { get; set; }

/// <summary>
/// The license plan. Defaults to <see cref="TerminalLicensePlans.Demo"/>.
Expand Down
19 changes: 7 additions & 12 deletions src/OneImlx.Terminal/Hosting/TerminalHostedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,18 @@ internal virtual Task PrintHostApplicationMandatoryLicensingAsync(License licens
{
if (license.Plan == TerminalLicensePlans.Demo)
{
if (license.Usage == LicenseUsages.Educational)
if (license.Usage == LicenseUsage.Educational)
{
logger.LogWarning("Your demo license is free for educational purposes. For non-educational, release, or production environment, you require a commercial license.");
}
else if (license.Usage == LicenseUsages.RnD)
else if (license.Usage == LicenseUsage.RnD)
{
logger.LogWarning("Your demo license is free for RnD, test, and evaluation purposes. For release, or production environment, you require a commercial license.");
}
}
else if (license.Plan == TerminalLicensePlans.Custom)
{
if (license.Usage == LicenseUsages.RnD)
if (license.Usage == LicenseUsage.RnD)
{
logger.LogWarning("Your custom license is free for RnD, test and evaluation purposes. For release, or production environment, you require a commercial license.");
}
Expand Down Expand Up @@ -207,18 +207,13 @@ protected virtual Task PrintHostApplicationHeaderAsync()
protected virtual Task PrintHostApplicationLicensingAsync(License license)
{
// Print the license information
logger.LogInformation("consumer={0} ({1})", license.Claims.Name, license.Claims.TenantId);
logger.LogInformation("tenant={0} ({1})", license.Claims.TenantName, license.Claims.TenantId);
logger.LogInformation("country={0}", license.Claims.TenantCountry);
logger.LogInformation("subject={0}", options.Licensing.Subject);
logger.LogInformation("license_handler={0}", license.Handler);
logger.LogInformation("license={0}", options.Licensing.Id);
logger.LogInformation("mode={0}", license.Handler);
logger.LogInformation("usage={0}", license.Usage);
logger.LogInformation("plan={0}", license.Plan);
logger.LogInformation("key_source={0}", options.Licensing.LicenseKeySource);
if (license.LicenseKeySource == LicenseSources.JsonFile)
{
// Don't dump the key, just the lic file path
logger.LogInformation("key_file={0}", license.LicenseKey);
}
logger.LogInformation("key={0}", license.LicenseKey);
logger.LogInformation($"expiry={0}", license.Claims.Expiry);

return Task.CompletedTask;
Expand Down
2 changes: 1 addition & 1 deletion src/OneImlx.Terminal/Integration/ITerminalCommandSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Integration
/// An abstraction of a terminal command source.
/// </summary>
/// <remarks>
///
/// A command source is a provider of terminal commands. It can be a local or remote source.
/// </remarks>
public interface ITerminalCommandSource<TContext> where TContext : class
{
Expand Down
71 changes: 34 additions & 37 deletions src/OneImlx.Terminal/Integration/PublishedAssemblyLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,55 +54,52 @@ public PublishedAssemblyLoader(ILogger<PublishedAssemblyLoader> logger)
/// primary command source assemblies.</returns>
public Task<IEnumerable<Assembly>> LoadAssembliesAsync(PublishedCommandSourceContext context)
{
return Task.Run(() =>
{
List<Assembly> assemblies = new();
List<Assembly> assemblies = [];

foreach (var kvp in context.PublishedAssemblies)
foreach (var kvp in context.PublishedAssemblies)
{
string assemblyPath = Path.Combine(kvp.Value, kvp.Key);
if (!File.Exists(assemblyPath))
{
string assemblyPath = Path.Combine(kvp.Value, kvp.Key);
if (!File.Exists(assemblyPath))
{
throw new TerminalException(TerminalErrors.ServerError, "The published command source assembly does not exist. path={0}", assemblyPath);
}
throw new TerminalException(TerminalErrors.ServerError, "The published command source assembly does not exist. path={0}", assemblyPath);
}

var assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
var assemblyName = AssemblyName.GetAssemblyName(assemblyPath);

// Check if the assembly is already loaded
var currentLoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
var existingAssembly = currentLoadedAssemblies.FirstOrDefault(a => AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName));
if (existingAssembly != null)
{
assemblies.Add(existingAssembly);
logger.LogWarning($"Assembly already loaded, load path ignored. path={0} assembly={1}", assemblyPath, assemblyName);
continue;
}
// Check if the assembly is already loaded
var currentLoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
var existingAssembly = currentLoadedAssemblies.FirstOrDefault(a => AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName));
if (existingAssembly != null)
{
assemblies.Add(existingAssembly);
logger.LogWarning($"Assembly already loaded, load path ignored. path={0} assembly={1}", assemblyPath, assemblyName);
continue;
}

// Load the assembly
Assembly loadedAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
logger.LogInformation($"Loaded assembly. assembly={0}", assemblyName);
// Load the assembly
Assembly loadedAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
logger.LogInformation($"Loaded assembly. assembly={0}", assemblyName);

// Log dependent assemblies
if (logger.IsEnabled(LogLevel.Debug))
{
var newLoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
var diffAssemblies = newLoadedAssemblies.Except(currentLoadedAssemblies);
// Log dependent assemblies
if (logger.IsEnabled(LogLevel.Debug))
{
var newLoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
var diffAssemblies = newLoadedAssemblies.Except(currentLoadedAssemblies);

foreach (Assembly asm in diffAssemblies)
foreach (Assembly asm in diffAssemblies)
{
if (asm != loadedAssembly) // Exclude the primary assembly
{
if (asm != loadedAssembly) // Exclude the primary assembly
{
logger.LogDebug($"Loaded dependent assembly. assembly={0}", asm.GetName());
}
logger.LogDebug($"Loaded dependent assembly. assembly={0}", asm.GetName());
}
}

// Register
assemblies.Add(loadedAssembly);
}

return assemblies.AsEnumerable();
});
// Register
assemblies.Add(loadedAssembly);
}

return Task.FromResult(assemblies.AsEnumerable());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public sealed class PublishedCommandSourceContext
/// </summary>
public PublishedCommandSourceContext()
{
PublishedAssemblies = new Dictionary<string, string>();
PublishedAssemblies = [];
}

/// <summary>
/// The collection of published assembly with extension and its publish folder.
/// </summary>
/// <remarks>
/// Key is the published assembly file name with extension, and value is its publish folder that contains all the dependencies and the assembly itself.
/// </remarks>
public Dictionary<string, string> PublishedAssemblies { get; }
}
}
Loading

0 comments on commit 0cdf612

Please # to comment.