// 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;
}
}
}