Skip to content

Commit 03e9e54

Browse files
authored
Merge pull request #468 from ionite34/backport/main/pr-465_pr-465_pr-465_pr-465_pr-465_pr-465_pr-465_pr-465_pr-465_pr-465_pr-465
[dev to main] backport: install node/pnpm and build invoke frontend during install (465)
2 parents 0b88add + c548ea4 commit 03e9e54

File tree

7 files changed

+297
-12
lines changed

7 files changed

+297
-12
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to Stability Matrix will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
77

8+
## v2.7.9
9+
### Fixed
10+
- Fixed InvokeAI v3.6.0 `"detail": "Not Found"` error when opening the UI
11+
812
## v2.7.8
913
### Changed
1014
- Python Packages install dialog now allows entering multiple arguments or option flags

StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs

+81-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Runtime.Versioning;
55
using System.Threading.Tasks;
66
using Avalonia.Controls;
7+
using DynamicData;
78
using FluentAvalonia.UI.Controls;
89
using NLog;
910
using StabilityMatrix.Avalonia.Languages;
@@ -32,10 +33,13 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper
3233

3334
private DirectoryPath PythonDir => AssetsDir.JoinDir("Python310");
3435
public bool IsPythonInstalled => PythonDir.JoinFile(PyRunner.RelativePythonDllPath).Exists;
35-
3636
private DirectoryPath PortableGitInstallDir => HomeDir + "PortableGit";
3737
public string GitBinPath => PortableGitInstallDir + "bin";
3838

39+
private DirectoryPath NodeDir => AssetsDir.JoinDir("nodejs");
40+
private string NpmPath => Path.Combine(NodeDir, "bin", "npm");
41+
private bool IsNodeInstalled => File.Exists(NpmPath);
42+
3943
// Cached store of whether or not git is installed
4044
private bool? isGitInstalled;
4145

@@ -240,6 +244,82 @@ public Task<string> GetGitOutput(string? workingDirectory = null, params string[
240244
throw new NotImplementedException();
241245
}
242246

247+
[SupportedOSPlatform("Linux")]
248+
[SupportedOSPlatform("macOS")]
249+
public async Task RunNpm(
250+
ProcessArgs args,
251+
string? workingDirectory = null,
252+
Action<ProcessOutput>? onProcessOutput = null
253+
)
254+
{
255+
var command = args.Prepend([NpmPath]);
256+
257+
var result = await ProcessRunner.RunBashCommand(command.ToArray(), workingDirectory ?? "");
258+
if (result.ExitCode != 0)
259+
{
260+
Logger.Error(
261+
"npm command [{Command}] failed with exit code " + "{ExitCode}:\n{StdOut}\n{StdErr}",
262+
command,
263+
result.ExitCode,
264+
result.StandardOutput,
265+
result.StandardError
266+
);
267+
268+
throw new ProcessException(
269+
$"npm command [{command}] failed with exit code"
270+
+ $" {result.ExitCode}:\n{result.StandardOutput}\n{result.StandardError}"
271+
);
272+
}
273+
274+
onProcessOutput?.Invoke(ProcessOutput.FromStdOutLine(result.StandardOutput));
275+
onProcessOutput?.Invoke(ProcessOutput.FromStdErrLine(result.StandardError));
276+
}
277+
278+
[SupportedOSPlatform("Linux")]
279+
[SupportedOSPlatform("macOS")]
280+
public async Task InstallNodeIfNecessary(IProgress<ProgressReport>? progress = null)
281+
{
282+
if (IsNodeInstalled)
283+
{
284+
Logger.Info("node already installed");
285+
return;
286+
}
287+
288+
Logger.Info("Downloading node");
289+
290+
var downloadUrl = Compat.IsMacOS
291+
? "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-arm64.tar.gz"
292+
: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz";
293+
294+
var nodeDownloadPath = AssetsDir.JoinFile(Path.GetFileName(downloadUrl));
295+
296+
await downloadService.DownloadToFileAsync(downloadUrl, nodeDownloadPath, progress: progress);
297+
298+
Logger.Info("Installing node");
299+
progress?.Report(
300+
new ProgressReport(
301+
progress: 0.5f,
302+
isIndeterminate: true,
303+
type: ProgressType.Generic,
304+
message: "Installing prerequisites..."
305+
)
306+
);
307+
308+
// unzip
309+
await ArchiveHelper.Extract7ZAuto(nodeDownloadPath, AssetsDir);
310+
311+
var nodeDir = Compat.IsMacOS
312+
? AssetsDir.JoinDir("node-v20.11.0-darwin-arm64")
313+
: AssetsDir.JoinDir("node-v20.11.0-linux-x64");
314+
Directory.Move(nodeDir, NodeDir);
315+
316+
progress?.Report(
317+
new ProgressReport(progress: 1f, message: "Node install complete", type: ProgressType.Generic)
318+
);
319+
320+
File.Delete(nodeDownloadPath);
321+
}
322+
243323
[UnsupportedOSPlatform("Linux")]
244324
[UnsupportedOSPlatform("macOS")]
245325
public Task InstallTkinterIfNecessary(IProgress<ProgressReport>? progress = null)

StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs

+56-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper
2626
private const string VcRedistDownloadUrl = "https://aka.ms/vs/16/release/vc_redist.x64.exe";
2727
private const string TkinterDownloadUrl =
2828
"https://cdn.lykos.ai/tkinter-cpython-embedded-3.10.11-win-x64.zip";
29+
private const string NodeDownloadUrl = "https://nodejs.org/dist/v20.11.0/node-v20.11.0-win-x64.zip";
2930

3031
private string HomeDir => settingsManager.LibraryDir;
3132

@@ -49,8 +50,10 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper
4950
private string TkinterZipPath => Path.Combine(AssetsDir, "tkinter.zip");
5051
private string TkinterExtractPath => PythonDir;
5152
private string TkinterExistsPath => Path.Combine(PythonDir, "tkinter");
52-
public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin");
53+
private string NodeExistsPath => Path.Combine(AssetsDir, "nodejs", "npm.cmd");
54+
private string NodeDownloadPath => Path.Combine(AssetsDir, "nodejs.zip");
5355

56+
public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin");
5457
public bool IsPythonInstalled => File.Exists(PythonDllPath);
5558

5659
public WindowsPrerequisiteHelper(
@@ -111,12 +114,28 @@ public async Task<string> GetGitOutput(string? workingDirectory = null, params s
111114
return process;
112115
}
113116

117+
public async Task RunNpm(
118+
ProcessArgs args,
119+
string? workingDirectory = null,
120+
Action<ProcessOutput>? onProcessOutput = null
121+
)
122+
{
123+
var result = await ProcessRunner
124+
.GetProcessResultAsync(NodeExistsPath, args, workingDirectory)
125+
.ConfigureAwait(false);
126+
127+
result.EnsureSuccessExitCode();
128+
onProcessOutput?.Invoke(ProcessOutput.FromStdOutLine(result.StandardOutput));
129+
onProcessOutput?.Invoke(ProcessOutput.FromStdErrLine(result.StandardError));
130+
}
131+
114132
public async Task InstallAllIfNecessary(IProgress<ProgressReport>? progress = null)
115133
{
116134
await InstallVcRedistIfNecessary(progress);
117135
await UnpackResourcesIfNecessary(progress);
118136
await InstallPythonIfNecessary(progress);
119137
await InstallGitIfNecessary(progress);
138+
await InstallNodeIfNecessary(progress);
120139
}
121140

122141
public async Task UnpackResourcesIfNecessary(IProgress<ProgressReport>? progress = null)
@@ -372,6 +391,42 @@ await downloadService.DownloadToFileAsync(
372391
File.Delete(VcRedistDownloadPath);
373392
}
374393

394+
[SupportedOSPlatform("windows")]
395+
public async Task InstallNodeIfNecessary(IProgress<ProgressReport>? progress = null)
396+
{
397+
if (File.Exists(NodeExistsPath))
398+
{
399+
Logger.Info("node already installed");
400+
return;
401+
}
402+
403+
Logger.Info("Downloading node");
404+
await downloadService.DownloadToFileAsync(NodeDownloadUrl, NodeDownloadPath, progress: progress);
405+
406+
Logger.Info("Installing node");
407+
progress?.Report(
408+
new ProgressReport(
409+
progress: 0.5f,
410+
isIndeterminate: true,
411+
type: ProgressType.Generic,
412+
message: "Installing prerequisites..."
413+
)
414+
);
415+
416+
// unzip
417+
await ArchiveHelper.Extract(NodeDownloadPath, AssetsDir, progress);
418+
419+
// move to assets dir
420+
var existingNodeDir = Path.Combine(AssetsDir, "node-v20.11.0-win-x64");
421+
Directory.Move(existingNodeDir, Path.Combine(AssetsDir, "nodejs"));
422+
423+
progress?.Report(
424+
new ProgressReport(progress: 1f, message: "Node install complete", type: ProgressType.Generic)
425+
);
426+
427+
File.Delete(NodeDownloadPath);
428+
}
429+
375430
private async Task UnzipGit(IProgress<ProgressReport>? progress = null)
376431
{
377432
if (progress == null)

StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs

+6
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,10 @@ Task RunGit(
3434

3535
Task<string> GetGitOutput(string? workingDirectory = null, params string[] args);
3636
Task InstallTkinterIfNecessary(IProgress<ProgressReport>? progress = null);
37+
Task RunNpm(
38+
ProcessArgs args,
39+
string? workingDirectory = null,
40+
Action<ProcessOutput>? onProcessOutput = null
41+
);
42+
Task InstallNodeIfNecessary(IProgress<ProgressReport>? progress = null);
3743
}

StabilityMatrix.Core/Helper/PrerequisiteHelper.cs

+14
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ public Task InstallTkinterIfNecessary(IProgress<ProgressReport>? progress = null
107107
throw new NotImplementedException();
108108
}
109109

110+
public Task RunNpm(
111+
ProcessArgs args,
112+
string? workingDirectory = null,
113+
Action<ProcessOutput>? onProcessOutput = null
114+
)
115+
{
116+
throw new NotImplementedException();
117+
}
118+
119+
public Task InstallNodeIfNecessary(IProgress<ProgressReport>? progress = null)
120+
{
121+
throw new NotImplementedException();
122+
}
123+
110124
public async Task InstallAllIfNecessary(IProgress<ProgressReport>? progress = null)
111125
{
112126
await InstallVcRedistIfNecessary(progress);

0 commit comments

Comments
 (0)