// Copyright Epic Games, Inc. All Rights Reserved. using EpicGames.Core; using EpicGames.ProjectStore; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Xml; namespace AutomationTool.Tasks { /// /// Parameters for a task that exports an snapshot from ZenServer /// public class ZenImportOplogTaskParameters { /// /// The type of destination to import from to (cloud, file...) /// [TaskParameter] public string ImportType; /// /// comma separated full path to the oplog dir to import into the local zen server /// Files="Path1,Path2" /// [TaskParameter(Optional = true)] public string Files; /// /// The project from which to import for /// [TaskParameter(Optional = true)] public FileReference Project; /// /// The name of the newly created Zen Project we will be importing into /// [TaskParameter(Optional = true)] public string ProjectName; /// /// The target platform to import the snapshot for /// [TaskParameter(Optional = true)] public string Platform; /// /// Root dir for the UE project. Used to derive the Enging folder and the Project folder /// [TaskParameter(Optional = true)] public string RootDir; /// /// The name of the imported oplog /// [TaskParameter(Optional = true)] public string OplogName; /// /// The host URL for the zen server we are importing from /// [TaskParameter(Optional = true)] public string HostName = "localhost"; /// /// The host port for the zen server we are importing from /// [TaskParameter(Optional = true)] public string HostPort = "8558"; /// /// The cloud URL to import from /// [TaskParameter(Optional = true)] public string CloudURL; /// /// what namespace to use when importing from cloud /// [TaskParameter(Optional = true)] public string Namespace; /// /// what bucket to use when importing from cloud /// [TaskParameter(Optional = true)] public string Bucket; /// /// What key to use when importing from cloud /// [TaskParameter(Optional = true)] public string Key; } /// /// Imports an oplog from Zen to a specified destination. /// [TaskElement("ZenImportOplog", typeof(ZenImportOplogTaskParameters))] public class ZenImportOplogTask : BgTaskImpl { /// /// Parameters for the task /// ZenImportOplogTaskParameters Parameters; FileReference ProjectFile; /// /// Constructor. /// /// Parameters for this task public ZenImportOplogTask(ZenImportOplogTaskParameters 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 Task ExecuteAsync(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet) { SnapshotStorageType ImportMethod = SnapshotStorageType.Invalid; if (!string.IsNullOrEmpty(Parameters.ImportType)) { ImportMethod = (SnapshotStorageType)Enum.Parse(typeof(SnapshotStorageType), Parameters.ImportType); } ProjectFile = Parameters.Project; if (!FileReference.Exists(ProjectFile)) { throw new AutomationException("Missing project file - {0}", ProjectFile.FullName); } ZenExportSnapshotTask.ZenLaunch(ProjectFile); // Get the Zen executable path FileReference ZenExe = ZenExportSnapshotTask.ZenExeFileReference(); { if (String.IsNullOrEmpty(Parameters.RootDir)) { throw new AutomationException("RootDir was not specified"); } if (String.IsNullOrEmpty(Parameters.ProjectName)) { throw new AutomationException("ProjectName was not specified"); } // Create a new project to import everything into. string RootDir = Parameters.RootDir; string EngineDir = System.IO.Path.Combine(Parameters.RootDir, "Engine"); string ProjectDir = System.IO.Path.Combine(Parameters.RootDir, ProjectFile.GetFileNameWithoutAnyExtensions()); string HostURLArg = string.Format("--hosturl http://{0}:{1}", Parameters.HostName, Parameters.HostPort); StringBuilder OplogProjectCreateCommandline = new StringBuilder(); OplogProjectCreateCommandline.AppendFormat("project-create -p {0} --rootdir {1} --enginedir {2} --projectdir {3} --projectfile {4} {5}", Parameters.ProjectName, RootDir, EngineDir, ProjectDir, ProjectFile.FullName, HostURLArg); Logger.LogInformation("Running '{Arg0} {Arg1}'", CommandUtils.MakePathSafeToUseWithCommandLine(ZenExe.FullName), OplogProjectCreateCommandline.ToString()); CommandUtils.RunAndLog(CommandUtils.CmdEnv, ZenExe.FullName, OplogProjectCreateCommandline.ToString(), Options: CommandUtils.ERunOptions.Default); } switch (ImportMethod) { case SnapshotStorageType.File: ImportFromFile(ZenExe); break; case SnapshotStorageType.Cloud: ImportFromCloud(ZenExe); break; default: throw new AutomationException("Unknown/invalid/unimplemented import type - {0}", Parameters.ImportType); } WriteProjectStoreFile(); return Task.CompletedTask; } private void ImportFromFile(FileReference ZenExe) { if (String.IsNullOrEmpty(Parameters.OplogName)) { throw new AutomationException("OplogName was not specified"); } foreach (string FileToImport in Parameters.Files.Split(',')) { if (DirectoryReference.Exists(new DirectoryReference(FileToImport))) { StringBuilder OplogImportCommandline = new StringBuilder(); OplogImportCommandline.AppendFormat("oplog-import --file {0} --oplog {1} -p {2}", FileToImport, Parameters.OplogName, Parameters.ProjectName); Logger.LogInformation("Running '{Arg0} {Arg1}'", CommandUtils.MakePathSafeToUseWithCommandLine(ZenExe.FullName), OplogImportCommandline.ToString()); CommandUtils.RunAndLog(CommandUtils.CmdEnv, ZenExe.FullName, OplogImportCommandline.ToString(), Options: CommandUtils.ERunOptions.Default); } } } private void WriteProjectStoreFile() { DirectoryReference PlatformCookedDirectory = DirectoryReference.Combine(ProjectFile.Directory, "Saved", "Cooked", Parameters.Platform); if (!DirectoryReference.Exists(PlatformCookedDirectory)) { DirectoryReference.CreateDirectory(PlatformCookedDirectory); } ProjectStoreData ProjectStore = new ProjectStoreData(); ProjectStore.ZenServer = new ZenServerStoreData { ProjectId = Parameters.ProjectName, OplogId = Parameters.OplogName }; JsonSerializerOptions SerializerOptions = new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true }; SerializerOptions.Converters.Add(new JsonStringEnumConverter()); FileReference ProjectStoreFile = FileReference.Combine(PlatformCookedDirectory, "ue.projectstore"); File.WriteAllText(ProjectStoreFile.FullName, JsonSerializer.Serialize(ProjectStore, SerializerOptions), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } private void ImportFromCloud(FileReference ZenExe) { if (string.IsNullOrEmpty(Parameters.CloudURL)) { throw new AutomationException("Missing destination cloud host"); } if (string.IsNullOrEmpty(Parameters.Namespace)) { throw new AutomationException("Missing destination cloud namespace"); } if (string.IsNullOrEmpty(Parameters.Key)) { throw new AutomationException("Missing destination cloud storage key"); } string BucketName = Parameters.Bucket; string ProjectNameAsBucketName = ProjectFile.GetFileNameWithoutAnyExtensions().ToLowerInvariant(); if (string.IsNullOrEmpty(BucketName)) { BucketName = ProjectNameAsBucketName; } string HostURLArg = string.Format("--hosturl http://{0}:{1}", Parameters.HostName, Parameters.HostPort); StringBuilder OplogImportCommandline = new StringBuilder(); OplogImportCommandline.AppendFormat("oplog-import {0} --cloud {1} --namespace {2} --bucket {3}", HostURLArg, Parameters.CloudURL, Parameters.Namespace, BucketName); OplogImportCommandline.AppendFormat(" {0}", Parameters.Key); Logger.LogInformation("Running '{Arg0} {Arg1}'", CommandUtils.MakePathSafeToUseWithCommandLine(ZenExe.FullName), OplogImportCommandline.ToString()); CommandUtils.RunAndLog(CommandUtils.CmdEnv, ZenExe.FullName, OplogImportCommandline.ToString(), Options: CommandUtils.ERunOptions.Default); } /// /// 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() { yield break; } /// /// 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; } } }