// Copyright Epic Games, Inc. All Rights Reserved. using AutomationTool; using EpicGames.Core; using System; using System.Collections.Generic; using System.Text.Json; using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using System.Xml; using UnrealBuildBase; using Microsoft.Extensions.Logging; using EpicGames.Horde.Storage.Bundles; using EpicGames.Horde.Storage.Clients; using EpicGames.Horde.Storage; using EpicGames.Horde.Storage.Nodes; using System.Threading; using System.Data; using EpicGames.Horde.Storage.Backends; using Microsoft.Extensions.DependencyInjection; using EpicGames.Horde; using EpicGames.Horde.Artifacts; using System.Linq; using System.Diagnostics; #nullable enable namespace AutomationTool.Tasks { /// /// Parameters for a CreateArtifact task /// public class CreateArtifactTaskParameters { /// /// Name for the artifact /// [TaskParameter] public string Name = String.Empty; /// /// Type of the artifact /// [TaskParameter(Optional = true)] public string Type = "unknown"; /// /// Description for the artifact /// [TaskParameter(Optional = true)] public string? Description; /// /// Base directory to resolve relative paths for input files. /// [TaskParameter] public string? BaseDir; /// /// Changelist number for this artifact /// [TaskParameter(Optional = true)] public int? Change; /// /// Files to be uploaded. /// [TaskParameter(Optional = true, ValidationType = TaskParameterValidationType.FileSpec)] public string Files = "..."; } /// /// Deploys a tool update through Horde /// [TaskElement("CreateArtifact", typeof(CreateArtifactTaskParameters))] public class CreateArtifactTask : SpawnTaskBase { class LoggerProviderAdapter : ILoggerProvider { readonly ILogger _logger; public LoggerProviderAdapter(ILogger logger) => _logger = logger; public ILogger CreateLogger(string categoryName) => _logger; public void Dispose() { } } /// /// Parameters for this task /// CreateArtifactTaskParameters Parameters; /// /// Construct a Helm task /// /// Parameters for the task public CreateArtifactTask(CreateArtifactTaskParameters 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) { // Create a DI container for building the graph ServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddHorde(); serviceCollection.AddLogging(builder => builder.AddProvider(new LoggerProviderAdapter(Log.Logger))); serviceCollection.Configure(x => x.AllowAuthPrompt = !Automation.IsBuildMachine); serviceCollection.Configure(options => options.AddFilter(typeof(HttpClient).FullName, LogLevel.Warning)); await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); ArtifactName artifactName = new ArtifactName(Parameters.Name); ArtifactType artifactType = new ArtifactType(Parameters.Type); HordeHttpClient hordeHttpClient = serviceProvider.GetRequiredService(); int? change = (Parameters.Change == 0) ? (int?)null : Parameters.Change; CreateArtifactResponse response = await hordeHttpClient.CreateArtifactAsync(artifactName, artifactType, Parameters.Description, change: change); Logger.LogInformation("Creating artifact {ArtifactId} '{ArtifactName}' ({ArtifactType}) (ns: {NamespaceId}, ref: {RefName})", response.ArtifactId, artifactName, artifactType, response.NamespaceId, response.RefName); Stopwatch timer = Stopwatch.StartNew(); HttpStorageClientFactory httpStorageClientFactory = serviceProvider.GetRequiredService(); using (IStorageClient client = httpStorageClientFactory.CreateClient(response.NamespaceId, response.Token)) { await using (IBlobWriter writer = client.CreateBlobWriter(response.RefName)) { DirectoryReference baseDir = ResolveDirectory(Parameters.BaseDir); List files = ResolveFilespec(baseDir, Parameters.Files, TagNameToFileSet).Select(x => x.ToFileInfo()).ToList(); int totalCount = files.Count; long totalSize = files.Sum(x => x.Length); IBlobRef outputNodeRef = await writer.WriteFilesAsync(baseDir.ToDirectoryInfo(), files, progress: new UpdateStatsLogger(totalCount, totalSize, Logger)); await writer.FlushAsync(); await client.WriteRefAsync(response.RefName, outputNodeRef); } } Logger.LogInformation("Completed in {Time:n1}s", timer.Elapsed.TotalSeconds); } /// /// 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() { return FindTagNamesFromList(Parameters.Files); } /// /// 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; } } }