mirror of https://github.com/dotnet/tye.git
committed by
GitHub
19 changed files with 417 additions and 303 deletions
@ -0,0 +1,34 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio 15 |
||||
|
VisualStudioVersion = 15.0.26124.0 |
||||
|
MinimumVisualStudioVersion = 15.0.26124.0 |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test-project", "test-project\test-project.csproj", "{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Debug|x64 = Debug|x64 |
||||
|
Debug|x86 = Debug|x86 |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
Release|x64 = Release|x64 |
||||
|
Release|x86 = Release|x86 |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Debug|x64.ActiveCfg = Debug|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Debug|x64.Build.0 = Debug|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Debug|x86.ActiveCfg = Debug|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Debug|x86.Build.0 = Debug|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Release|x64.ActiveCfg = Release|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Release|x64.Build.0 = Release|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Release|x86.ActiveCfg = Release|Any CPU |
||||
|
{7D3606B2-7B8E-4ABB-BE0A-E0B18285D8F5}.Release|x86.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
||||
@ -0,0 +1,26 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace test_project |
||||
|
{ |
||||
|
public class Program |
||||
|
{ |
||||
|
public static void Main(string[] args) |
||||
|
{ |
||||
|
CreateHostBuilder(args).Build().Run(); |
||||
|
} |
||||
|
|
||||
|
public static IHostBuilder CreateHostBuilder(string[] args) => |
||||
|
Host.CreateDefaultBuilder(args) |
||||
|
.ConfigureWebHostDefaults(webBuilder => |
||||
|
{ |
||||
|
webBuilder.UseStartup<Startup>(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
{ |
||||
|
"iisSettings": { |
||||
|
"windowsAuthentication": false, |
||||
|
"anonymousAuthentication": true, |
||||
|
"iisExpress": { |
||||
|
"applicationUrl": "http://localhost:18482", |
||||
|
"sslPort": 44344 |
||||
|
} |
||||
|
}, |
||||
|
"profiles": { |
||||
|
"IIS Express": { |
||||
|
"commandName": "IISExpress", |
||||
|
"launchBrowser": true, |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
}, |
||||
|
"test_project": { |
||||
|
"commandName": "Project", |
||||
|
"launchBrowser": true, |
||||
|
"applicationUrl": "https://localhost:5001;http://localhost:5000", |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
|
||||
|
namespace test_project |
||||
|
{ |
||||
|
public class Startup |
||||
|
{ |
||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
|
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
|
public void ConfigureServices(IServiceCollection services) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
||||
|
{ |
||||
|
if (env.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseDeveloperExceptionPage(); |
||||
|
} |
||||
|
|
||||
|
app.UseRouting(); |
||||
|
|
||||
|
app.UseEndpoints(endpoints => |
||||
|
{ |
||||
|
endpoints.MapGet("/", async context => |
||||
|
{ |
||||
|
await context.Response.WriteAsync("Hello World!"); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft": "Warning", |
||||
|
"Microsoft.Hosting.Lifetime": "Information" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft": "Warning", |
||||
|
"Microsoft.Hosting.Lifetime": "Information" |
||||
|
} |
||||
|
}, |
||||
|
"AllowedHosts": "*" |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk.Web"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp3.1</TargetFramework> |
||||
|
<RootNamespace>test_project</RootNamespace> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -1,259 +0,0 @@ |
|||||
using System; |
|
||||
using System.CommandLine; |
|
||||
using System.CommandLine.Builder; |
|
||||
using System.CommandLine.Invocation; |
|
||||
using System.CommandLine.IO; |
|
||||
using System.CommandLine.Parsing; |
|
||||
using System.CommandLine.Rendering; |
|
||||
using System.IO; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using System.Threading.Tasks; |
|
||||
using Micronetes.Hosting; |
|
||||
using Micronetes.Hosting.Model; |
|
||||
using YamlDotNet.Serialization; |
|
||||
using YamlDotNet.Serialization.NamingConventions; |
|
||||
|
|
||||
namespace Micronetes.Host |
|
||||
{ |
|
||||
class Program |
|
||||
{ |
|
||||
static async Task<int> Main(string[] args) |
|
||||
{ |
|
||||
var command = new RootCommand(); |
|
||||
|
|
||||
command.Add(RunCommand(args)); |
|
||||
command.Add(NewCommand()); |
|
||||
|
|
||||
command.Description = "Process manager and orchestrator for microservices."; |
|
||||
|
|
||||
var builder = new CommandLineBuilder(command); |
|
||||
builder.UseHelp(); |
|
||||
builder.UseVersionOption(); |
|
||||
builder.UseDebugDirective(); |
|
||||
builder.UseParseErrorReporting(); |
|
||||
builder.ParseResponseFileAs(ResponseFileHandling.ParseArgsAsSpaceSeparated); |
|
||||
builder.UsePrefixes(new[] { "-", "--", }); // disable garbage windows conventions
|
|
||||
|
|
||||
builder.CancelOnProcessTermination(); |
|
||||
builder.UseExceptionHandler(HandleException); |
|
||||
|
|
||||
// Allow fancy drawing.
|
|
||||
builder.UseAnsiTerminalWhenAvailable(); |
|
||||
|
|
||||
var parser = builder.Build(); |
|
||||
return await parser.InvokeAsync(args); |
|
||||
} |
|
||||
|
|
||||
private static Command NewCommand() |
|
||||
{ |
|
||||
var command = new Command("new", "create a yaml manifest") |
|
||||
{ |
|
||||
}; |
|
||||
|
|
||||
var argument = new Argument("path") |
|
||||
{ |
|
||||
Description = "A solution or project file to generate a yaml manifest from", |
|
||||
Arity = ArgumentArity.ZeroOrOne |
|
||||
}; |
|
||||
|
|
||||
command.AddArgument(argument); |
|
||||
|
|
||||
command.Handler = CommandHandler.Create<IConsole, string>((console, path) => |
|
||||
{ |
|
||||
if (File.Exists("m8s.yaml")) |
|
||||
{ |
|
||||
console.Out.WriteLine("\"m8s.yaml\" already exists."); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var template = @"- name: app
|
|
||||
# project: app.csproj # msbuild project path (relative to this file) |
|
||||
# executable: app.exe # path to an executable (relative to this file) |
|
||||
# args: --arg1=3 # arguments to pass to the process |
|
||||
# replicas: 5 # number of times to launch the application |
|
||||
# env: # array of environment variables |
|
||||
# - name: key |
|
||||
# value: value |
|
||||
# bindings: # optional array of bindings (ports, connection strings) |
|
||||
# - port: 8080 # number port of the binding |
|
||||
";
|
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
if (!string.IsNullOrEmpty(path)) |
|
||||
{ |
|
||||
var application = ResolveApplication(path); |
|
||||
var serializer = new SerializerBuilder() |
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance) |
|
||||
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) |
|
||||
.Build(); |
|
||||
|
|
||||
var extension = Path.GetExtension(application.Source).ToLowerInvariant(); |
|
||||
var directory = Path.GetDirectoryName(application.Source); |
|
||||
var descriptions = application.Services.Select(s => s.Value.Description).ToList(); |
|
||||
|
|
||||
// Clear all bindings if any for solutions and project files
|
|
||||
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj") |
|
||||
{ |
|
||||
foreach (var d in descriptions) |
|
||||
{ |
|
||||
d.Bindings = null; |
|
||||
d.Replicas = null; |
|
||||
d.Build = null; |
|
||||
d.Configuration = null; |
|
||||
d.Project = d.Project.Substring(directory.Length).TrimStart(Path.DirectorySeparatorChar); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template = serializer.Serialize(descriptions); |
|
||||
} |
|
||||
} |
|
||||
catch (FileNotFoundException) |
|
||||
{ |
|
||||
// No file found, just generate a new one
|
|
||||
} |
|
||||
|
|
||||
File.WriteAllText("m8s.yaml", template); |
|
||||
console.Out.WriteLine("Created \"m8s.yaml\""); |
|
||||
}); |
|
||||
|
|
||||
return command; |
|
||||
} |
|
||||
|
|
||||
private static Command RunCommand(string[] args) |
|
||||
{ |
|
||||
var command = new Command("run", "run the application") |
|
||||
{ |
|
||||
}; |
|
||||
|
|
||||
var argument = new Argument("path") |
|
||||
{ |
|
||||
Description = "A file or directory to execute. Supports a project files, solution files or a yaml manifest.", |
|
||||
Arity = ArgumentArity.ZeroOrOne |
|
||||
}; |
|
||||
|
|
||||
// TODO: We'll need to support a --build-args
|
|
||||
command.AddOption(new Option("--no-build") |
|
||||
{ |
|
||||
Description = "Do not build project files before running.", |
|
||||
Required = false |
|
||||
}); |
|
||||
|
|
||||
command.AddOption(new Option("--port") |
|
||||
{ |
|
||||
Description = "The port to run control plane on.", |
|
||||
Argument = new Argument<int>("port"), |
|
||||
Required = false |
|
||||
}); |
|
||||
|
|
||||
command.AddOption(new Option("--logs") |
|
||||
{ |
|
||||
Description = "Write structured application logs to the specified log providers. Supported providers are console, elastic (Elasticsearch), ai (ApplicationInsights), seq.", |
|
||||
Argument = new Argument<string>("logs"), |
|
||||
Required = false |
|
||||
}); |
|
||||
|
|
||||
command.AddOption(new Option("--dtrace") |
|
||||
{ |
|
||||
Description = "Write distributed traces to the specified providers. Supported providers are zipkin.", |
|
||||
Argument = new Argument<string>("logs"), |
|
||||
Required = false |
|
||||
}); |
|
||||
|
|
||||
command.AddOption(new Option("--debug") |
|
||||
{ |
|
||||
Description = "Wait for debugger attach in all services.", |
|
||||
Required = false |
|
||||
}); |
|
||||
|
|
||||
command.AddArgument(argument); |
|
||||
|
|
||||
command.Handler = CommandHandler.Create<IConsole, string>((console, path) => |
|
||||
{ |
|
||||
Application app = ResolveApplication(path); |
|
||||
return MicronetesHost.RunAsync(app, args); |
|
||||
}); |
|
||||
|
|
||||
return command; |
|
||||
} |
|
||||
|
|
||||
private static Application ResolveApplication(string path) |
|
||||
{ |
|
||||
if (string.IsNullOrEmpty(path)) |
|
||||
{ |
|
||||
path = ResolveFileFromDirectory(Directory.GetCurrentDirectory()); |
|
||||
} |
|
||||
else if (Directory.Exists(path)) |
|
||||
{ |
|
||||
path = ResolveFileFromDirectory(Path.GetFullPath(path)); |
|
||||
} |
|
||||
|
|
||||
if (!File.Exists(path)) |
|
||||
{ |
|
||||
throw new FileNotFoundException($"{path} does not exist"); |
|
||||
} |
|
||||
|
|
||||
switch (Path.GetExtension(path).ToLower()) |
|
||||
{ |
|
||||
case ".yaml": |
|
||||
case ".yml": |
|
||||
return Application.FromYaml(path); |
|
||||
case ".csproj": |
|
||||
case ".fsproj": |
|
||||
return Application.FromProject(path); |
|
||||
case ".sln": |
|
||||
return Application.FromSolution(path); |
|
||||
default: |
|
||||
throw new NotSupportedException($"{path} not supported"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private static string ResolveFileFromDirectory(string basePath) |
|
||||
{ |
|
||||
var formats = new[] { "m8s.yaml", "m8s.yml", "*.csproj", "*.fsproj", "*.sln" }; |
|
||||
|
|
||||
foreach (var format in formats) |
|
||||
{ |
|
||||
var files = Directory.GetFiles(basePath, format); |
|
||||
if (files.Length == 0) |
|
||||
{ |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
if (files.Length > 1) |
|
||||
{ |
|
||||
throw new InvalidOperationException($"Ambiguous match found {string.Join(", ", files.Select(Path.GetFileName))}"); |
|
||||
} |
|
||||
|
|
||||
return files[0]; |
|
||||
} |
|
||||
|
|
||||
throw new InvalidOperationException($"None of the supported files were found (m8s.yaml, .csproj, .fsproj, .sln)"); |
|
||||
} |
|
||||
|
|
||||
private static void HandleException(Exception exception, InvocationContext context) |
|
||||
{ |
|
||||
// context.Console.ResetTerminalForegroundColor();
|
|
||||
// context.Console.SetTerminalForegroundColor(ConsoleColor.Red);
|
|
||||
|
|
||||
if (exception is OperationCanceledException) |
|
||||
{ |
|
||||
context.Console.Error.WriteLine("operation canceled."); |
|
||||
} |
|
||||
else if (exception is TargetInvocationException tae) |
|
||||
{ |
|
||||
context.Console.Error.WriteLine(tae.InnerException.Message); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
context.Console.Error.WriteLine("unhandled exception: "); |
|
||||
context.Console.Error.WriteLine(exception.ToString()); |
|
||||
} |
|
||||
|
|
||||
// context.Console.ResetTerminalForegroundColor();
|
|
||||
|
|
||||
context.ResultCode = 1; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,7 +0,0 @@ |
|||||
{ |
|
||||
"profiles": { |
|
||||
"dotnet-micronetes": { |
|
||||
"commandName": "Project" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|
||||
<PackAsTool>true</PackAsTool> |
|
||||
<RootNamespace>Micronetes.Host</RootNamespace> |
|
||||
<AssemblyName>m8s</AssemblyName> |
|
||||
<ToolCommandName>m8s</ToolCommandName> |
|
||||
<Description>Local orchestrator for microservices.</Description> |
|
||||
<Nullable>disable</Nullable> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\Micronetes.Hosting\Micronetes.Hosting.csproj" /> |
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20071.2" /> |
|
||||
<PackageReference Include="System.Commandline.Rendering" Version="0.3.0-alpha.20070.2" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Tye |
||||
|
{ |
||||
|
internal static class NameSanitizer |
||||
|
{ |
||||
|
// Converts an arbitrary string into something usable as a C# identifier.
|
||||
|
//
|
||||
|
// For instance the project file might be `test-project.csproj` but `test-project` is
|
||||
|
// not what will appear in `launchSettings.json`. We need to convert to
|
||||
|
// `test_project`
|
||||
|
public static string SanitizeToIdentifier(string name) |
||||
|
{ |
||||
|
if (name is null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(name)); |
||||
|
} |
||||
|
|
||||
|
// This is not perfect. For now it just handles cases we've encountered.
|
||||
|
return name.Replace("-", "_"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,153 @@ |
|||||
|
using System; |
||||
|
using System.CommandLine; |
||||
|
using System.CommandLine.Invocation; |
||||
|
using System.CommandLine.IO; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using Micronetes.Hosting.Model; |
||||
|
using YamlDotNet.Serialization; |
||||
|
using YamlDotNet.Serialization.NamingConventions; |
||||
|
|
||||
|
namespace Tye |
||||
|
{ |
||||
|
static partial class Program |
||||
|
{ |
||||
|
private static Command CreateInitCommand() |
||||
|
{ |
||||
|
var command = new Command("init", "create a yaml manifest") |
||||
|
{ |
||||
|
}; |
||||
|
|
||||
|
var argument = new Argument("path") |
||||
|
{ |
||||
|
Description = "A solution or project file to generate a yaml manifest from", |
||||
|
Arity = ArgumentArity.ZeroOrOne |
||||
|
}; |
||||
|
|
||||
|
command.AddArgument(argument); |
||||
|
|
||||
|
command.Handler = CommandHandler.Create<IConsole, string>((console, path) => |
||||
|
{ |
||||
|
if (File.Exists("tye.yaml")) |
||||
|
{ |
||||
|
console.Out.WriteLine("\"tye.yaml\" already exists."); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var template = @"- name: app
|
||||
|
# project: app.csproj # msbuild project path (relative to this file) |
||||
|
# executable: app.exe # path to an executable (relative to this file) |
||||
|
# args: --arg1=3 # arguments to pass to the process |
||||
|
# replicas: 5 # number of times to launch the application |
||||
|
# env: # array of environment variables |
||||
|
# - name: key |
||||
|
# value: value |
||||
|
# bindings: # optional array of bindings (ports, connection strings) |
||||
|
# - port: 8080 # number port of the binding |
||||
|
";
|
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
var application = ResolveApplication(path); |
||||
|
if (application is object) |
||||
|
{ |
||||
|
var serializer = new SerializerBuilder() |
||||
|
.WithNamingConvention(CamelCaseNamingConvention.Instance) |
||||
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) |
||||
|
.Build(); |
||||
|
|
||||
|
var extension = Path.GetExtension(application.Source).ToLowerInvariant(); |
||||
|
var directory = Path.GetDirectoryName(application.Source); |
||||
|
var descriptions = application.Services.Select(s => s.Value.Description).ToList(); |
||||
|
|
||||
|
// Clear all bindings if any for solutions and project files
|
||||
|
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj") |
||||
|
{ |
||||
|
foreach (var d in descriptions) |
||||
|
{ |
||||
|
d.Bindings = null; |
||||
|
d.Replicas = null; |
||||
|
d.Build = null; |
||||
|
d.Configuration = null; |
||||
|
d.Project = d.Project.Substring(directory.Length).TrimStart(Path.DirectorySeparatorChar); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template = serializer.Serialize(descriptions); |
||||
|
} |
||||
|
} |
||||
|
catch (FileNotFoundException) |
||||
|
{ |
||||
|
// No file found, just generate a new one
|
||||
|
} |
||||
|
|
||||
|
File.WriteAllText("tye.yaml", template); |
||||
|
console.Out.WriteLine("Created \"tye.yaml\""); |
||||
|
}); |
||||
|
|
||||
|
return command; |
||||
|
} |
||||
|
|
||||
|
private static Application? ResolveApplication(string? path) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(path)) |
||||
|
{ |
||||
|
path = ResolveFileFromDirectory(Directory.GetCurrentDirectory()); |
||||
|
} |
||||
|
else if (Directory.Exists(path)) |
||||
|
{ |
||||
|
path = ResolveFileFromDirectory(Path.GetFullPath(path)); |
||||
|
} |
||||
|
|
||||
|
if (path == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (!File.Exists(path)) |
||||
|
{ |
||||
|
throw new FileNotFoundException($"{path} does not exist"); |
||||
|
} |
||||
|
|
||||
|
switch (Path.GetExtension(path).ToLower()) |
||||
|
{ |
||||
|
case ".yaml": |
||||
|
case ".yml": |
||||
|
return Application.FromYaml(path); |
||||
|
|
||||
|
case ".csproj": |
||||
|
case ".fsproj": |
||||
|
return Application.FromProject(path); |
||||
|
|
||||
|
case ".sln": |
||||
|
return Application.FromSolution(path); |
||||
|
|
||||
|
default: |
||||
|
throw new NotSupportedException($"{path} not supported"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static string? ResolveFileFromDirectory(string basePath) |
||||
|
{ |
||||
|
var formats = new[] { "tye.yaml", "tye.yml", "*.csproj", "*.fsproj", "*.sln" }; |
||||
|
|
||||
|
foreach (var format in formats) |
||||
|
{ |
||||
|
var files = Directory.GetFiles(basePath, format); |
||||
|
if (files.Length == 0) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (files.Length > 1) |
||||
|
{ |
||||
|
throw new InvalidOperationException($"Ambiguous match found {string.Join(", ", files.Select(Path.GetFileName))}"); |
||||
|
} |
||||
|
|
||||
|
return files[0]; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
using System; |
||||
|
using System.CommandLine; |
||||
|
using System.CommandLine.Invocation; |
||||
|
using Micronetes.Hosting; |
||||
|
|
||||
|
namespace Tye |
||||
|
{ |
||||
|
static partial class Program |
||||
|
{ |
||||
|
private static Command CreateRunCommand(string[] args) |
||||
|
{ |
||||
|
var command = new Command("run", "run the application") |
||||
|
{ |
||||
|
}; |
||||
|
|
||||
|
var argument = new Argument("path") |
||||
|
{ |
||||
|
Description = "A file or directory to execute. Supports a project files, solution files or a yaml manifest.", |
||||
|
Arity = ArgumentArity.ZeroOrOne |
||||
|
}; |
||||
|
|
||||
|
// TODO: We'll need to support a --build-args
|
||||
|
command.AddOption(new Option("--no-build") |
||||
|
{ |
||||
|
Description = "Do not build project files before running.", |
||||
|
Required = false |
||||
|
}); |
||||
|
|
||||
|
command.AddOption(new Option("--port") |
||||
|
{ |
||||
|
Description = "The port to run control plane on.", |
||||
|
Argument = new Argument<int>("port"), |
||||
|
Required = false |
||||
|
}); |
||||
|
|
||||
|
command.AddOption(new Option("--logs") |
||||
|
{ |
||||
|
Description = "Write structured application logs to the specified log providers. Supported providers are console, elastic (Elasticsearch), ai (ApplicationInsights), seq.", |
||||
|
Argument = new Argument<string>("logs"), |
||||
|
Required = false |
||||
|
}); |
||||
|
|
||||
|
command.AddOption(new Option("--dtrace") |
||||
|
{ |
||||
|
Description = "Write distributed traces to the specified providers. Supported providers are zipkin.", |
||||
|
Argument = new Argument<string>("logs"), |
||||
|
Required = false |
||||
|
}); |
||||
|
|
||||
|
command.AddOption(new Option("--debug") |
||||
|
{ |
||||
|
Description = "Wait for debugger attach in all services.", |
||||
|
Required = false |
||||
|
}); |
||||
|
|
||||
|
command.AddArgument(argument); |
||||
|
|
||||
|
command.Handler = CommandHandler.Create<IConsole, string>((console, path) => |
||||
|
{ |
||||
|
var application = ResolveApplication(path); |
||||
|
if (application is null) |
||||
|
{ |
||||
|
throw new CommandException($"None of the supported files were found (tye.yaml, .csproj, .fsproj, .sln)"); |
||||
|
} |
||||
|
|
||||
|
return MicronetesHost.RunAsync(application, args); |
||||
|
}); |
||||
|
|
||||
|
return command; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue