diff --git a/docs/reference/commandline/tye-run.md b/docs/reference/commandline/tye-run.md index e147cc98e..6521d8d12 100644 --- a/docs/reference/commandline/tye-run.md +++ b/docs/reference/commandline/tye-run.md @@ -7,7 +7,7 @@ ## Synopsis ```text -tye run [-?|-h|--help] [--no-build] [--port ] [--logs ] [--dtrace ] [--metrics ] [--debug ] [--docker] [--dashboard] [--watch] [-f|--framework ] [--tags ] [-v|--verbosity ] [] +tye run [-?|-h|--help] [--no-build] [--port ] [--logs ] [--dtrace ] [--metrics ] [--debug ] [--docker] [--dashboard] [--watch ] [-f|--framework ] [--tags ] [-v|--verbosity ] [] ``` ## Description @@ -49,7 +49,7 @@ If a directory path is specified, `tye run` will default to using these files, i - `--debug ` - Waits for debugger attach to service. Specify `*` to wait to attach to all services. + Waits for debugger attach to dotnet service. Specify `*` to wait to attach to all dotnet services. - `--docker` @@ -59,9 +59,9 @@ If a directory path is specified, `tye run` will default to using these files, i Launch dashboard on run. -- `--watch` +- `--watch ` - Watches for file changes in all projects that are built by tye. Uses [`dotnet watch`](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.1) to monitor for file changes. + Watches for file changes in a dotnet project that is built by tye. Uses [`dotnet watch`](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.1) to monitor for file changes. Specify `*` to watch all dotnet services. - `-f|--framework ` diff --git a/src/Microsoft.Tye.Core/HostOptions.cs b/src/Microsoft.Tye.Core/HostOptions.cs index 73135cfd8..27e6e2f75 100644 --- a/src/Microsoft.Tye.Core/HostOptions.cs +++ b/src/Microsoft.Tye.Core/HostOptions.cs @@ -11,6 +11,7 @@ public class HostOptions public bool Dashboard { get; set; } public List Debug { get; } = new List(); + public List Watch { get; } = new List(); public string? DistributedTraceProvider { get; set; } @@ -25,7 +26,5 @@ public class HostOptions public int? Port { get; set; } public Verbosity LogVerbosity { get; set; } = Verbosity.Debug; - - public bool Watch { get; set; } } } diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs index 4dc8f1248..ae8fddb52 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs @@ -218,7 +218,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? application.PopulateEnvironment(service, (k, v) => environment[k] = v); - if (_options.DebugMode && (_options.DebugAllServices || _options.ServicesToDebug!.Contains(serviceName, StringComparer.OrdinalIgnoreCase))) + if (_options.ShouldDebugService(serviceName)) { environment["DOTNET_STARTUP_HOOKS"] = typeof(Hosting.Runtime.HostingRuntimeHelpers).Assembly.Location; } @@ -267,7 +267,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? status.StoppingTokenSource = stoppingCts; await using var _ = processInfo.StoppedTokenSource.Token.Register(() => status.StoppingTokenSource.Cancel()); - if (!_options.Watch) + if (!_options.ShouldWatchService(serviceName)) { service.Replicas[replica] = status; service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Added, status)); @@ -327,7 +327,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? WriteReplicaToStore(pid.ToString()); - if (_options.Watch) + if (_options.ShouldWatchService(serviceName)) { // OnStart/OnStop will be called multiple times for watch. // Watch will constantly be adding and removing from the list, so only add here for watch. @@ -346,7 +346,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Stopped, status)); } - if (!_options.Watch) + if (!_options.ShouldWatchService(serviceName)) { // Only increase backoff when not watching project as watch will wait for file changes before rebuild. backOff *= 2; @@ -375,7 +375,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? } }; - if (_options.Watch && (service.Description.RunInfo is ProjectRunInfo runInfo)) + if (_options.ShouldWatchService(serviceName) && (service.Description.RunInfo is ProjectRunInfo runInfo)) { var projectFile = runInfo.ProjectFile.FullName; var fileSetFactory = new MsBuildFileSetFactory(_logger, @@ -387,7 +387,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? await new DotNetWatcher(_logger) .WatchAsync(processInfo, fileSetFactory, replica, status.StoppingTokenSource.Token); } - else if (_options.Watch && (service.Description.RunInfo is AzureFunctionRunInfo azureFunctionRunInfo) && !string.IsNullOrEmpty(azureFunctionRunInfo.ProjectFile)) + else if (_options.ShouldWatchService(serviceName) && (service.Description.RunInfo is AzureFunctionRunInfo azureFunctionRunInfo) && !string.IsNullOrEmpty(azureFunctionRunInfo.ProjectFile)) { var projectFile = azureFunctionRunInfo.ProjectFile; var fileSetFactory = new MsBuildFileSetFactory(_logger, @@ -408,7 +408,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string? { _logger.LogError(0, ex, "Failed to launch process for service {ServiceName}", replica); - if (!_options.Watch) + if (!_options.ShouldWatchService(serviceName)) { // Only increase backoff when not watching project as watch will wait for file changes before rebuild. backOff *= 2; diff --git a/src/Microsoft.Tye.Hosting/ProcessRunnerOptions.cs b/src/Microsoft.Tye.Hosting/ProcessRunnerOptions.cs index 699814279..173f7dd3a 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunnerOptions.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunnerOptions.cs @@ -9,21 +9,33 @@ namespace Microsoft.Tye.Hosting { public class ProcessRunnerOptions { - public bool DebugMode { get; set; } + public const string AllServices = "*"; + public bool BuildProjects { get; set; } + + public bool DebugMode { get; set; } public string[]? ServicesToDebug { get; set; } public bool DebugAllServices { get; set; } - public bool Watch { get; set; } + public bool ShouldDebugService (string serviceName) => DebugMode && (DebugAllServices || ServicesToDebug!.Contains(serviceName, StringComparer.OrdinalIgnoreCase)); + + public bool WatchMode { get; set; } + public string[]? ServicesToWatch { get; set; } + public bool WatchAllServices { get; set; } + public bool ShouldWatchService(string serviceName) => WatchMode && (WatchAllServices || ServicesToWatch!.Contains(serviceName, StringComparer.OrdinalIgnoreCase)); public static ProcessRunnerOptions FromHostOptions(HostOptions options) { return new ProcessRunnerOptions { BuildProjects = !options.NoBuild, + DebugMode = options.Debug.Any(), ServicesToDebug = options.Debug.ToArray(), - DebugAllServices = options.Debug?.Contains("*", StringComparer.OrdinalIgnoreCase) ?? false, - Watch = options.Watch + DebugAllServices = options.Debug?.Contains(AllServices, StringComparer.OrdinalIgnoreCase) ?? false, + + WatchMode = options.Watch.Any(), + ServicesToWatch = options.Watch.ToArray(), + WatchAllServices = options.Watch?.Contains(AllServices, StringComparer.OrdinalIgnoreCase) ?? false, }; } } diff --git a/src/tye/Program.RunCommand.cs b/src/tye/Program.RunCommand.cs index dd59e428b..d16a2004f 100644 --- a/src/tye/Program.RunCommand.cs +++ b/src/tye/Program.RunCommand.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.IO; -using System.Linq; using System.Threading; using Microsoft.Tye.Hosting; @@ -56,7 +54,7 @@ private static Command CreateRunCommand() { Arity = ArgumentArity.ZeroOrMore, }, - Description = "Wait for debugger attach to specific service. Specify \"*\" to wait for all services.", + Description = $"Wait for debugger attach to specific service. Specify {ProcessRunnerOptions.AllServices} to wait for all services.", Required = false }, new Option("--docker") @@ -71,7 +69,11 @@ private static Command CreateRunCommand() }, new Option("--watch") { - Description = "Watches for code changes for all dotnet projects.", + Argument = new Argument("service") + { + Arity = ArgumentArity.ZeroOrMore, + }, + Description = $"Watches a specific dotnet service for code changes. Specify {ProcessRunnerOptions.AllServices} to watch for all dotnet services.", Required = false }, StandardOptions.Framework, @@ -111,9 +113,9 @@ private static Command CreateRunCommand() LoggingProvider = args.Logs, MetricsProvider = args.Metrics, LogVerbosity = args.Verbosity, - Watch = args.Watch }; options.Debug.AddRange(args.Debug); + options.Watch.AddRange(args.Watch); await application.ProcessExtensionsAsync(options, output, ExtensionContext.OperationKind.LocalRun); @@ -170,7 +172,7 @@ private class RunCommandArguments public Verbosity Verbosity { get; set; } - public bool Watch { get; set; } + public string[] Watch { get; set; } = Array.Empty(); public string Framework { get; set; } = default!; diff --git a/test/E2ETest/TyeRunTests.cs b/test/E2ETest/TyeRunTests.cs index 09c09434e..14d1ea44f 100644 --- a/test/E2ETest/TyeRunTests.cs +++ b/test/E2ETest/TyeRunTests.cs @@ -19,7 +19,6 @@ using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Primitives; using Microsoft.Tye; using Microsoft.Tye.Hosting; using Microsoft.Tye.Hosting.Model; @@ -353,7 +352,10 @@ public async Task FrontendBackendWatchRunTest() var client = new HttpClient(new RetryHandler(handler)); - await RunHostingApplication(application, new HostOptions() { Watch = true }, async (app, uri) => + var hostOptions = new HostOptions(); + hostOptions.Watch.Add(ProcessRunnerOptions.AllServices); + + await RunHostingApplication(application, hostOptions, async (app, uri) => { // make sure both are running var frontendUri = await GetServiceUrl(client, uri, "frontend"); @@ -404,7 +406,10 @@ public async Task WebAppWatchRunTest() var client = new HttpClient(new RetryHandler(handler)); - await RunHostingApplication(application, new HostOptions() { Watch = true }, async (app, uri) => + var hostOptions = new HostOptions(); + hostOptions.Watch.Add(ProcessRunnerOptions.AllServices); + + await RunHostingApplication(application, hostOptions, async (app, uri) => { // make sure app is running var appUri = await GetServiceUrl(client, uri, "web-app");