From dbcc90dc5a59af2a501b3ec5fd7a58af73ce3aec Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Thu, 16 Jul 2020 17:39:49 -0700 Subject: [PATCH] Support watch for azure functions and fix a bug with active replicas (#595) --- src/Microsoft.Tye.Core/ApplicationFactory.cs | 16 ++++++++-- .../AzureFunctionServiceBuilder.cs | 1 + .../Model/AzureFunctionRunInfo.cs | 2 ++ src/Microsoft.Tye.Hosting/ProcessRunner.cs | 29 +++++++++++++++---- .../Watch/DotNetWatcher.cs | 5 ++++ 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Tye.Core/ApplicationFactory.cs b/src/Microsoft.Tye.Core/ApplicationFactory.cs index 41f98e09..97ee6f0f 100644 --- a/src/Microsoft.Tye.Core/ApplicationFactory.cs +++ b/src/Microsoft.Tye.Core/ApplicationFactory.cs @@ -225,15 +225,27 @@ namespace Microsoft.Tye } else if (!string.IsNullOrEmpty(configService.AzureFunction)) { + var azureFunctionDirectory = Path.Combine(config.Source.DirectoryName!, configService.AzureFunction); + var functionBuilder = new AzureFunctionServiceBuilder( configService.Name, - Path.Combine(config.Source.DirectoryName!, configService.AzureFunction)) + azureFunctionDirectory) { Args = configService.Args, Replicas = configService.Replicas ?? 1, - FuncExecutablePath = configService.FuncExecutable + FuncExecutablePath = configService.FuncExecutable, }; + foreach (var proj in Directory.EnumerateFiles(azureFunctionDirectory)) + { + var fileInfo = new FileInfo(proj); + if (fileInfo.Extension == ".csproj" || fileInfo.Extension == ".fsproj") + { + functionBuilder.ProjectFile = fileInfo.FullName; + break; + } + } + // TODO liveness? service = functionBuilder; } diff --git a/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs b/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs index d38919e2..ff315929 100644 --- a/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs +++ b/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs @@ -16,5 +16,6 @@ namespace Microsoft.Tye public string? Args { get; set; } public string FunctionPath { get; } public string? FuncExecutablePath { get; set; } + public string? ProjectFile { get; set; } } } diff --git a/src/Microsoft.Tye.Hosting/Model/AzureFunctionRunInfo.cs b/src/Microsoft.Tye.Hosting/Model/AzureFunctionRunInfo.cs index a68f1eec..a2cc964f 100644 --- a/src/Microsoft.Tye.Hosting/Model/AzureFunctionRunInfo.cs +++ b/src/Microsoft.Tye.Hosting/Model/AzureFunctionRunInfo.cs @@ -13,10 +13,12 @@ namespace Microsoft.Tye.Hosting.Model Args = function.Args; FunctionPath = function.FunctionPath; FuncExecutablePath = function.FuncExecutablePath; + ProjectFile = function.ProjectFile; } public string? Args { get; } public string FunctionPath { get; } public string? FuncExecutablePath { get; set; } + public string? ProjectFile { get; set; } } } diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs index ebb45391..8003920f 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs @@ -322,7 +322,7 @@ namespace Microsoft.Tye.Hosting WriteReplicaToStore(pid.ToString()); - if (_options.Watch && service.Description.RunInfo is ProjectRunInfo runInfo) + if (_options.Watch) { // 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. @@ -359,16 +359,21 @@ namespace Microsoft.Tye.Hosting }, Build = async () => { - var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" /nologo", throwOnError: false, workingDirectory: application.ContextDirectory); - if (buildResult.ExitCode != 0) + if (service.Description.RunInfo is ProjectRunInfo) { - _logger.LogInformation("Building projects failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, buildResult.ExitCode); + var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" /nologo", throwOnError: false, workingDirectory: application.ContextDirectory); + if (buildResult.ExitCode != 0) + { + _logger.LogInformation("Building projects failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, buildResult.ExitCode); + } + return buildResult.ExitCode; } - return buildResult.ExitCode; + + return 0; } }; - if (_options.Watch && service.Description.RunInfo is ProjectRunInfo runInfo) + if (_options.Watch && (service.Description.RunInfo is ProjectRunInfo runInfo)) { var projectFile = runInfo.ProjectFile.FullName; var fileSetFactory = new MsBuildFileSetFactory(_logger, @@ -380,6 +385,18 @@ namespace Microsoft.Tye.Hosting 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)) + { + var projectFile = azureFunctionRunInfo.ProjectFile; + var fileSetFactory = new MsBuildFileSetFactory(_logger, + projectFile, + waitOnError: true, + trace: false); + environment["DOTNET_WATCH"] = "1"; + + await new DotNetWatcher(_logger) + .WatchAsync(processInfo, fileSetFactory, replica, status.StoppingTokenSource.Token); + } else { await ProcessUtil.RunAsync(processInfo, status.StoppingTokenSource.Token, throwOnError: false); diff --git a/src/Microsoft.Tye.Hosting/Watch/DotNetWatcher.cs b/src/Microsoft.Tye.Hosting/Watch/DotNetWatcher.cs index f2c97ca7..b3e89dfe 100644 --- a/src/Microsoft.Tye.Hosting/Watch/DotNetWatcher.cs +++ b/src/Microsoft.Tye.Hosting/Watch/DotNetWatcher.cs @@ -100,6 +100,11 @@ namespace Microsoft.DotNet.Watcher { while (true) { + if (cancellationToken.IsCancellationRequested) + { + break; + } + var exitCode = await processSpec.Build(); if (exitCode == 0) {