diff --git a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodeJSHostingExtensions.cs b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodeJSHostingExtensions.cs index 9ee02c52..2908b106 100644 --- a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodeJSHostingExtensions.cs +++ b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodeJSHostingExtensions.cs @@ -3,6 +3,7 @@ using Aspire.Hosting.Utils; using Aspire.CommunityToolkit.Hosting.NodeJS.Extensions; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; namespace Aspire.Hosting; @@ -96,10 +97,12 @@ public static IResourceBuilder AddPnpmApp(this IDistributedAppl /// Ensures the Node.js packages are installed before the application starts using npm as the package manager. /// /// The Node.js app resource. + /// When true use npm ci otherwise use npm install when installing packages. /// A reference to the . - public static IResourceBuilder WithNpmPackageInstallation(this IResourceBuilder resource) + public static IResourceBuilder WithNpmPackageInstallation(this IResourceBuilder resource, bool useCI = false) { - resource.ApplicationBuilder.Services.TryAddLifecycleHook(); + resource.ApplicationBuilder.Services.TryAddLifecycleHook(sp => + new(useCI, sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService())); return resource; } diff --git a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodePackageInstaller.cs b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodePackageInstaller.cs index c674dfaa..666276da 100644 --- a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodePackageInstaller.cs +++ b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NodePackageInstaller.cs @@ -12,8 +12,10 @@ namespace Aspire.CommunityToolkit.Hosting.NodeJS.Extensions; /// The package manager to use. /// The logger service to use. /// The notification service to use. -internal class NodePackageInstaller(string packageManager, ResourceLoggerService loggerService, ResourceNotificationService notificationService) +internal class NodePackageInstaller(string packageManager, string installCommand, string lockfile, ResourceLoggerService loggerService, ResourceNotificationService notificationService) { + private readonly bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + /// /// Finds the Node.js resources using the specified package manager and installs the packages. /// @@ -42,16 +44,16 @@ private async Task PerformInstall(NodeAppResource resource, CancellationToken ca { var logger = loggerService.GetLogger(resource); - var packageJsonPath = Path.Combine(resource.WorkingDirectory, "package.json"); + var packageJsonPath = Path.Combine(resource.WorkingDirectory, lockfile); if (!File.Exists(packageJsonPath)) { await notificationService.PublishUpdateAsync(resource, state => state with { - State = new($"No package.json file found in {resource.WorkingDirectory}", KnownResourceStates.FailedToStart) + State = new($"No {lockfile} file found in {resource.WorkingDirectory}", KnownResourceStates.FailedToStart) }).ConfigureAwait(false); - throw new InvalidOperationException($"No package.json file found in {resource.WorkingDirectory}"); + throw new InvalidOperationException($"No {lockfile} file found in {resource.WorkingDirectory}"); } await notificationService.PublishUpdateAsync(resource, state => state with @@ -61,25 +63,12 @@ await notificationService.PublishUpdateAsync(resource, state => state with logger.LogInformation("Installing {PackageManager} packages in {WorkingDirectory}", packageManager, resource.WorkingDirectory); - var packageInstaller = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new Process - { - StartInfo = new ProcessStartInfo - { - FileName = packageManager, - Arguments = "install", - WorkingDirectory = resource.WorkingDirectory, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - UseShellExecute = false, - } - } - : new Process + var packageInstaller = new Process { StartInfo = new ProcessStartInfo { - FileName = "cmd", - Arguments = $"/c {packageManager} install", + FileName = isWindows ? "cmd" : packageManager, + Arguments = isWindows ? $"/c {packageManager} {installCommand}" : installCommand, WorkingDirectory = resource.WorkingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, @@ -118,7 +107,7 @@ await notificationService.PublishUpdateAsync(resource, state => state with packageInstaller.BeginOutputReadLine(); packageInstaller.BeginErrorReadLine(); - packageInstaller.WaitForExit(); + await packageInstaller.WaitForExitAsync(cancellationToken).ConfigureAwait(false); if (packageInstaller.ExitCode != 0) { diff --git a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NpmPackageInstallerLifecycleHook.cs b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NpmPackageInstallerLifecycleHook.cs index dde47472..7163e175 100644 --- a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NpmPackageInstallerLifecycleHook.cs +++ b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/NpmPackageInstallerLifecycleHook.cs @@ -13,9 +13,13 @@ namespace Aspire.CommunityToolkit.Hosting.NodeJS.Extensions; /// The logger service used for logging. /// The notification service used for sending notifications. /// The execution context of the distributed application. -internal class NpmPackageInstallerLifecycleHook(ResourceLoggerService loggerService, ResourceNotificationService notificationService, DistributedApplicationExecutionContext context) : IDistributedApplicationLifecycleHook +internal class NpmPackageInstallerLifecycleHook( + bool useCI, + ResourceLoggerService loggerService, + ResourceNotificationService notificationService, + DistributedApplicationExecutionContext context) : IDistributedApplicationLifecycleHook { - private readonly NodePackageInstaller _installer = new("npm", loggerService, notificationService); + private readonly NodePackageInstaller _installer = new("npm", useCI ? "ci" : "install", "package-lock.json", loggerService, notificationService); /// public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default) diff --git a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PnpmPackageInstallerLifecycleHook.cs b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PnpmPackageInstallerLifecycleHook.cs index 05d13956..1a1d666f 100644 --- a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PnpmPackageInstallerLifecycleHook.cs +++ b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PnpmPackageInstallerLifecycleHook.cs @@ -15,7 +15,7 @@ internal class PnpmPackageInstallerLifecycleHook( ResourceNotificationService notificationService, DistributedApplicationExecutionContext context) : IDistributedApplicationLifecycleHook { - private readonly NodePackageInstaller _installer = new("pnpm", loggerService, notificationService); + private readonly NodePackageInstaller _installer = new("pnpm", "install", "pnpm-lock.yaml", loggerService, notificationService); /// public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default) diff --git a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PublicAPI.Unshipped.txt b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PublicAPI.Unshipped.txt index 45a54671..8e0e09ee 100644 --- a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PublicAPI.Unshipped.txt +++ b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/PublicAPI.Unshipped.txt @@ -2,6 +2,6 @@ static Aspire.Hosting.NodeJSHostingExtensions.AddPnpmApp(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! workingDirectory, string! scriptName = "start", string![]? args = null) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.NodeJSHostingExtensions.AddViteApp(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string? workingDirectory = null, string! packageManager = "npm") -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.NodeJSHostingExtensions.AddYarnApp(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! workingDirectory, string! scriptName = "start", string![]? args = null) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! -static Aspire.Hosting.NodeJSHostingExtensions.WithNpmPackageInstallation(this Aspire.Hosting.ApplicationModel.IResourceBuilder! resource) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! +static Aspire.Hosting.NodeJSHostingExtensions.WithNpmPackageInstallation(this Aspire.Hosting.ApplicationModel.IResourceBuilder! resource, bool useCI = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.NodeJSHostingExtensions.WithPnpmPackageInstallation(this Aspire.Hosting.ApplicationModel.IResourceBuilder! resource) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.NodeJSHostingExtensions.WithYarnPackageInstallation(this Aspire.Hosting.ApplicationModel.IResourceBuilder! resource) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! \ No newline at end of file diff --git a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/YarnPackageInstallerLifecycleHook.cs b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/YarnPackageInstallerLifecycleHook.cs index 5573fdfe..1166c846 100644 --- a/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/YarnPackageInstallerLifecycleHook.cs +++ b/src/Aspire.CommunityToolkit.Hosting.NodeJS.Extensions/YarnPackageInstallerLifecycleHook.cs @@ -15,7 +15,7 @@ internal class YarnPackageInstallerLifecycleHook( ResourceNotificationService notificationService, DistributedApplicationExecutionContext context) : IDistributedApplicationLifecycleHook { - private readonly NodePackageInstaller _installer = new("yarn", loggerService, notificationService); + private readonly NodePackageInstaller _installer = new("yarn", "install", "yarn.lock", loggerService, notificationService); /// public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)