// Copyright Epic Games, Inc. All Rights Reserved. using EpicGames.Core; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; using AutomationTool; using UnrealBuildBase; using System.Threading.Tasks; namespace AutomationTool.Tasks { /// /// Parameters for a Docker-Build task /// public class DockerBuildTaskParameters { /// /// Base directory for the build /// [TaskParameter] public string BaseDir; /// /// Files to be staged before building the image /// [TaskParameter] public string Files; /// /// Path to the Dockerfile. Uses the root of basedir if not specified. /// [TaskParameter(Optional = true)] public string DockerFile; /// /// Tag for the image /// [TaskParameter(Optional = true)] public string Tag; /// /// Optional arguments /// [TaskParameter(Optional = true)] public string Arguments; /// /// List of additional directories to overlay into the staged input files. Allows credentials to be staged, etc... /// [TaskParameter(Optional = true)] public string OverlayDirs; /// /// Environment variables to set /// [TaskParameter(Optional = true)] public string Environment; /// /// File to read environment variables from /// [TaskParameter(Optional = true)] public string EnvironmentFile; } /// /// Spawns Docker and waits for it to complete. /// [TaskElement("Docker-Build", typeof(DockerBuildTaskParameters))] public class DockerBuildTask : SpawnTaskBase { /// /// Parameters for this task /// DockerBuildTaskParameters Parameters; /// /// Construct a Docker task /// /// Parameters for the task public DockerBuildTask(DockerBuildTaskParameters InParameters) { Parameters = InParameters; } /// /// Execute the task. /// /// Information about the current job /// Set of build products produced by this node. /// Mapping from tag names to the set of files they include public override async Task ExecuteAsync(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet) { Log.TraceInformation("Building Docker image"); using (LogIndentScope Scope = new LogIndentScope(" ")) { DirectoryReference BaseDir = ResolveDirectory(Parameters.BaseDir); List SourceFiles = ResolveFilespec(BaseDir, Parameters.Files, TagNameToFileSet).ToList(); DirectoryReference StagingDir = DirectoryReference.Combine(Unreal.EngineDirectory, "Intermediate", "Docker"); FileUtils.ForceDeleteDirectoryContents(StagingDir); List TargetFiles = SourceFiles.ConvertAll(x => FileReference.Combine(StagingDir, x.MakeRelativeTo(BaseDir))); CommandUtils.ThreadedCopyFiles(SourceFiles, BaseDir, StagingDir); if (!String.IsNullOrEmpty(Parameters.OverlayDirs)) { foreach (string OverlayDir in Parameters.OverlayDirs.Split(';')) { CommandUtils.ThreadedCopyFiles(ResolveDirectory(OverlayDir), StagingDir); } } StringBuilder Arguments = new StringBuilder("build ."); if (Parameters.Tag != null) { Arguments.Append($" -t {Parameters.Tag}"); } if (Parameters.DockerFile != null) { FileReference DockerFile = ResolveFile(Parameters.DockerFile); if (!DockerFile.IsUnderDirectory(BaseDir)) { throw new AutomationException($"Dockerfile '{DockerFile}' is not under base directory ({BaseDir})"); } Arguments.Append($" -f {DockerFile.MakeRelativeTo(BaseDir).QuoteArgument()}"); } if (Parameters.Arguments != null) { Arguments.Append($" {Parameters.Arguments}"); } await SpawnTaskBase.ExecuteAsync("docker", Arguments.ToString(), EnvVars: ParseEnvVars(Parameters.Environment, Parameters.EnvironmentFile), WorkingDir: StagingDir.FullName); } } /// /// Output this task out to an XML writer. /// public override void Write(XmlWriter Writer) { Write(Writer, Parameters); } /// /// Find all the tags which are used as inputs to this task /// /// The tag names which are read by this task public override IEnumerable FindConsumedTagNames() { List TagNames = new List(); TagNames.AddRange(FindTagNamesFromFilespec(Parameters.DockerFile)); TagNames.AddRange(FindTagNamesFromFilespec(Parameters.Files)); return TagNames; } /// /// Find all the tags which are modified by this task /// /// The tag names which are modified by this task public override IEnumerable FindProducedTagNames() { yield break; } } }