diff --git a/src/Tye.Core/DockerContainerBuilder.cs b/src/Tye.Core/DockerContainerBuilder.cs index 692cde0e..fe027a26 100644 --- a/src/Tye.Core/DockerContainerBuilder.cs +++ b/src/Tye.Core/DockerContainerBuilder.cs @@ -5,6 +5,7 @@ using System; using System.CommandLine.Invocation; using System.IO; +using System.Linq; using System.Threading.Tasks; namespace Tye @@ -51,12 +52,31 @@ namespace Tye dockerFilePath = tempFile.FilePath; } + // We need to know if this is a single-phase or multi-phase dockerfile because the context directory will be + // different depending on that choice. + string contextDirectory; + if (container.UseMultiphaseDockerfile ?? true) + { + contextDirectory = "."; + } + else + { + var publishOutput = service.Outputs.OfType().FirstOrDefault(); + if (publishOutput is null) + { + throw new InvalidOperationException("We should have published the project for a single-phase dockerfile."); + } + + contextDirectory = publishOutput.Directory.FullName; + } + + output.WriteDebugLine("Running 'docker build'."); - output.WriteCommandLine("docker", $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\""); + output.WriteCommandLine("docker", $"build \"{contextDirectory}\" -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"docker", - $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"", + $"build \"{contextDirectory}\" -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"", application.GetProjectDirectory(project), stdOut: capture.StdOut, stdErr: capture.StdErr); diff --git a/src/Tye.Core/ProjectPublishOutput.cs b/src/Tye.Core/ProjectPublishOutput.cs new file mode 100644 index 00000000..8b3e9aa6 --- /dev/null +++ b/src/Tye.Core/ProjectPublishOutput.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; + +namespace Tye +{ + public class ProjectPublishOutput : ServiceOutput + { + public ProjectPublishOutput(DirectoryInfo directory) + { + Directory = directory; + } + + public DirectoryInfo Directory { get; } + } +} diff --git a/src/Tye.Core/PublishProjectStep.cs b/src/Tye.Core/PublishProjectStep.cs new file mode 100644 index 00000000..a4571404 --- /dev/null +++ b/src/Tye.Core/PublishProjectStep.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.CommandLine.Invocation; +using System.IO; +using System.Threading.Tasks; + +namespace Tye +{ + // Used to publish a project when using a single-file dockerfile + public class PublishProjectStep : ServiceExecutor.Step + { + public override string DisplayText => "Publishing Project..."; + + public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service) + { + if (SkipWithoutProject(output, service, out var project)) + { + return; + } + + if (SkipWithoutContainerInfo(output, service, out var container)) + { + return; + } + + if (container.UseMultiphaseDockerfile != false) + { + return; + } + + var projectFilePath = Path.Combine(application.RootDirectory, project.RelativeFilePath); + var outputDirectory = Path.Combine(Path.GetDirectoryName(projectFilePath), "bin", "Release", project.TargetFramework, "publish"); + + output.WriteDebugLine("Running 'dotnet publish'."); + output.WriteCommandLine("dotnet", $"publish \"{projectFilePath}\" -c Release -o \"{outputDirectory}\""); + var capture = output.Capture(); + var exitCode = await Process.ExecuteAsync( + $"dotnet", + $"publish \"{projectFilePath}\" -c Release -o \"{outputDirectory}\"", + application.GetProjectDirectory(project), + stdOut: capture.StdOut, + stdErr: capture.StdErr); + + output.WriteDebugLine($"Done running 'dotnet publish' exit code: {exitCode}"); + if (exitCode != 0) + { + throw new CommandException("'dotnet publish' failed."); + } + + output.WriteInfoLine($"Created Publish Output: '{outputDirectory}'"); + service.Outputs.Add(new ProjectPublishOutput(new DirectoryInfo(outputDirectory))); + } + } +} diff --git a/src/tye/Program.DeployCommand.cs b/src/tye/Program.DeployCommand.cs index c75c13e4..6b3a5761 100644 --- a/src/tye/Program.DeployCommand.cs +++ b/src/tye/Program.DeployCommand.cs @@ -44,6 +44,7 @@ namespace Tye var steps = new List() { new CombineStep() { Environment = environment, }, + new PublishProjectStep(), new BuildDockerImageStep() { Environment = environment, }, new PushDockerImageStep() { Environment = environment, }, }; @@ -100,7 +101,7 @@ namespace Tye var container = new ContainerInfo() { // Single-phase workflow doesn't currently work. - UseMultiphaseDockerfile = true, + UseMultiphaseDockerfile = false, }; service.GeneratedAssets.Container = container; services.Add(serviceEntry); diff --git a/src/tye/Program.GenerateCommand.cs b/src/tye/Program.GenerateCommand.cs index 25a993f4..97f7135f 100644 --- a/src/tye/Program.GenerateCommand.cs +++ b/src/tye/Program.GenerateCommand.cs @@ -48,6 +48,7 @@ namespace Tye var steps = new List() { new CombineStep() { Environment = environment, }, + new PublishProjectStep(), new BuildDockerImageStep() { Environment = environment, }, // Make an image but don't push it };