-
-
Notifications
You must be signed in to change notification settings - Fork 453
Add Node.js environment setup #1498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
4ea1ee0
add Node.js environment setup
jjw24 eb203c2
executable plugin language check ignore case
jjw24 7441e90
python language check ignore case
jjw24 9e55fc5
add node plugin execution
jjw24 9b4a592
add Node.js file path browser to Settings window
jjw24 e29e2bd
change language key selectPythonDirectory to select
jjw24 acb6306
in Settings Window separate language from plugin env section
jjw24 65db4e4
add translation for file browser window title
jjw24 5263bf5
add filter to file dialog for selecting Python exe
jjw24 76c0f03
update python directory to python file directory
jjw24 56e6d6e
remove pythonDirectory language key
jjw24 1bb2de3
change wording 'No Setting' to 'None' when no path
jjw24 adf712d
add Node.js path to error reporting
jjw24 4b0d17c
set Constant env paths variables when PlugingSettings is set
jjw24 f76e296
update set plugin path for python
jjw24 2dcd4d7
centralise all plugin env folders in Environments directory
jjw24 c013713
Merge remote-tracking branch 'origin/dev' into add_nodejs_env
jjw24 c7cee4a
add ensure latest installation
jjw24 4e48c3e
refactor
jjw24 6561253
ensure latest only when using Flow's env setup
jjw24 2dba518
Merge remote-tracking branch 'origin/dev' into add_nodejs_env
jjw24 9eb5489
update setting's environment file paths after app update
jjw24 07bc2a7
remove PythonEmbeddable location
jjw24 680ed77
add abstract create plugin pair method to handle each plugin type
jjw24 d297779
remove unused SupportedLanguage property
jjw24 5975b6e
plugin env paths correction after update or mode change
jjw24 87e49cd
formatting
jjw24 e1e8951
remove duplicated updater pre-startup methods
jjw24 e28906d
rename method
jjw24 cef53bc
add empty string defaults to plugin executable paths
jjw24 0daf52f
remove PythonEmbeddable folder regardless if it's being used or not
jjw24 d2658c7
add null check
jjw24 5af272a
Merge branch 'dev' into add_nodejs_env
jjw24 d386735
Merge remote-tracking branch 'origin/dev' into add_nodejs_env
jjw24 ae66a2c
Merge branch 'dev' into add_nodejs_env
VictoriousRaptor 7da7e60
Merge branch 'dev' into add_nodejs_env
jjw24 75a23d1
Fix option order in setting window
VictoriousRaptor 50cd17e
Merge branch 'dev' into add_nodejs_env
VictoriousRaptor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
234 changes: 234 additions & 0 deletions
234
Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
using Flow.Launcher.Infrastructure.Logger; | ||
using Flow.Launcher.Infrastructure.UserSettings; | ||
using Flow.Launcher.Plugin; | ||
using Flow.Launcher.Plugin.SharedCommands; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Windows.Forms; | ||
|
||
namespace Flow.Launcher.Core.ExternalPlugins.Environments | ||
{ | ||
public abstract class AbstractPluginEnvironment | ||
{ | ||
internal abstract string Language { get; } | ||
|
||
internal abstract string EnvName { get; } | ||
|
||
internal abstract string EnvPath { get; } | ||
|
||
internal abstract string InstallPath { get; } | ||
|
||
internal abstract string ExecutablePath { get; } | ||
|
||
internal virtual string FileDialogFilter => string.Empty; | ||
|
||
internal abstract string PluginsSettingsFilePath { get; set; } | ||
|
||
internal List<PluginMetadata> PluginMetadataList; | ||
|
||
internal PluginsSettings PluginSettings; | ||
|
||
internal AbstractPluginEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) | ||
{ | ||
PluginMetadataList = pluginMetadataList; | ||
PluginSettings = pluginSettings; | ||
} | ||
|
||
internal IEnumerable<PluginPair> Setup() | ||
{ | ||
if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase))) | ||
return new List<PluginPair>(); | ||
|
||
// TODO: Remove. This is backwards compatibility for 1.10.0 release- changed PythonEmbeded to Environments/Python | ||
if (Language.Equals(AllowedLanguage.Python, StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
FilesFolders.RemoveFolderIfExists(Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable")); | ||
|
||
if (!string.IsNullOrEmpty(PluginSettings.PythonDirectory) && PluginSettings.PythonDirectory.StartsWith(Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable"))) | ||
{ | ||
InstallEnvironment(); | ||
PluginSettings.PythonDirectory = string.Empty; | ||
} | ||
} | ||
|
||
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath)) | ||
{ | ||
// Ensure latest only if user is using Flow's environment setup. | ||
if (PluginsSettingsFilePath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase)) | ||
EnsureLatestInstalled(ExecutablePath, PluginsSettingsFilePath, EnvPath); | ||
|
||
return SetPathForPluginPairs(PluginsSettingsFilePath, Language); | ||
} | ||
|
||
if (MessageBox.Show($"Flow detected you have installed {Language} plugins, which " + | ||
$"will require {EnvName} to run. Would you like to download {EnvName}? " + | ||
Environment.NewLine + Environment.NewLine + | ||
"Click no if it's already installed, " + | ||
$"and you will be prompted to select the folder that contains the {EnvName} executable", | ||
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No) | ||
{ | ||
var msg = $"Please select the {EnvName} executable"; | ||
var selectedFile = string.Empty; | ||
|
||
selectedFile = GetFileFromDialog(msg, FileDialogFilter); | ||
|
||
if (!string.IsNullOrEmpty(selectedFile)) | ||
PluginsSettingsFilePath = selectedFile; | ||
|
||
// Nothing selected because user pressed cancel from the file dialog window | ||
if (string.IsNullOrEmpty(selectedFile)) | ||
InstallEnvironment(); | ||
} | ||
else | ||
{ | ||
InstallEnvironment(); | ||
} | ||
|
||
if (FilesFolders.FileExists(PluginsSettingsFilePath)) | ||
{ | ||
return SetPathForPluginPairs(PluginsSettingsFilePath, Language); | ||
} | ||
else | ||
{ | ||
MessageBox.Show( | ||
$"Unable to set {Language} executable path, please try from Flow's settings (scroll down to the bottom)."); | ||
Log.Error("PluginsLoader", | ||
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", | ||
$"{Language}Environment"); | ||
|
||
return new List<PluginPair>(); | ||
} | ||
} | ||
|
||
internal abstract void InstallEnvironment(); | ||
|
||
private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath) | ||
{ | ||
if (expectedPath == currentPath) | ||
return; | ||
|
||
FilesFolders.RemoveFolderIfExists(installedDirPath); | ||
|
||
InstallEnvironment(); | ||
|
||
} | ||
|
||
internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata); | ||
|
||
private IEnumerable<PluginPair> SetPathForPluginPairs(string filePath, string languageToSet) | ||
{ | ||
var pluginPairs = new List<PluginPair>(); | ||
|
||
foreach (var metadata in PluginMetadataList) | ||
{ | ||
if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) | ||
pluginPairs.Add(CreatePluginPair(filePath, metadata)); | ||
} | ||
|
||
return pluginPairs; | ||
} | ||
|
||
private string GetFileFromDialog(string title, string filter = "") | ||
{ | ||
var dlg = new OpenFileDialog | ||
{ | ||
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), | ||
Multiselect = false, | ||
CheckFileExists = true, | ||
CheckPathExists = true, | ||
Title = title, | ||
Filter = filter | ||
}; | ||
|
||
var result = dlg.ShowDialog(); | ||
if (result == DialogResult.OK) | ||
{ | ||
return dlg.FileName; | ||
} | ||
else | ||
{ | ||
return string.Empty; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// After app updated while in portable mode or switched between portable/roaming mode, | ||
/// need to update each plugin's executable path so user will not be prompted again to reinstall the environments. | ||
/// </summary> | ||
/// <param name="settings"></param> | ||
public static void PreStartPluginExecutablePathUpdate(Settings settings) | ||
{ | ||
if (DataLocation.PortableDataLocationInUse()) | ||
{ | ||
// When user is using portable but has moved flow to a different location | ||
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName) | ||
&& !settings.PluginSettings.PythonExecutablePath.StartsWith(DataLocation.PortableDataPath)) | ||
{ | ||
settings.PluginSettings.PythonExecutablePath | ||
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath); | ||
} | ||
|
||
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName) | ||
&& !settings.PluginSettings.NodeExecutablePath.StartsWith(DataLocation.PortableDataPath)) | ||
{ | ||
settings.PluginSettings.NodeExecutablePath | ||
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath); | ||
} | ||
|
||
// When user has switched from roaming to portable | ||
if (IsUsingRoamingPath(settings.PluginSettings.PythonExecutablePath)) | ||
{ | ||
settings.PluginSettings.PythonExecutablePath | ||
= settings.PluginSettings.PythonExecutablePath.Replace(DataLocation.RoamingDataPath, DataLocation.PortableDataPath); | ||
} | ||
|
||
if (IsUsingRoamingPath(settings.PluginSettings.NodeExecutablePath)) | ||
{ | ||
settings.PluginSettings.NodeExecutablePath | ||
= settings.PluginSettings.NodeExecutablePath.Replace(DataLocation.RoamingDataPath, DataLocation.PortableDataPath); | ||
} | ||
} | ||
else | ||
{ | ||
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName)) | ||
settings.PluginSettings.PythonExecutablePath | ||
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath); | ||
|
||
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName)) | ||
settings.PluginSettings.NodeExecutablePath | ||
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath); | ||
} | ||
} | ||
|
||
private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName) | ||
{ | ||
if (string.IsNullOrEmpty(filePath)) | ||
return false; | ||
|
||
// DataLocation.PortableDataPath returns the current portable path, this determines if an out | ||
// of date path is also a portable path. | ||
var portableAppEnvLocation = $"UserData\\{DataLocation.PluginEnvironments}\\{pluginEnvironmentName}"; | ||
|
||
return filePath.Contains(portableAppEnvLocation); | ||
} | ||
|
||
private static bool IsUsingRoamingPath(string filePath) | ||
{ | ||
if (string.IsNullOrEmpty(filePath)) | ||
return false; | ||
|
||
return filePath.StartsWith(DataLocation.RoamingDataPath); | ||
} | ||
|
||
private static string GetUpdatedEnvironmentPath(string filePath) | ||
{ | ||
var index = filePath.IndexOf(DataLocation.PluginEnvironments); | ||
|
||
// get the substring after "Environments" because we can not determine it dynamically | ||
var ExecutablePathSubstring = filePath.Substring(index + DataLocation.PluginEnvironments.Count()); | ||
return $"{DataLocation.PluginEnvironmentsPath}{ExecutablePathSubstring}"; | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
Flow.Launcher.Core/ExternalPlugins/Environments/JavaScriptEnvironment.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.Collections.Generic; | ||
using Flow.Launcher.Infrastructure.UserSettings; | ||
using Flow.Launcher.Plugin; | ||
|
||
namespace Flow.Launcher.Core.ExternalPlugins.Environments | ||
{ | ||
|
||
internal class JavaScriptEnvironment : TypeScriptEnvironment | ||
{ | ||
internal override string Language => AllowedLanguage.JavaScript; | ||
|
||
internal JavaScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { } | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using Droplex; | ||
using Flow.Launcher.Core.Plugin; | ||
using Flow.Launcher.Infrastructure.UserSettings; | ||
using Flow.Launcher.Plugin; | ||
using Flow.Launcher.Plugin.SharedCommands; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
|
||
namespace Flow.Launcher.Core.ExternalPlugins.Environments | ||
{ | ||
internal class PythonEnvironment : AbstractPluginEnvironment | ||
{ | ||
internal override string Language => AllowedLanguage.Python; | ||
|
||
internal override string EnvName => DataLocation.PythonEnvironmentName; | ||
|
||
internal override string EnvPath => Path.Combine(DataLocation.PluginEnvironmentsPath, EnvName); | ||
|
||
internal override string InstallPath => Path.Combine(EnvPath, "PythonEmbeddable-v3.8.9"); | ||
|
||
internal override string ExecutablePath => Path.Combine(InstallPath, "pythonw.exe"); | ||
|
||
internal override string FileDialogFilter => "Python|pythonw.exe"; | ||
|
||
internal override string PluginsSettingsFilePath { get => PluginSettings.PythonExecutablePath; set => PluginSettings.PythonExecutablePath = value; } | ||
|
||
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { } | ||
|
||
internal override void InstallEnvironment() | ||
{ | ||
FilesFolders.RemoveFolderIfExists(InstallPath); | ||
|
||
// Python 3.8.9 is used for Windows 7 compatibility | ||
DroplexPackage.Drop(App.python_3_8_9_embeddable, InstallPath).Wait(); | ||
|
||
PluginsSettingsFilePath = ExecutablePath; | ||
} | ||
|
||
internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) | ||
{ | ||
return new PluginPair | ||
{ | ||
Plugin = new PythonPlugin(filePath), | ||
Metadata = metadata | ||
}; | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System.Collections.Generic; | ||
using Droplex; | ||
using Flow.Launcher.Infrastructure.UserSettings; | ||
using Flow.Launcher.Plugin.SharedCommands; | ||
using Flow.Launcher.Plugin; | ||
using System.IO; | ||
using Flow.Launcher.Core.Plugin; | ||
|
||
namespace Flow.Launcher.Core.ExternalPlugins.Environments | ||
{ | ||
internal class TypeScriptEnvironment : AbstractPluginEnvironment | ||
{ | ||
internal override string Language => AllowedLanguage.TypeScript; | ||
|
||
internal override string EnvName => DataLocation.NodeEnvironmentName; | ||
|
||
internal override string EnvPath => Path.Combine(DataLocation.PluginEnvironmentsPath, EnvName); | ||
|
||
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0"); | ||
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe"); | ||
|
||
internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; } | ||
|
||
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { } | ||
|
||
internal override void InstallEnvironment() | ||
{ | ||
FilesFolders.RemoveFolderIfExists(InstallPath); | ||
|
||
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); | ||
|
||
PluginsSettingsFilePath = ExecutablePath; | ||
} | ||
|
||
internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) | ||
{ | ||
return new PluginPair | ||
{ | ||
Plugin = new NodePlugin(filePath), | ||
Metadata = metadata | ||
}; | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOL why javascript extends from typescript
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly so we don't need to reimplement anything since they both are using same everything. So plugin dev can set their plugin type as JavaScript or Typescript.