mirror of
https://github.com/netchx/netch.git
synced 2026-03-18 18:13:21 +08:00
Update Updater
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models.GitHubRelease;
|
||||
using Netch.Utils;
|
||||
using static Netch.Updater.Updater;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
@@ -24,9 +23,11 @@ namespace Netch.Controllers
|
||||
|
||||
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
|
||||
|
||||
public static string? LatestVersionNumber;
|
||||
public static string? LatestVersionUrl;
|
||||
public static Release? LatestRelease;
|
||||
public static Release LatestRelease = null!;
|
||||
|
||||
public static string LatestVersionNumber => LatestRelease.tag_name;
|
||||
|
||||
public static string LatestVersionUrl => LatestRelease.html_url;
|
||||
|
||||
public static event EventHandler? NewVersionFound;
|
||||
|
||||
@@ -34,7 +35,7 @@ namespace Netch.Controllers
|
||||
|
||||
public static event EventHandler? NewVersionNotFound;
|
||||
|
||||
public static async void Check(bool isPreRelease)
|
||||
public static async Task Check(bool isPreRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -43,10 +44,8 @@ namespace Netch.Controllers
|
||||
|
||||
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
|
||||
|
||||
var releases = JsonSerializer.Deserialize<List<Release>>(json);
|
||||
var releases = JsonSerializer.Deserialize<List<Release>>(json)!;
|
||||
LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
|
||||
LatestVersionNumber = LatestRelease.tag_name;
|
||||
LatestVersionUrl = LatestRelease.html_url;
|
||||
Logging.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
|
||||
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
|
||||
{
|
||||
@@ -70,47 +69,31 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DownloadUpdate(DownloadProgressChangedEventHandler onDownloadProgressChanged)
|
||||
public static bool GetFileNameAndHashFromMarkdownForm(in string text, out string fileName, out string sha256, string? keyword = null)
|
||||
{
|
||||
using WebClient client = new();
|
||||
|
||||
var latestVersionDownloadUrl = LatestRelease!.assets[0].browser_download_url;
|
||||
var tagPage = await client.DownloadStringTaskAsync(LatestVersionUrl!);
|
||||
var match = Regex.Match(tagPage, @"<td .*>(?<sha256>.*)</td>", RegexOptions.Singleline);
|
||||
|
||||
// TODO Replace with regex get basename and sha256
|
||||
var fileName = Path.GetFileName(new Uri(latestVersionDownloadUrl).LocalPath);
|
||||
fileName = fileName.Insert(fileName.LastIndexOf('.'), LatestVersionNumber!);
|
||||
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
|
||||
|
||||
var sha256 = match.Groups["sha256"].Value;
|
||||
|
||||
if (File.Exists(fileFullPath))
|
||||
{
|
||||
if (Utils.Utils.SHA256CheckSum(fileFullPath) == sha256)
|
||||
{
|
||||
UpdateNetch(fileFullPath);
|
||||
return;
|
||||
}
|
||||
|
||||
File.Delete(fileFullPath);
|
||||
}
|
||||
|
||||
IEnumerable<Match> matches;
|
||||
try
|
||||
{
|
||||
client.DownloadProgressChanged += onDownloadProgressChanged;
|
||||
await client.DownloadFileTaskAsync(new Uri(latestVersionDownloadUrl), fileFullPath);
|
||||
client.DownloadProgressChanged -= onDownloadProgressChanged;
|
||||
matches = Regex.Matches(text, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline).Cast<Match>().Skip(2);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
|
||||
Logging.Error(e.ToString());
|
||||
throw new Exception(i18N.Translate("Find update filename and hash failed"));
|
||||
}
|
||||
|
||||
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
|
||||
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
|
||||
Match match = keyword == null ? matches.First() : matches.First(m => m.Groups["filename"].Value.Contains(keyword));
|
||||
|
||||
UpdateNetch(fileFullPath);
|
||||
if (match != null)
|
||||
{
|
||||
fileName = match.Groups["filename"].Value;
|
||||
sha256 = match.Groups["sha256"].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
fileName = string.Empty;
|
||||
sha256 = string.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1338,7 +1338,7 @@ namespace Netch.Forms
|
||||
Hide();
|
||||
}
|
||||
|
||||
public async void Exit(bool forceExit = false)
|
||||
public async void Exit(bool forceExit = false, bool saveConfiguration = true)
|
||||
{
|
||||
if (!IsWaiting() && !Global.Settings.StopWhenExited && !forceExit)
|
||||
{
|
||||
@@ -1351,7 +1351,11 @@ namespace Netch.Forms
|
||||
State = State.Terminating;
|
||||
NotifyIcon.Visible = false;
|
||||
Hide();
|
||||
Configuration.Save();
|
||||
|
||||
if (saveConfiguration)
|
||||
{
|
||||
Configuration.Save();
|
||||
}
|
||||
|
||||
foreach (var file in new[] {"data\\last.json", "data\\privoxy.conf"})
|
||||
if (File.Exists(file))
|
||||
@@ -1411,12 +1415,12 @@ namespace Netch.Forms
|
||||
NewVersionLabel.Visible = true;
|
||||
};
|
||||
|
||||
UpdateChecker.Check(Global.Settings.CheckBetaUpdate);
|
||||
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
|
||||
}
|
||||
|
||||
private async void NewVersionLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!UpdateChecker.LatestRelease!.assets.Any())
|
||||
if (ModifierKeys == Keys.Control || !UpdateChecker.LatestRelease!.assets.Any())
|
||||
{
|
||||
Utils.Utils.Open(UpdateChecker.LatestVersionUrl!);
|
||||
return;
|
||||
@@ -1436,7 +1440,7 @@ namespace Netch.Forms
|
||||
BeginInvoke(new Action(() => { NewVersionLabel.Text = $"{args.ProgressPercentage}%"; }));
|
||||
}
|
||||
|
||||
await UpdateChecker.DownloadUpdate(OnDownloadProgressChanged);
|
||||
await Updater.Updater.DownloadAndUpdate(Path.Combine(Global.NetchDir, "data"), Global.NetchDir, OnDownloadProgressChanged);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Netch
|
||||
Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ";" + Path.Combine(Global.NetchDir, "bin"),
|
||||
EnvironmentVariableTarget.Process);
|
||||
|
||||
Updater.Updater.CleanOld();
|
||||
Updater.Updater.CleanOld(Global.NetchDir);
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] {"mode\\Custom", "data", "i18n", "logging"};
|
||||
|
||||
@@ -1,29 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Netch.Forms;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Controllers;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Updater
|
||||
{
|
||||
public static class Updater
|
||||
public class Updater
|
||||
{
|
||||
public static void UpdateNetch(string updateFilePath)
|
||||
{
|
||||
var tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var extractPath = Path.Combine(tempFolder, "extract");
|
||||
int exitCode;
|
||||
if ((exitCode = Extract(updateFilePath, extractPath, true, tempFolder)) != 0)
|
||||
{
|
||||
MessageBoxX.Show($"7za exit with code {exitCode}");
|
||||
return;
|
||||
}
|
||||
private static IEnumerable<string> _keepDirectory = new List<string>(new[] {"data", "mode\\Custom"});
|
||||
private readonly string _targetPath;
|
||||
private readonly string _tempFolder;
|
||||
private readonly string _updateFilePath;
|
||||
|
||||
foreach (var file in Directory.GetFiles(Global.NetchDir, "*", SearchOption.AllDirectories))
|
||||
private Updater(string updateFilePath, string targetPath)
|
||||
{
|
||||
_targetPath = targetPath;
|
||||
_tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(_tempFolder);
|
||||
|
||||
_updateFilePath = Path.GetFullPath(updateFilePath);
|
||||
_keepDirectory = _keepDirectory.Select(s => Path.Combine(targetPath, s));
|
||||
}
|
||||
|
||||
private void ApplyUpdate()
|
||||
{
|
||||
var extractPath = Path.Combine(_tempFolder, "extract");
|
||||
int exitCode;
|
||||
if ((exitCode = Extract(extractPath, true)) != 0)
|
||||
throw new Exception(i18N.Translate($"7za exit with code {exitCode}"));
|
||||
|
||||
MarkFilesOld();
|
||||
|
||||
MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _targetPath);
|
||||
|
||||
Configuration.Save();
|
||||
Global.Mutex.ReleaseMutex();
|
||||
Process.Start(Global.NetchExecutable);
|
||||
Global.MainForm.Exit(true, false);
|
||||
}
|
||||
|
||||
private void MarkFilesOld()
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(_targetPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (new[] {"data", "mode\\Custom"}.ToList().Any(p => file.StartsWith(Path.Combine(Global.NetchDir, p))))
|
||||
if (_keepDirectory.Any(p => file.StartsWith(p)))
|
||||
continue;
|
||||
|
||||
try
|
||||
@@ -35,31 +62,19 @@ namespace Netch.Updater
|
||||
throw new Exception("Updater wasn't able to rename file: " + file);
|
||||
}
|
||||
}
|
||||
|
||||
MoveDirectory(Path.Combine(extractPath, "Netch"), Global.NetchDir, true);
|
||||
|
||||
Global.Mutex.ReleaseMutex();
|
||||
Process.Start(Global.NetchExecutable);
|
||||
Global.MainForm.Exit(true);
|
||||
}
|
||||
|
||||
private static int Extract(string archiveFileName, string destDirName, bool overwrite, string? tempFolder = null)
|
||||
private int Extract(string destDirName, bool overwrite)
|
||||
{
|
||||
tempFolder ??= Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var temp7za = Path.Combine(tempFolder, "7za.exe");
|
||||
archiveFileName = Path.GetFullPath(archiveFileName);
|
||||
destDirName = Path.GetFullPath(destDirName);
|
||||
|
||||
if (!Directory.Exists(tempFolder))
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
var temp7za = Path.Combine(_tempFolder, "7za.exe");
|
||||
|
||||
if (!File.Exists(temp7za))
|
||||
File.WriteAllBytes(temp7za, Resources._7za);
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($" x \"{archiveFileName}\" -o\"{destDirName}\" ");
|
||||
argument.Append($" x \"{_updateFilePath}\" -o\"{destDirName}\"");
|
||||
if (overwrite)
|
||||
argument.Append(" -y ");
|
||||
argument.Append(" -y");
|
||||
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
@@ -67,41 +82,35 @@ namespace Netch.Updater
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
FileName = temp7za,
|
||||
Arguments = argument.ToString()
|
||||
});
|
||||
})!;
|
||||
|
||||
process?.WaitForExit();
|
||||
return process?.ExitCode ?? 2;
|
||||
process.WaitForExit();
|
||||
return process.ExitCode;
|
||||
}
|
||||
|
||||
private static void MoveDirectory(string sourceDirName, string destDirName, bool overwrite)
|
||||
private static void MoveAllFilesOver(string source, string target)
|
||||
{
|
||||
sourceDirName = Path.GetFullPath(sourceDirName);
|
||||
destDirName = Path.GetFullPath(destDirName);
|
||||
if (!overwrite)
|
||||
foreach (string directory in Directory.GetDirectories(source))
|
||||
{
|
||||
Directory.Move(sourceDirName, destDirName);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var dir in Directory.GetDirectories(sourceDirName, "*", SearchOption.AllDirectories))
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
string dirName = Path.GetFileName(directory);
|
||||
|
||||
foreach (var f in Directory.GetFiles(sourceDirName, "*", SearchOption.AllDirectories))
|
||||
try
|
||||
{
|
||||
File.Move(f, f.Replace(sourceDirName, destDirName));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new Exception("Updater wasn't able to move file: " + f);
|
||||
}
|
||||
if (!Directory.Exists(Path.Combine(target, dirName)))
|
||||
Directory.CreateDirectory(Path.Combine(target, dirName));
|
||||
|
||||
MoveAllFilesOver(directory, Path.Combine(target, dirName));
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(source))
|
||||
{
|
||||
var destFile = Path.Combine(target, Path.GetFileName(file));
|
||||
File.Delete(destFile);
|
||||
File.Move(file, destFile);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CleanOld()
|
||||
public static void CleanOld(string targetPath)
|
||||
{
|
||||
foreach (var f in Directory.GetFiles(Global.NetchDir, "*.old", SearchOption.AllDirectories))
|
||||
foreach (var f in Directory.GetFiles(targetPath, "*.old", SearchOption.AllDirectories))
|
||||
try
|
||||
{
|
||||
File.Delete(f);
|
||||
@@ -111,5 +120,41 @@ namespace Netch.Updater
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DownloadAndUpdate(string downloadPath,
|
||||
string targetPath,
|
||||
DownloadProgressChangedEventHandler onDownloadProgressChanged)
|
||||
{
|
||||
var keyword = (string?) null;
|
||||
|
||||
if (!UpdateChecker.GetFileNameAndHashFromMarkdownForm(UpdateChecker.LatestRelease.body, out var fileName, out var sha256, keyword))
|
||||
throw new Exception(i18N.Translate("parse release note failed"));
|
||||
|
||||
var fileFullPath = Path.Combine(downloadPath, fileName);
|
||||
var updater = new Updater(fileFullPath, targetPath);
|
||||
|
||||
if (!(File.Exists(fileFullPath) && Utils.Utils.SHA256CheckSum(fileFullPath) == sha256))
|
||||
{
|
||||
using WebClient client = new();
|
||||
try
|
||||
{
|
||||
client.DownloadProgressChanged += onDownloadProgressChanged;
|
||||
await client.DownloadFileTaskAsync(new Uri(UpdateChecker.LatestRelease.assets[0].browser_download_url), fileFullPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.DownloadProgressChanged -= onDownloadProgressChanged;
|
||||
}
|
||||
|
||||
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
|
||||
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
|
||||
}
|
||||
|
||||
updater.ApplyUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user