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()
{
var command = new Command("deploy", "Deploy the application");
var argument = new Argument("path")
var command = new Command("deploy", "Deploy the application")
{
Description = "A solution or project file to generate a yaml manifest from",
Arity = ArgumentArity.ZeroOrOne
CommonArguments.Path_Required,
StandardOptions.Interactive,
StandardOptions.Verbosity,
};
command.Add(argument);
command.Add(StandardOptions.Interactive);
command.Add(StandardOptions.Verbosity);
command.Handler = CommandHandler.Create<IConsole, string, Verbosity, bool>((console, path, verbosity, interactive) =>
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool>((console, path, verbosity, interactive) =>
{
var application = ResolveApplication(path);
if (application is null)
// Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
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);
});

117
src/tye/Program.InitCommand.cs

@ -16,22 +16,17 @@ namespace Tye
{
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",
Arity = ArgumentArity.ZeroOrOne
};
command.AddArgument(argument);
command.Handler = CommandHandler.Create<IConsole, string>((console, path) =>
{
if (File.Exists("tye.yaml"))
if (path is FileInfo &&
path.Exists &&
(string.Equals(".yml", path.Extension, StringComparison.OrdinalIgnoreCase) ||
string.Equals(".yaml", path.Extension, StringComparison.OrdinalIgnoreCase)))
{
console.Out.WriteLine("\"tye.yaml\" already exists.");
return;
throw new CommandException($"File '{path.FullName}' already exists.");
}
var template = @"- name: app
@ -46,39 +41,32 @@ namespace Tye
# - port: 8080 # number port of the binding
";
try
if (path is FileInfo && path.Exists)
{
var application = ResolveApplication(path);
if (application is object)
{
var serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
.Build();
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();
var extension = path.Extension.ToLowerInvariant();
var directory = path.Directory;
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")
// Clear all bindings if any for solutions and project files
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj")
{
foreach (var d in descriptions)
{
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);
}
d.Bindings = null;
d.Replicas = null;
d.Build = null;
d.Configuration = null;
d.Project = d.Project.Substring(directory.FullName.Length).TrimStart(Path.DirectorySeparatorChar);
}
template = serializer.Serialize(descriptions);
}
}
catch (FileNotFoundException)
{
// No file found, just generate a new one
template = serializer.Serialize(descriptions);
}
File.WriteAllText("tye.yaml", template);
@ -88,66 +76,29 @@ namespace Tye
return command;
}
private static Application? ResolveApplication(string? path)
private static Application ResolveApplication(FileInfo file)
{
if (string.IsNullOrEmpty(path))
{
path = ResolveFileFromDirectory(Directory.GetCurrentDirectory());
}
else if (Directory.Exists(path))
if (!file.Exists)
{
path = ResolveFileFromDirectory(Path.GetFullPath(path));
throw new FileNotFoundException($"File '{file.FullName}' does not exist");
}
if (path == null)
{
return null;
}
if (!File.Exists(path))
{
throw new FileNotFoundException($"{path} does not exist");
}
switch (Path.GetExtension(path).ToLower())
switch (file.Extension.ToLower())
{
case ".yaml":
case ".yml":
return Application.FromYaml(path);
return Application.FromYaml(file.FullName);
case ".csproj":
case ".fsproj":
return Application.FromProject(path);
return Application.FromProject(file.FullName);
case ".sln":
return Application.FromSolution(path);
return Application.FromSolution(file.FullName);
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.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using Micronetes.Hosting;
namespace Tye
@ -11,12 +12,7 @@ namespace Tye
{
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
CommonArguments.Path_Required,
};
// TODO: We'll need to support a --build-args
@ -53,16 +49,15 @@ namespace Tye
Required = false
});
command.AddArgument(argument);
command.Handler = CommandHandler.Create<IConsole, string>((console, path) =>
command.Handler = CommandHandler.Create<IConsole, FileInfo>((console, path) =>
{
var application = ResolveApplication(path);
if (application is null)
// Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
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);
});

7
src/tye/Program.cs

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

Loading…
Cancel
Save