diff --git a/src/Microsoft.Tye.Core/ApplicationFactory.cs b/src/Microsoft.Tye.Core/ApplicationFactory.cs index 6e7fdc7f2..6280901a0 100644 --- a/src/Microsoft.Tye.Core/ApplicationFactory.cs +++ b/src/Microsoft.Tye.Core/ApplicationFactory.cs @@ -100,6 +100,13 @@ bool IsAzureFunctionService(ConfigService service) } var sw = Stopwatch.StartNew(); + + // This will correctly expand full paths to all service files and projects + EvaluatePaths( + configServices: services, + configRoot: config.Source.DirectoryName!, + output: output); + // Project services will be restored and evaluated before resolving all other services. // This batching will mitigate the performance cost of running MSBuild out of process. var projectServices = services.Where(s => !string.IsNullOrEmpty(s.Project)); @@ -142,6 +149,11 @@ bool IsAzureFunctionService(ConfigService service) { output.WriteDebugLine("Re-evaluating multi-targeted projects"); + EvaluatePaths( + configServices: multiTFMProjects, + configRoot: config.Source.DirectoryName!, + output: output); + var multiTFMEvaluationResult = await EvaluateProjectsAsync( projects: multiTFMProjects, configRoot: config.Source.DirectoryName!, @@ -259,7 +271,7 @@ bool IsAzureFunctionService(ConfigService service) Args = configService.Args, Build = configService.Build ?? true, Replicas = configService.Replicas ?? 1, - DockerFile = Path.Combine(source.DirectoryName!, configService.DockerFile), + DockerFile = Path.Combine(source.DirectoryName!, configService.DockerFileFullPath!), // Supplying an absolute path with trailing slashes fails for DockerFileContext when calling docker build, so trim trailing slash. DockerFileContext = GetDockerFileContext(source, configService), BuildArgs = configService.DockerFileArgs @@ -503,6 +515,27 @@ service is ProjectServiceBuilder project2 && return root; } + private static void EvaluatePaths(IEnumerable configServices, string configRoot, OutputContext output) + { + output.WriteDebugLine("Evaluating configuration paths"); + + foreach (var configService in configServices) + { + if (!string.IsNullOrEmpty(configService.Project)) + { + configService.ProjectFullPath = Path.Combine( + configRoot, + Environment.ExpandEnvironmentVariables(configService.Project!)); + } + if (!string.IsNullOrEmpty(configService.DockerFile)) + { + configService.DockerFileFullPath = Path.Combine( + configRoot, + Environment.ExpandEnvironmentVariables(configService.DockerFile!)); + } + } + } + private static async Task EvaluateProjectsAsync(IEnumerable projects, string configRoot, OutputContext output) { using var directory = TempDirectory.Create(); @@ -514,9 +547,6 @@ private static async Task EvaluateProjectsAsync(IEnumerable DockerFileArgs { get; set; } = new Dictionary(); public string? DockerFileContext { get; set; } public string? Project { get; set; } diff --git a/test/E2ETest/TyeRunTests.cs b/test/E2ETest/TyeRunTests.cs index 0f37c4120..465d23ed0 100644 --- a/test/E2ETest/TyeRunTests.cs +++ b/test/E2ETest/TyeRunTests.cs @@ -1011,6 +1011,44 @@ public async Task MultiRepo_WorksWithCloning() }); } + [ConditionalFact] + [SkipIfDockerNotRunning] + public async Task MultiRepo_WorksWithCloningAndDockerfile() + { + using var projectDirectory = TempDirectory.Create(preferUserDirectoryOnMacOS: true); + + var content = @" +name: tye-docker-sample +services: +- name: minapp + repository: https://github.com/OlegKarasik/tye-docker-sample"; + + var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"); + var projectFile = new FileInfo(yamlFile); + await File.WriteAllTextAsync(yamlFile, content); + + // Debug targets can be null if not specified, so make sure calling host.Start does not throw. + var outputContext = new OutputContext(_sink, Verbosity.Debug); + var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); + + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (a, b, c, d) => true, + AllowAutoRedirect = false + }; + + var client = new HttpClient(new RetryHandler(handler)); + + await RunHostingApplication(application, new HostOptions(), async (app, uri) => + { + var appUri = await GetServiceUrl(client, uri, "minapp"); + + var appResponse = await client.GetAsync(appUri); + + Assert.True(appResponse.IsSuccessStatusCode); + }); + } + [ConditionalFact] [SkipIfDockerNotRunning] public async Task DockerFileTest()