From 639d7b8e660a4627171a01fd973eeb231a8cbd62 Mon Sep 17 00:00:00 2001 From: Danil Kislov Date: Wed, 27 Mar 2019 11:10:37 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D1=83=D0=BA=D0=B0=D0=B7=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B0=D1=82=D1=8B=20(=D0=BA=D0=BE=D0=BD=D1=86?= =?UTF-8?q?=D0=B5=D1=80=D1=82=20=D0=BF=D0=BE=20=D0=B7=D0=B0=D1=8F=D0=B2?= =?UTF-8?q?=D0=BA=D0=B0=D0=BC=20=D0=B7=D0=B0=D0=B1=D1=8B=D0=B2=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B8=D1=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .semver.txt | 2 +- src/JiraTimeBot/DI/ContainerBuilder.cs | 3 + src/JiraTimeBot/JiraIntegration/JiraApi.cs | 15 +- src/JiraTimeBot/JiraTimeBot.csproj | 3 +- src/JiraTimeBot/Job.cs | 69 ++++--- src/JiraTimeBot/Properties/AssemblyInfo.cs | 62 +++---- .../TasksProcessors/ITasksProcessor.cs | 2 +- .../TasksProcessors/MeetingProcessor.cs | 4 +- .../TasksProcessors/WorkLogTasksProcessor.cs | 6 +- src/JiraTimeBot/UI/frmMain.Designer.cs | 27 +++ src/JiraTimeBot/UI/frmMain.cs | 80 ++------- src/JiraTimeBot/Update/IUpdater.cs | 9 + src/JiraTimeBot/Update/UpdateChecker.cs | 78 -------- src/JiraTimeBot/Update/Updater.cs | 169 ++++++++++++++++++ 14 files changed, 307 insertions(+), 222 deletions(-) create mode 100644 src/JiraTimeBot/Update/IUpdater.cs delete mode 100644 src/JiraTimeBot/Update/UpdateChecker.cs create mode 100644 src/JiraTimeBot/Update/Updater.cs diff --git a/.semver.txt b/.semver.txt index 2e9116b..f6c5328 100644 --- a/.semver.txt +++ b/.semver.txt @@ -1 +1 @@ -1.0.36 +1.0.37 diff --git a/src/JiraTimeBot/DI/ContainerBuilder.cs b/src/JiraTimeBot/DI/ContainerBuilder.cs index 63d03c3..2c1e6e5 100644 --- a/src/JiraTimeBot/DI/ContainerBuilder.cs +++ b/src/JiraTimeBot/DI/ContainerBuilder.cs @@ -11,6 +11,7 @@ using System; using System.Windows.Forms; using JiraTimeBot.UI; using JiraTimeBot.UI.Startup; +using JiraTimeBot.Update; namespace JiraTimeBot.DI { @@ -38,6 +39,8 @@ namespace JiraTimeBot.DI _builder.Register(f => new TrayMenu()).As().AsSelf(); _builder.Register(c => new CommitSkipper()).As(); + _builder.RegisterType().As(); + _builder.RegisterType().As().AsSelf(); _builder.RegisterType().As().AsSelf(); diff --git a/src/JiraTimeBot/JiraIntegration/JiraApi.cs b/src/JiraTimeBot/JiraIntegration/JiraApi.cs index 40217c7..49fefc3 100644 --- a/src/JiraTimeBot/JiraIntegration/JiraApi.cs +++ b/src/JiraTimeBot/JiraIntegration/JiraApi.cs @@ -95,14 +95,9 @@ namespace JiraTimeBot.JiraIntegration public void SetTodayWorklog(List taskTimeItems, Settings settings, DateTime? date = null, bool dummy = false, bool addCommentsToWorklog = false, CancellationToken cancellationToken = default(CancellationToken)) { + date = date.GetValueOrDefault(DateTime.Now.Date).Date; var jira = Jira.CreateRestClient(settings.JiraUrl, settings.JiraUserName, settings.JiraPassword); - if (date == null) - { - date = DateTime.Now.Date; - } - date = date.Value.Date; - //Удаляем добавленные вручную пользователем данные. RemoveWorklogsAddedByUser(taskTimeItems, settings, date, dummy, cancellationToken); @@ -124,9 +119,9 @@ namespace JiraTimeBot.JiraIntegration } var hasTodayWorklog = false; - var workLogs = issue.GetWorklogsAsync().Result; + var workLogs = issue.GetWorklogsAsync(cancellationToken).Result; var userWorklogs = workLogs.Where(w => - w.CreateDate.GetValueOrDefault().Date == date && w.Author.Equals(settings.JiraUserName, + w.StartDate.GetValueOrDefault().Date == date && w.Author.Equals(settings.JiraUserName, StringComparison.InvariantCultureIgnoreCase)).ToList(); foreach (var workLog in userWorklogs) @@ -145,7 +140,7 @@ namespace JiraTimeBot.JiraIntegration } if (!dummy) { - issue.DeleteWorklogAsync(workLog); + issue.DeleteWorklogAsync(workLog, token: cancellationToken); } hasTodayWorklog = false; } @@ -165,7 +160,7 @@ namespace JiraTimeBot.JiraIntegration Worklog workLogToAdd = new Worklog(timeSpentJira, date.Value, comment); if (!dummy) { - workLogToAdd = issue.AddWorklogAsync(workLogToAdd).Result; + workLogToAdd = issue.AddWorklogAsync(workLogToAdd, token: cancellationToken).Result; } _log.Trace($"Добавили Worklog для {taskTimeItem.Branch}: {workLogToAdd.Author} {workLogToAdd.CreateDate}: {workLogToAdd.TimeSpent}"); } diff --git a/src/JiraTimeBot/JiraTimeBot.csproj b/src/JiraTimeBot/JiraTimeBot.csproj index 72b3259..e1e7eb1 100644 --- a/src/JiraTimeBot/JiraTimeBot.csproj +++ b/src/JiraTimeBot/JiraTimeBot.csproj @@ -122,7 +122,8 @@ - + + frmMain.cs diff --git a/src/JiraTimeBot/Job.cs b/src/JiraTimeBot/Job.cs index d98161c..8732c65 100644 --- a/src/JiraTimeBot/Job.cs +++ b/src/JiraTimeBot/Job.cs @@ -31,7 +31,7 @@ namespace JiraTimeBot return Task.Run(() => DoTheJobImpl(settings, tasksProcessor, dummyMode, cancellationToken), cancellationToken); } - + private void DoTheJobImpl(Settings settings, ITasksProcessor tasksProcessor, bool dummyMode, CancellationToken cancellationToken) { int daysDiff = 0; @@ -40,43 +40,58 @@ namespace JiraTimeBot daysDiff = -1; } + var setForDate = DateTime.Now.Date; while (true) { - DateTime date = DateTime.Now.Date.AddDays(daysDiff); + DateTime realDate = DateTime.Now.Date.AddDays(daysDiff); - IMercurialLog mercurial = _mercurialProviders.MercurialLog; - if (settings.WorkType == WorkType.JiraLogs) - { - _log.Info("Использую Jira как источник информации."); - mercurial = _mercurialProviders.JiraCommitEmulator; - } - List commits = mercurial.GetMercurialLog(settings, date, cancellationToken); - List taskTimes = _taskTimeDiscoverer.CalculateTaskTime(commits, settings, cancellationToken); - - if (cancellationToken.IsCancellationRequested) + if (SetTaskTimesForDateImpl(setForDate, realDate, settings, tasksProcessor, dummyMode, cancellationToken)) { return; } - if (!taskTimes.Any()) + _log.Warn($"{realDate:dd.MM.yyyy} вы не сделали ничего полезного =) Использую предыдущий день."); + daysDiff--; + if (daysDiff < -7) { - _log.Warn($"{date:dd.MM.yyyy} вы не сделали ничего полезного =) Использую предыдущий день."); - daysDiff--; - if (daysDiff < -7) - { - _log.Error("Не нашли ни одного коммита за предыдущие 7 дней. Возможно вы в отпуске? Выхожу."); - return; - } - - continue; + _log.Error("Не нашли ни одного коммита за предыдущие 7 дней. Возможно вы в отпуске? Выхожу."); + return; } - - tasksProcessor.Process(date, taskTimes, settings, dummyMode, cancellationToken); - - _log.Info("Готово."); - return; } } + public Task SetTaskTimesForDate(DateTime setForDate, DateTime realDate, Settings settings, ITasksProcessor tasksProcessor, bool dummyMode, CancellationToken cancellationToken) + { + _log.Info("Начинаем работу"); + + return Task.Run(() => SetTaskTimesForDateImpl(setForDate, realDate, settings, tasksProcessor, dummyMode, cancellationToken), cancellationToken); + } + + private bool SetTaskTimesForDateImpl(DateTime setForDate, DateTime realDate, Settings settings, ITasksProcessor tasksProcessor, bool dummyMode, CancellationToken cancellationToken) + { + IMercurialLog mercurial = _mercurialProviders.MercurialLog; + if (settings.WorkType == WorkType.JiraLogs) + { + _log.Info("Использую Jira как источник информации."); + mercurial = _mercurialProviders.JiraCommitEmulator; + } + List commits = mercurial.GetMercurialLog(settings, realDate, cancellationToken); + List taskTimes = _taskTimeDiscoverer.CalculateTaskTime(commits, settings, cancellationToken); + + if (cancellationToken.IsCancellationRequested) + { + return false; + } + + if (!taskTimes.Any()) + { + return false; + } + + tasksProcessor.Process(setForDate, realDate, taskTimes, settings, dummyMode, cancellationToken); + + _log.Info("Готово."); + return true; + } } } diff --git a/src/JiraTimeBot/Properties/AssemblyInfo.cs b/src/JiraTimeBot/Properties/AssemblyInfo.cs index 4083cb6..9adeba5 100644 --- a/src/JiraTimeBot/Properties/AssemblyInfo.cs +++ b/src/JiraTimeBot/Properties/AssemblyInfo.cs @@ -1,31 +1,31 @@ -using System.Reflection; -using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("JiraTimeBot")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("JiraTimeBot")] -[assembly: AssemblyCopyright("Copyright © 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f3b89316-c38c-4ce6-9d88-0b1373cfba1a")] -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.36")] -[assembly: AssemblyFileVersion("1.0.36")] -[assembly: AssemblyInformationalVersion("1.0.36")] +using System.Reflection; +using System.Runtime.InteropServices; +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JiraTimeBot")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JiraTimeBot")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f3b89316-c38c-4ce6-9d88-0b1373cfba1a")] +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.37")] +[assembly: AssemblyFileVersion("1.0.37")] +[assembly: AssemblyInformationalVersion("1.0.37")] diff --git a/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs b/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs index 23968f2..8ff5477 100644 --- a/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs +++ b/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs @@ -8,6 +8,6 @@ namespace JiraTimeBot.TasksProcessors { interface ITasksProcessor { - void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)); + void Process(DateTime setForDate, DateTime realDate, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs b/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs index 4003944..4d3c0cf 100644 --- a/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs +++ b/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs @@ -19,9 +19,9 @@ namespace JiraTimeBot.TasksProcessors _jiraApi = jiraApi; } - public void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)) + public void Process(DateTime setForDate, DateTime realDate, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)) { - _log.Trace($"На реальную дату {date:dd.MM.yyyy} распределение по задачам:"); + _log.Trace($"На реальную дату {realDate:dd.MM.yyyy} распределение по задачам:"); foreach (var taskTime in taskTimes.OrderByDescending(f => f.Time)) { diff --git a/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs b/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs index f1cc39c..4e7b2ff 100644 --- a/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs +++ b/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs @@ -18,15 +18,15 @@ namespace JiraTimeBot.TasksProcessors _jiraApi = jiraApi; } - public void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)) + public void Process(DateTime setForDate, DateTime realDate, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)) { - _log.Trace($"На реальную дату {date:dd.MM.yyyy} распределение по задачам:"); + _log.Trace($"На реальную дату {realDate:dd.MM.yyyy} распределение по задачам:"); foreach (var taskTime in taskTimes) { _log.Trace($"- {taskTime.Branch} (коммитов {taskTime.Commits}): {taskTime.Time}"); } - _jiraApi.SetTodayWorklog(taskTimes, settings, dummy: dummyMode, addCommentsToWorklog: settings.AddCommentsToWorklog, cancellationToken: cancellationToken); + _jiraApi.SetTodayWorklog(taskTimes, settings, date: setForDate, dummy: dummyMode, addCommentsToWorklog: settings.AddCommentsToWorklog, cancellationToken: cancellationToken); } } } diff --git a/src/JiraTimeBot/UI/frmMain.Designer.cs b/src/JiraTimeBot/UI/frmMain.Designer.cs index 57f4ec6..d0f30f8 100644 --- a/src/JiraTimeBot/UI/frmMain.Designer.cs +++ b/src/JiraTimeBot/UI/frmMain.Designer.cs @@ -39,6 +39,8 @@ this.btnCancel = new System.Windows.Forms.Button(); this.btnSettings = new System.Windows.Forms.Button(); this.tmrUpdate = new System.Windows.Forms.Timer(this.components); + this.dteForDay = new System.Windows.Forms.DateTimePicker(); + this.btnDoForDate = new System.Windows.Forms.Button(); this.SuspendLayout(); // // txtLog @@ -117,11 +119,34 @@ this.tmrUpdate.Interval = 1000; this.tmrUpdate.Tick += new System.EventHandler(this.tmrUpdate_Tick); // + // dteForDay + // + this.dteForDay.CustomFormat = "dd.MM.yyyy"; + this.dteForDay.Format = System.Windows.Forms.DateTimePickerFormat.Short; + this.dteForDay.Location = new System.Drawing.Point(12, 69); + this.dteForDay.MinDate = new System.DateTime(2019, 1, 1, 0, 0, 0, 0); + this.dteForDay.Name = "dteForDay"; + this.dteForDay.ShowUpDown = true; + this.dteForDay.Size = new System.Drawing.Size(113, 20); + this.dteForDay.TabIndex = 17; + // + // btnDoForDate + // + this.btnDoForDate.Location = new System.Drawing.Point(13, 96); + this.btnDoForDate.Name = "btnDoForDate"; + this.btnDoForDate.Size = new System.Drawing.Size(112, 36); + this.btnDoForDate.TabIndex = 18; + this.btnDoForDate.Text = "Проставить на дату"; + this.btnDoForDate.UseVisualStyleBackColor = true; + this.btnDoForDate.Click += new System.EventHandler(this.btnDoForDate_Click); + // // frmMain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(740, 320); + this.Controls.Add(this.btnDoForDate); + this.Controls.Add(this.dteForDay); this.Controls.Add(this.btnSettings); this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnMeeting); @@ -151,6 +176,8 @@ private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnSettings; private System.Windows.Forms.Timer tmrUpdate; + private System.Windows.Forms.DateTimePicker dteForDay; + private System.Windows.Forms.Button btnDoForDate; } } diff --git a/src/JiraTimeBot/UI/frmMain.cs b/src/JiraTimeBot/UI/frmMain.cs index b979f23..6e29bc0 100644 --- a/src/JiraTimeBot/UI/frmMain.cs +++ b/src/JiraTimeBot/UI/frmMain.cs @@ -216,38 +216,11 @@ namespace JiraTimeBot.UI _updateChecked = true; tmrUpdate.Interval = 1 * 60 * 60 * 1000; - var checker = new UpdateChecker(); - var release = checker.GetRelease(); - if (release == null) + var updater = _container.Resolve(); + if (updater.UpdateToNewVersion(firstTime)) { - return; - } - - var gitVersionStr = checker.GetLatestVersion(release); - var gitVersion = new Version(gitVersionStr); - var appVersion = new Version(Application.ProductVersion); - - if (gitVersion > appVersion) - { - var downloadUrl = checker.GetDownloadUrl(release); - _log.Info($"Доступна новая версия: {gitVersion} {downloadUrl}"); - - var success = checker.DownloadFile(downloadUrl, "update.zip"); - if (!success) - { - return; - } - - var result = InstallUpdate(gitVersion, "update.zip"); - if (result) - { - //если обновились - отключаем до рестарта - tmrUpdate.Enabled = false; - } - } - else if (firstTime) - { - _log.Info($"Вы используете актуальную версию."); + //если обновились - отключаем до рестарта + tmrUpdate.Enabled = false; } } catch (Exception ex) @@ -257,46 +230,17 @@ namespace JiraTimeBot.UI } } - private bool InstallUpdate(Version gitVersion, string updateFile) + private async void btnDoForDate_Click(object sender, EventArgs e) { - try + var settings = ReadSettingsAndLock(); + + using (_tokenSource = GetTokenSource()) { - _log.Info($"Устанавливаю обновление до версии {gitVersion}."); - //Rename he executing file - System.IO.FileInfo appFile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location); - var bkpDir = Path.Combine(appFile.DirectoryName, "bkp"); - if (!Directory.Exists(bkpDir)) - { - Directory.CreateDirectory(bkpDir); - } - - using (ZipArchive archive = ZipFile.OpenRead(Path.Combine(appFile.DirectoryName, updateFile))) - { - foreach (ZipArchiveEntry entry in archive.Entries) - { - var zipEntryPath = Path.Combine(appFile.DirectoryName, entry.FullName); - if (File.Exists(zipEntryPath)) - { - System.IO.FileInfo zipEntryFile = new System.IO.FileInfo(zipEntryPath); - if (File.Exists(bkpDir + "\\" + zipEntryFile.Name)) - { - File.Delete(bkpDir + "\\" + zipEntryFile.Name); - } - System.IO.File.Move(zipEntryFile.FullName, bkpDir + "\\" + zipEntryFile.Name); - } - - entry.ExtractToFile(zipEntryPath); - } - } - - _log.Info("Обновление установлено. Перезапустите программу для применения изменений."); - return true; - } - catch (Exception) - { - _log.Error($"Что-то пошло не так при обновлении."); - return false; + await _job.SetTaskTimesForDate(dteForDay.Value, dteForDay.Value, settings, _tasksProcessors.WorkLogTasksProcessor, txtDummyMode.Checked, _tokenSource.Token); } + + LockUnlock(true); + } } } diff --git a/src/JiraTimeBot/Update/IUpdater.cs b/src/JiraTimeBot/Update/IUpdater.cs new file mode 100644 index 0000000..014e8fa --- /dev/null +++ b/src/JiraTimeBot/Update/IUpdater.cs @@ -0,0 +1,9 @@ +using System; + +namespace JiraTimeBot.Update +{ + internal interface IUpdater + { + bool UpdateToNewVersion(bool firstTime); + } +} diff --git a/src/JiraTimeBot/Update/UpdateChecker.cs b/src/JiraTimeBot/Update/UpdateChecker.cs deleted file mode 100644 index f4df302..0000000 --- a/src/JiraTimeBot/Update/UpdateChecker.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Net; -using Newtonsoft.Json; - -namespace JiraTimeBot.Update -{ - class UpdateChecker - { - private const string GithubUser = "monster1025"; - private const string GithubRepo = "JiraTimeBot"; - - public string GetLatestVersion(GitHubReleaseResponse release) - { - var version = release?.TagName?.TrimStart('v'); - return version; - } - - public string GetDownloadUrl(GitHubReleaseResponse release) - { - var downloadUrl = release?.Assets?.FirstOrDefault()?.BrowserDownloadUrl?.ToString(); - return downloadUrl ?? ""; - } - - public GitHubReleaseResponse GetRelease() - { - var latestReleasePage = Get($"https://api.github.com/repos/{GithubUser}/{GithubRepo}/releases/latest"); - if (string.IsNullOrEmpty(latestReleasePage)) - { - return null; - } - - var release = JsonConvert.DeserializeObject(latestReleasePage); - return release; - } - - public bool DownloadFile(string url, string to) - { - try - { - - using (var client = new WebClient()) - { - client.Headers.Add("User-Agent", - "Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0"); - client.DownloadFile(url, to); - return true; - } - } - catch (Exception) - { - return false; - } - } - - private string Get(string uri) - { - try - { - HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); - request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - request.UserAgent = "Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0"; - - using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) - using (Stream stream = response.GetResponseStream()) - using (StreamReader reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } - catch (Exception) - { - return ""; - } - } - } -} diff --git a/src/JiraTimeBot/Update/Updater.cs b/src/JiraTimeBot/Update/Updater.cs new file mode 100644 index 0000000..c2c3516 --- /dev/null +++ b/src/JiraTimeBot/Update/Updater.cs @@ -0,0 +1,169 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Windows.Forms; +using Newtonsoft.Json; + +namespace JiraTimeBot.Update +{ + public class Updater : IUpdater + { + private readonly ILog _log; + + public Updater(ILog log) + { + _log = log; + } + + private const string GithubUser = "monster1025"; + private const string GithubRepo = "JiraTimeBot"; + + public string GetLatestVersion(GitHubReleaseResponse release) + { + var version = release?.TagName?.TrimStart('v'); + return version; + } + + public string GetDownloadUrl(GitHubReleaseResponse release) + { + var downloadUrl = release?.Assets?.FirstOrDefault()?.BrowserDownloadUrl?.ToString(); + return downloadUrl ?? ""; + } + + public GitHubReleaseResponse GetRelease() + { + var latestReleasePage = Get($"https://api.github.com/repos/{GithubUser}/{GithubRepo}/releases/latest"); + if (string.IsNullOrEmpty(latestReleasePage)) + { + return null; + } + + var release = JsonConvert.DeserializeObject(latestReleasePage); + return release; + } + + public bool UpdateToNewVersion(bool firstTime) + { + var release = GetRelease(); + if (release == null) + { + return false; + } + + var gitVersionStr = GetLatestVersion(release); + var gitVersion = new Version(gitVersionStr); + var appVersion = new Version(Application.ProductVersion); + + if (gitVersion > appVersion) + { + var downloadUrl = GetDownloadUrl(release); + _log.Info($"Доступна новая версия: {gitVersion} {downloadUrl}"); + + var success = DownloadFile(downloadUrl, "update.zip"); + if (!success) + { + return false; + } + + var result = InstallUpdate(gitVersion, "update.zip"); + if (result) + { + return true; + } + + return false; + } + + if (firstTime) + { + _log.Info($"Вы используете актуальную версию."); + } + return false; + } + + public bool InstallUpdate(Version gitVersion, string updateFile) + { + try + { + _log.Info($"Устанавливаю обновление до версии {gitVersion}."); + //Rename he executing file + System.IO.FileInfo appFile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location); + var bkpDir = Path.Combine(appFile.DirectoryName, "bkp"); + if (!Directory.Exists(bkpDir)) + { + Directory.CreateDirectory(bkpDir); + } + + using (ZipArchive archive = ZipFile.OpenRead(Path.Combine(appFile.DirectoryName, updateFile))) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + var zipEntryPath = Path.Combine(appFile.DirectoryName, entry.FullName); + if (File.Exists(zipEntryPath)) + { + System.IO.FileInfo zipEntryFile = new System.IO.FileInfo(zipEntryPath); + if (File.Exists(bkpDir + "\\" + zipEntryFile.Name)) + { + File.Delete(bkpDir + "\\" + zipEntryFile.Name); + } + System.IO.File.Move(zipEntryFile.FullName, bkpDir + "\\" + zipEntryFile.Name); + } + + entry.ExtractToFile(zipEntryPath); + } + } + + _log.Info("Обновление установлено. Перезапустите программу для применения изменений."); + return true; + } + catch (Exception) + { + _log.Error($"Что-то пошло не так при обновлении."); + return false; + } + } + + + private bool DownloadFile(string url, string to) + { + try + { + + using (var client = new WebClient()) + { + client.Headers.Add("User-Agent", + "Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0"); + client.DownloadFile(url, to); + return true; + } + } + catch (Exception) + { + return false; + } + } + + private string Get(string uri) + { + try + { + HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); + request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + request.UserAgent = "Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0"; + + using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) + using (Stream stream = response.GetResponseStream()) + using (StreamReader reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + catch (Exception) + { + return ""; + } + } + } +} \ No newline at end of file