diff --git a/PowerForge.Tests/HomeAssistantReleaseTests.cs b/PowerForge.Tests/HomeAssistantReleaseTests.cs index ce437e67a..1f850b83f 100644 --- a/PowerForge.Tests/HomeAssistantReleaseTests.cs +++ b/PowerForge.Tests/HomeAssistantReleaseTests.cs @@ -786,7 +786,10 @@ internal RecordingProcessRunner(bool mutateTrackedFile) { public Task RunAsync(ProcessRunRequest request, CancellationToken cancellationToken = default) { Requests.Add(request); - if (request.Arguments.SequenceEqual(new[] { "run", "pack" })) { + var command = request.Arguments; + if (command.Take(3).SequenceEqual(new[] { "/d", "/c", "npm" })) + command = command.Skip(3).ToArray(); + if (command.SequenceEqual(new[] { "run", "pack" })) { var release = Path.Combine(request.WorkingDirectory, "release"); Directory.CreateDirectory(release); File.WriteAllText(Path.Combine(release, "example.js"), "export const value = 1;\n"); diff --git a/PowerForge/Services/HomeAssistantRepositoryService.cs b/PowerForge/Services/HomeAssistantRepositoryService.cs index d19c5cefb..6cb93d021 100644 --- a/PowerForge/Services/HomeAssistantRepositoryService.cs +++ b/PowerForge/Services/HomeAssistantRepositoryService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; @@ -127,10 +128,10 @@ internal IReadOnlyList BuildAssets(HomeAssistantRepositorySnapshot snaps _ = HomeAssistantSemanticVersion.Parse(version); var root = ResolveRoot(repositoryRoot); if (snapshot.Kind == HomeAssistantRepositoryKind.LovelacePlugin) { - Run(root, "npm", "ci"); - Run(root, "npm", "test"); - Run(root, "npm", "run", "check"); - Run(root, "npm", "run", "pack"); + RunNpm(root, "ci"); + RunNpm(root, "test"); + RunNpm(root, "run", "check"); + RunNpm(root, "run", "pack"); var primaryAsset = Path.Combine(root, "release", snapshot.HacsFileName!); if (!File.Exists(primaryAsset)) @@ -151,7 +152,16 @@ internal IReadOnlyList BuildAssets(HomeAssistantRepositorySnapshot snaps return Array.Empty(); } - private void Run(string workingDirectory, string executable, params string[] arguments) { + private void RunNpm(string workingDirectory, params string[] arguments) { + var executable = "npm"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + // Windows ships npm as a command shim. Direct process creation only searches + // for executables; cmd resolves npm.cmd (or a package manager's npm.exe). + // These operands are the fixed build-stage commands above, never user input. + executable = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); + arguments = new[] { "/d", "/c", "npm" }.Concat(arguments).ToArray(); + } + var environment = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = null, ["ACTIONS_ID_TOKEN_REQUEST_URL"] = null,