Browse Source

Move arg validation inside arg parsing

davidfowl/dependencies
Ryan Nowak 7 years ago
parent
commit
78a877bf00
  1. 105
      src/tye/CommonArguments.cs
  2. 22
      src/tye/Program.DeployCommand.cs
  3. 117
      src/tye/Program.InitCommand.cs
  4. 19
      src/tye/Program.RunCommand.cs
  5. 7
      src/tye/Program.cs

105
src/tye/CommonArguments.cs

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Parsing;
using System.IO;
namespace Tye
{
internal static class CommonArguments
{
private static readonly string[] FileFormats = new[] { "tye.yaml", "tye.yml", "*.csproj", "*.fsproj", "*.sln" };
public static Argument<FileInfo> Path_Optional
{
get
{
return new Argument<FileInfo>((r) => TryParsePath(r, required: false), isDefault: true)
{
Arity = ArgumentArity.ZeroOrOne,
Description = "file or directory, can be a yaml, sln, or project file",
Name = "path",
};
}
}
public static Argument<FileInfo> Path_Required
{
get
{
return new Argument<FileInfo>((r) => TryParsePath(r, required: true), isDefault: true)
{
Arity = ArgumentArity.ZeroOrOne,
Description = "file or directory, can be a yaml, sln, or project file",
Name = "path",
};
}
}
public static bool TryFindSupportedFile(string directoryPath, out string? filePath, out string? errorMessage)
{
foreach (var format in FileFormats)
{
var files = Directory.GetFiles(directoryPath, format);
if (files.Length == 1)
{
errorMessage = null;
filePath = files[0];
return true;
}
if (files.Length > 1)
{
errorMessage = $"More than one matching files was found in directory '{directoryPath}'.";
filePath = default;
return false;
}
}
errorMessage = $"No project project file or solution was found in directory '{directoryPath}'.";
filePath = default;
return false;
}
static FileInfo TryParsePath(ArgumentResult result, bool required)
{
var token = result.Tokens.Count switch
{
0 => ".",
1 => result.Tokens[0].Value,
_ => throw new InvalidOperationException("Unexpected token count."),
};
if (string.IsNullOrEmpty(token))
{
token = ".";
}
if (File.Exists(token))
{
return new FileInfo(token);
}
if (Directory.Exists(token))
{
if (TryFindSupportedFile(token, out var filePath, out var errorMessage))
{
return new FileInfo(filePath);
}
else if (required)
{
result.ErrorMessage = errorMessage;
return default!;
}
else
{
return default!;
}
}
result.ErrorMessage = $"The file '{token}' could not be found.";
return default!;
}
}
}

22
src/tye/Program.DeployCommand.cs

@ -13,26 +13,22 @@ namespace Tye
{ {
public static Command CreateDeployCommand() public static Command CreateDeployCommand()
{ {
var command = new Command("deploy", "Deploy the application"); var command = new Command("deploy", "Deploy the application")
var argument = new Argument("path")
{ {
Description = "A solution or project file to generate a yaml manifest from", CommonArguments.Path_Required,
Arity = ArgumentArity.ZeroOrOne StandardOptions.Interactive,
StandardOptions.Verbosity,
}; };
command.Add(argument); command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool>((console, path, verbosity, interactive) =>
command.Add(StandardOptions.Interactive);
command.Add(StandardOptions.Verbosity);
command.Handler = CommandHandler.Create<IConsole, string, Verbosity, bool>((console, path, verbosity, interactive) =>
{ {
var application = ResolveApplication(path); // Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
if (application is null) if (path is null)
{ {
throw new CommandException($"None of the supported files were found (tye.yaml, .csproj, .fsproj, .sln)"); throw new CommandException("No project or solution file was found.");
} }
var application = ResolveApplication(path);
return ExecuteAsync(new OutputContext(console, verbosity), application, environment: "production", interactive); return ExecuteAsync(new OutputContext(console, verbosity), application, environment: "production", interactive);
}); });

117
src/tye/Program.InitCommand.cs

@ -16,22 +16,17 @@ namespace Tye
{ {
var command = new Command("init", "create a yaml manifest") var command = new Command("init", "create a yaml manifest")
{ {
CommonArguments.Path_Optional,
}; };
var argument = new Argument("path") command.Handler = CommandHandler.Create<IConsole, FileInfo?>((console, path) =>
{ {
Description = "A solution or project file to generate a yaml manifest from", if (path is FileInfo &&
Arity = ArgumentArity.ZeroOrOne path.Exists &&
}; (string.Equals(".yml", path.Extension, StringComparison.OrdinalIgnoreCase) ||
string.Equals(".yaml", path.Extension, StringComparison.OrdinalIgnoreCase)))
command.AddArgument(argument);
command.Handler = CommandHandler.Create<IConsole, string>((console, path) =>
{
if (File.Exists("tye.yaml"))
{ {
console.Out.WriteLine("\"tye.yaml\" already exists."); throw new CommandException($"File '{path.FullName}' already exists.");
return;
} }
var template = @"- name: app var template = @"- name: app
@ -46,39 +41,32 @@ namespace Tye
# - port: 8080 # number port of the binding # - port: 8080 # number port of the binding
"; ";
try if (path is FileInfo && path.Exists)
{ {
var application = ResolveApplication(path); var application = ResolveApplication(path);
if (application is object) var serializer = new SerializerBuilder()
{ .WithNamingConvention(CamelCaseNamingConvention.Instance)
var serializer = new SerializerBuilder() .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
.WithNamingConvention(CamelCaseNamingConvention.Instance) .Build();
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
.Build();
var extension = Path.GetExtension(application.Source).ToLowerInvariant(); var extension = path.Extension.ToLowerInvariant();
var directory = Path.GetDirectoryName(application.Source); var directory = path.Directory;
var descriptions = application.Services.Select(s => s.Value.Description).ToList(); var descriptions = application.Services.Select(s => s.Value.Description).ToList();
// Clear all bindings if any for solutions and project files // Clear all bindings if any for solutions and project files
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj") if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj")
{
foreach (var d in descriptions)
{ {
foreach (var d in descriptions) d.Bindings = null;
{ d.Replicas = null;
d.Bindings = null; d.Build = null;
d.Replicas = null; d.Configuration = null;
d.Build = null; d.Project = d.Project.Substring(directory.FullName.Length).TrimStart(Path.DirectorySeparatorChar);
d.Configuration = null;
d.Project = d.Project.Substring(directory.Length).TrimStart(Path.DirectorySeparatorChar);
}
} }
template = serializer.Serialize(descriptions);
} }
}
catch (FileNotFoundException) template = serializer.Serialize(descriptions);
{
// No file found, just generate a new one
} }
File.WriteAllText("tye.yaml", template); File.WriteAllText("tye.yaml", template);
@ -88,66 +76,29 @@ namespace Tye
return command; return command;
} }
private static Application? ResolveApplication(string? path) private static Application ResolveApplication(FileInfo file)
{ {
if (string.IsNullOrEmpty(path)) if (!file.Exists)
{
path = ResolveFileFromDirectory(Directory.GetCurrentDirectory());
}
else if (Directory.Exists(path))
{ {
path = ResolveFileFromDirectory(Path.GetFullPath(path)); throw new FileNotFoundException($"File '{file.FullName}' does not exist");
} }
if (path == null) switch (file.Extension.ToLower())
{
return null;
}
if (!File.Exists(path))
{
throw new FileNotFoundException($"{path} does not exist");
}
switch (Path.GetExtension(path).ToLower())
{ {
case ".yaml": case ".yaml":
case ".yml": case ".yml":
return Application.FromYaml(path); return Application.FromYaml(file.FullName);
case ".csproj": case ".csproj":
case ".fsproj": case ".fsproj":
return Application.FromProject(path); return Application.FromProject(file.FullName);
case ".sln": case ".sln":
return Application.FromSolution(path); return Application.FromSolution(file.FullName);
default: default:
throw new NotSupportedException($"{path} not supported"); throw new NotSupportedException($"File '{file.FullName}' is not a supported format.");
} }
} }
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;
}
} }
} }

19
src/tye/Program.RunCommand.cs

@ -1,6 +1,7 @@
using System; using System;
using System.CommandLine; using System.CommandLine;
using System.CommandLine.Invocation; using System.CommandLine.Invocation;
using System.IO;
using Micronetes.Hosting; using Micronetes.Hosting;
namespace Tye namespace Tye
@ -11,12 +12,7 @@ namespace Tye
{ {
var command = new Command("run", "run the application") var command = new Command("run", "run the application")
{ {
}; CommonArguments.Path_Required,
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 // TODO: We'll need to support a --build-args
@ -53,16 +49,15 @@ namespace Tye
Required = false Required = false
}); });
command.AddArgument(argument); command.Handler = CommandHandler.Create<IConsole, FileInfo>((console, path) =>
command.Handler = CommandHandler.Create<IConsole, string>((console, path) =>
{ {
var application = ResolveApplication(path); // Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
if (application is null) if (path is null)
{ {
throw new CommandException($"None of the supported files were found (tye.yaml, .csproj, .fsproj, .sln)"); throw new CommandException("No project or solution file was found.");
} }
var application = ResolveApplication(path);
return MicronetesHost.RunAsync(application, args); return MicronetesHost.RunAsync(application, args);
}); });

7
src/tye/Program.cs

@ -5,6 +5,7 @@ using System.CommandLine.Help;
using System.CommandLine.Invocation; using System.CommandLine.Invocation;
using System.CommandLine.IO; using System.CommandLine.IO;
using System.CommandLine.Parsing; using System.CommandLine.Parsing;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Tye namespace Tye
@ -48,6 +49,12 @@ namespace Tye
context.Console.ResetTerminalForegroundColor(); context.Console.ResetTerminalForegroundColor();
context.Console.SetTerminalForegroundColor(ConsoleColor.Red); context.Console.SetTerminalForegroundColor(ConsoleColor.Red);
if (exception is TargetInvocationException tie &&
tie.InnerException is object)
{
exception = tie.InnerException;
}
if (exception is OperationCanceledException) if (exception is OperationCanceledException)
{ {
context.Console.Error.WriteLine("Oh dear! Operation canceled."); context.Console.Error.WriteLine("Oh dear! Operation canceled.");

Loading…
Cancel
Save