| File: GenerateE2EManifest.cs | Web Access |
| Project: src\aspnetcore\src\Components\Testing\tasks\Microsoft.AspNetCore.Components.Testing.Tasks.csproj (Microsoft.AspNetCore.Components.Testing.Tasks) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Serialization.Json; using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; namespace Microsoft.AspNetCore.Components.Testing.Tasks; /// <summary> /// MSBuild task that generates a JSON manifest describing all E2E app projects /// under test. The manifest format depends on the <see cref="E2EAppMode"/>: /// <list type="bullet"> /// <item><c>build</c> (default) — one entry per app using <c>dotnet run</c>.</item> /// <item><c>publish</c> — one entry per app using the published executable.</item> /// <item><c>all</c> — two entries per app: source (<c><name></c>) and published (<c>publish/<name></c>).</item> /// </list> /// </summary> public class GenerateE2EManifest : Task { [Required] public ITaskItem[] AppItems { get; set; } [Required] public string ManifestPath { get; set; } [Required] public string E2EAppsOutputDir { get; set; } /// <summary> /// Relative directory name for E2E apps (e.g. "e2e-apps"). /// Used in manifest paths — never hardcoded. /// </summary> [Required] public string E2EAppsRelativeDir { get; set; } /// <summary> /// The E2E app mode: build, publish, or all. /// </summary> [Required] public string E2EAppMode { get; set; } /// <summary> /// Whether the target is running in a Publish context. /// When true, working directories are relative to AppContext.BaseDirectory. /// When false, source-mode paths are absolute (local dev). /// </summary> [Required] public string IsPublishing { get; set; } public override bool Execute() { var mode = E2EAppMode ?? "build"; var isPublishing = IsPublishing.Equals("true", StringComparison.OrdinalIgnoreCase); var includeBuild = mode.Equals("build", StringComparison.OrdinalIgnoreCase) || mode.Equals("all", StringComparison.OrdinalIgnoreCase); var includePublish = mode.Equals("publish", StringComparison.OrdinalIgnoreCase) || mode.Equals("all", StringComparison.OrdinalIgnoreCase); var isBothMode = mode.Equals("all", StringComparison.OrdinalIgnoreCase); var manifest = new E2EManifestModel(); foreach (var item in AppItems) { var name = item.GetMetadata("Filename"); var projectPath = item.GetMetadata("FullPath"); var publicUrl = item.GetMetadata("E2EPublicUrl") ?? ""; if (includeBuild) { var entry = new E2EAppEntryModel { Executable = "dotnet", Arguments = "run --no-launch-profile", PublicUrl = publicUrl, }; if (isPublishing) { // Source was copied to <relDir>/<name>/ — use relative path. entry.WorkingDirectory = Path.Combine(E2EAppsRelativeDir, name); } else { // Local dev — point at the real project source directory. entry.WorkingDirectory = Path.GetDirectoryName(projectPath) ?? ""; } manifest.Apps[name] = entry; } if (includePublish) { // In 'all' mode, published output lives under publish/<name> within the apps dir. var subPath = isBothMode ? Path.Combine("publish", name) : name; var relativeDir = Path.Combine(E2EAppsRelativeDir, subPath); var absoluteDir = Path.Combine(E2EAppsOutputDir, subPath); var exeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""; var appHostPath = Path.Combine(absoluteDir, name + exeSuffix); var appDllPath = Path.Combine(absoluteDir, name + ".dll"); var publishEntry = new E2EAppEntryModel { PublicUrl = publicUrl, WorkingDirectory = relativeDir, }; if (File.Exists(appHostPath)) { publishEntry.Executable = name + exeSuffix; publishEntry.Arguments = ""; } else if (File.Exists(appDllPath)) { publishEntry.Executable = "dotnet"; publishEntry.Arguments = name + ".dll"; } else { Log.LogError("Could not find published app at '{0}'. Expected '{1}' or '{2}'.", absoluteDir, appHostPath, appDllPath); return false; } // In 'all' mode, published entry uses "publish/<name>" key; otherwise it's the primary entry. var publishKey = isBothMode ? "publish/" + name : name; manifest.Apps[publishKey] = publishEntry; } } var manifestDir = Path.GetDirectoryName(ManifestPath); if (!string.IsNullOrEmpty(manifestDir)) { Directory.CreateDirectory(manifestDir); } using var fileStream = File.Create(ManifestPath); var serializer = new DataContractJsonSerializer(typeof(E2EManifestModel), new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true, }); using var writer = JsonReaderWriterFactory.CreateJsonWriter(fileStream, Encoding.UTF8, ownsStream: false, indent: true); serializer.WriteObject(writer, manifest); Log.LogMessage(MessageImportance.High, "Generated E2E manifest: {0}", ManifestPath); return true; } }