Skip to content
This repository was archived by the owner on Dec 7, 2023. It is now read-only.
Merged
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
93272da
Install plugin and dependancies.
Aug 6, 2021
0ed1611
remove unsed if statement
Aug 6, 2021
6bd6491
fix incorrect file path check
Aug 6, 2021
b789bfa
Add plugin file checksum
Aug 6, 2021
57556ae
update format
Aug 6, 2021
6920e43
Update Checksum
Aug 6, 2021
79a63ed
Update MainService.Plugins.cs
Aug 7, 2021
aac4acb
Merge branch 'master' into plugin-dependancy
Aug 7, 2021
32e9835
Update MainService.Plugins.cs
Aug 7, 2021
6ace0c0
Update MainService.Plugins.cs
Aug 8, 2021
aec9aa2
Update MainService.Plugins.cs
Aug 8, 2021
6b697b3
version
Aug 8, 2021
3af86cc
Update Helper.cs
Aug 8, 2021
0c26ba9
Remove using
erikzhang Aug 8, 2021
252956b
Merge branch 'plugin-dependancy' of github.com:Liaojinghui/neo-node i…
Aug 8, 2021
3786f17
remove hard code dependency
Aug 8, 2021
cbd2566
catch exception if no dependency file
Aug 8, 2021
5ba876f
skip existing dependency
Aug 8, 2021
6cdf6c4
fix typo
Aug 8, 2021
3f2f34f
update comments
Aug 9, 2021
f22c4e2
add `reinstall` command
Aug 9, 2021
7b8a696
fix format
Aug 9, 2021
571f8de
optimise dependency check
Aug 9, 2021
e2e8537
optimise `Uninstall` command
Aug 9, 2021
2e1942b
check dependency from archive file before install plugin
Aug 10, 2021
a8bf3aa
prevent circular dependency
Aug 10, 2021
4e5eac6
fix typo
Aug 10, 2021
5718a3d
update comment
Aug 10, 2021
6ab1591
fix `reinstall`
Aug 10, 2021
1d409ab
fix circular dependency
Aug 10, 2021
386cfdd
fix format
Aug 10, 2021
2e804c3
install dependency from config.json
Aug 15, 2021
b03fbc5
Merge branch 'master' into plugin-dependancy
Aug 19, 2021
a26dff4
Add url
Aug 19, 2021
c29cdd8
Merge branch 'master' into plugin-dependancy
Aug 25, 2021
087bbaf
Update MainService.Plugins.cs
shargon Aug 26, 2021
90d062e
Merge branch 'master' into plugin-dependancy
Aug 27, 2021
1b1651a
update command suggestion
Aug 30, 2021
224bf21
Merge branch 'master' into plugin-dependancy
Aug 31, 2021
450cc22
remove unused file path.
Sep 3, 2021
7e29ca6
Merge branch 'master' into plugin-dependancy
shargon Sep 16, 2021
8172b16
Merge branch 'master' into plugin-dependancy
Oct 10, 2021
be611a9
Merge branch 'master' into plugin-dependancy
Dec 11, 2021
4b4a9c5
fix merge issues
Dec 11, 2021
05ba379
Merge branch 'master' into plugin-dependancy
Jan 19, 2022
cc201b9
update typo and format
Jan 19, 2022
79e5ada
update console output format
Mar 29, 2022
5dd3b9f
Merge branch 'develop' into plugin-dependancy
Apr 1, 2022
1833dd6
Update neo-cli/CLI/MainService.Plugins.cs
Apr 8, 2022
2ffb8ae
Rename
erikzhang Apr 10, 2022
3c2adee
Update MainService.Plugins.cs
erikzhang Apr 10, 2022
a2d4280
install plugin first
Apr 10, 2022
be78734
update temp path
Apr 10, 2022
4e688e3
in case of multiple `config.json` no matter what
Apr 10, 2022
44b30b7
format
Apr 10, 2022
1b429da
Optimize
erikzhang Apr 11, 2022
8cf1154
Remove pluginToInstall
erikzhang Apr 11, 2022
372928a
Optimize console messages
erikzhang Apr 11, 2022
d653a05
Optimize InstallPluginAsync and InstallDependency
erikzhang Apr 11, 2022
d91896d
Rename
erikzhang Apr 11, 2022
72ff5e6
Add installed
erikzhang Apr 11, 2022
321eee1
remove reinstall
Apr 11, 2022
a65b8e1
Revert "remove reinstall"
Apr 13, 2022
c3b40a1
fix reinstall issue
Apr 13, 2022
d8c257c
optimise logic
Apr 13, 2022
6c4542f
optimise logic
Apr 13, 2022
c9754a2
optimise logic...
Apr 13, 2022
5091ae0
fix format
Apr 13, 2022
770d3b0
ignore file operation exception
Apr 13, 2022
bd0631b
remove uninstall.plugin.txt
Apr 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 175 additions & 20 deletions neo-cli/CLI/MainService.Plugins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Microsoft.Extensions.Configuration;
using Neo.ConsoleService;
using Neo.IO.Json;
using Neo.Plugins;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;

namespace Neo.CLI
Expand All @@ -30,14 +33,51 @@ partial class MainService
[ConsoleCommand("install", Category = "Plugin Commands")]
private async Task OnInstallCommandAsync(string pluginName)
{
if (PluginExists(pluginName))
{
ConsoleHelper.Warning($"Plugin already exist.");
return;
}

await InstallPluginAsync(pluginName);
ConsoleHelper.Warning("Install successful, please restart neo-cli.");
}

/// <summary>
/// Force to install a plugin again. This will overwrite
/// existing plugin files, in case of any file missing or
/// damage to the old version.
/// </summary>
/// <param name="pluginName">name of the plugin</param>
[ConsoleCommand("reinstall", Category = "Plugin Commands", Description = "Overwrite existing plugin by force.")]
private async Task OnReinstallCommand(string pluginName)
{
await InstallPluginAsync(pluginName, overWrite: true);
ConsoleHelper.Warning("Reinstall successful, please restart neo-cli.");
}

/// <summary>
/// Download plugin from github release
/// The function of download and install are divided
/// for the consideration of `update` command that
/// might be added in the future.
/// </summary>
/// <param name="pluginName">name of the plugin</param>
/// <returns>Downloaded content</returns>
private async Task<MemoryStream> DownloadPluginAsync(string pluginName)
{
var url =
$"https://github.com/neo-project/neo-modules/releases/download/v{typeof(Plugin).Assembly.GetVersion()}/{pluginName}.zip";
using HttpClient http = new();
HttpResponseMessage response = await http.GetAsync($"https://github.com/neo-project/neo-modules/releases/download/v{typeof(Plugin).Assembly.GetVersion()}/{pluginName}.zip");
HttpResponseMessage response = await http.GetAsync(url);
if (response.StatusCode == HttpStatusCode.NotFound)
{
response.Dispose();
Version versionCore = typeof(Plugin).Assembly.GetName().Version;
HttpRequestMessage request = new(HttpMethod.Get, "https://api.github.com/repos/neo-project/neo-modules/releases");
request.Headers.UserAgent.ParseAdd($"{GetType().Assembly.GetName().Name}/{GetType().Assembly.GetVersion()}");
HttpRequestMessage request = new(HttpMethod.Get,
"https://api.github.com/repos/neo-project/neo-modules/releases");
request.Headers.UserAgent.ParseAdd(
$"{GetType().Assembly.GetName().Name}/{GetType().Assembly.GetVersion()}");
using HttpResponseMessage responseApi = await http.SendAsync(request);
byte[] buffer = await responseApi.Content.ReadAsByteArrayAsync();
JObject releases = JObject.Parse(buffer);
Expand All @@ -54,20 +94,87 @@ private async Task OnInstallCommandAsync(string pluginName)
if (asset is null) throw new Exception("Plugin doesn't exist.");
response = await http.GetAsync(asset["browser_download_url"].GetString());
}

using (response)
{
var totalRead = 0L;
byte[] buffer = new byte[1024];
int read;
await using Stream stream = await response.Content.ReadAsStreamAsync();
using ZipArchive zip = new(stream, ZipArchiveMode.Read);
try
{
zip.ExtractToDirectory(".");
ConsoleHelper.Info("Install successful, please restart neo-cli.");
}
catch (IOException)
ConsoleHelper.Info("From ", $"{url}");
var output = new MemoryStream();
while ((read = await stream.ReadAsync(buffer)) > 0)
{
ConsoleHelper.Warning($"Plugin already exist.");
output.Write(buffer, 0, read);
totalRead += read;
Console.Write(
$"\rDownloading {pluginName}.zip {totalRead / 1024}KB/{response.Content.Headers.ContentLength / 1024}KB {(totalRead * 100) / response.Content.Headers.ContentLength}%");
}

Console.WriteLine();
return output;
}
}

/// <summary>
/// Install plugin from stream
/// </summary>
/// <param name="pluginName">name of the plugin</param>
/// <param name="overWrite">Install by force for `update`</param>
private async Task InstallPluginAsync(string pluginName, HashSet<string> installed = null,
bool overWrite = false)
{
installed ??= new HashSet<string>();
if (!installed.Add(pluginName)) return;
if (!overWrite && PluginExists(pluginName)) return;

await using MemoryStream stream = await DownloadPluginAsync(pluginName);
using (SHA256 sha256 = SHA256.Create())
{
ConsoleHelper.Info("SHA256: ", $"{sha256.ComputeHash(stream.ToArray()).ToHexString()}");
}

using ZipArchive zip = new(stream, ZipArchiveMode.Read);
ZipArchiveEntry entry = zip.Entries.FirstOrDefault(p => p.Name == "config.json");
if (entry is not null)
{
await using Stream es = entry.Open();
await InstallDependenciesAsync(es, installed);
}
zip.ExtractToDirectory("./", true);
}

/// <summary>
/// Install the dependency of the plugin
/// </summary>
/// <param name="config">plugin config path in temp</param>
/// <param name="installed">Dependency set</param>
private async Task InstallDependenciesAsync(Stream config, HashSet<string> installed)
{
IConfigurationSection dependency = new ConfigurationBuilder()
.AddJsonStream(config)
.Build()
.GetSection("Dependency");

if (!dependency.Exists()) return;
var dependencies = dependency.GetChildren().Select(p => p.Get<string>()).ToArray();
if (dependencies.Length == 0) return;

foreach (string plugin in dependencies.Where(p => !PluginExists(p)))
{
ConsoleHelper.Info($"Installing dependency: {plugin}");
await InstallPluginAsync(plugin, installed);
}
}

/// <summary>
/// Check that the plugin has all necessary files
/// </summary>
/// <param name="pluginName"> Name of the plugin</param>
/// <returns></returns>
private static bool PluginExists(string pluginName)
{
return File.Exists($"Plugins/{pluginName}.dll");
}

/// <summary>
Expand All @@ -77,30 +184,77 @@ private async Task OnInstallCommandAsync(string pluginName)
[ConsoleCommand("uninstall", Category = "Plugin Commands")]
private void OnUnInstallCommand(string pluginName)
{
var plugin = Plugin.Plugins.FirstOrDefault(p => p.Name == pluginName);
if (plugin is null)
if (!PluginExists(pluginName))
{
ConsoleHelper.Warning("Plugin not found");
return;
}
if (plugin is Logger)

var plugin = Plugin.Plugins.FirstOrDefault(p => p.Name == pluginName);
if (plugin is not null)
{
ConsoleHelper.Warning("You cannot uninstall a built-in plugin.");
return;
if (plugin is Logger)
{
ConsoleHelper.Warning("You cannot uninstall a built-in plugin.");
return;
}

Plugin.Plugins.Remove(plugin);
}

foreach (var p in Plugin.Plugins)
{
try
{
using var reader = File.OpenRead($"./Plugins/{p.Name}/config.json");
if (new ConfigurationBuilder()
.AddJsonStream(reader)
.Build()
.GetSection("Dependency")
.GetChildren()
.Select(d => d.Get<string>())
.Any(v => v == pluginName))
{
ConsoleHelper.Error(
$"Can not uninstall. Other plugins depend on this plugin, try `reinstall {pluginName}` if the plugin is broken.");
return;
}
}
catch (Exception)
{
// ignored
}
}

File.Delete(plugin.Path);
File.Delete(plugin.ConfigFile);
try
{
Directory.Delete(Path.GetDirectoryName(plugin.ConfigFile), false);
DeleteFiles(new[] { $"Plugins/{pluginName}.dll", $"Plugins/{pluginName}/config.json" });
Directory.Delete($"Plugins/{pluginName}", false);
}
catch (IOException)
{
}

ConsoleHelper.Info("Uninstall successful, please restart neo-cli.");
}

private static void DeleteFiles(IEnumerable<string> list)
{
foreach (var file in list)
{
try
{
if (!File.Exists(file)) continue;
ConsoleHelper.Info("Deleting ", file);
File.Delete(file);
}
catch (Exception)
{
// ignored
}
}
}

/// <summary>
/// Process "plugins" command
/// </summary>
Expand All @@ -113,7 +267,8 @@ private void OnPluginsCommand()
foreach (Plugin plugin in Plugin.Plugins)
{
if (plugin is Logger) continue;
ConsoleHelper.Info($"\t{plugin.Name,-20}", plugin.Description);
var name = $"{plugin.Name}@{plugin.Version}";
Console.WriteLine($"\t{name,-25}{plugin.Description}");
}
}
else
Expand Down