mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge branch 'development'
This commit is contained in:
commit
81b8f5bf48
@ -7,6 +7,8 @@ namespace Artemis.Core
|
||||
{
|
||||
private bool _valid;
|
||||
private bool _disposed;
|
||||
private SKRect _lastBounds;
|
||||
private SKRect _lastParentBounds;
|
||||
public SKBitmap? Bitmap { get; private set; }
|
||||
public SKCanvas? Canvas { get; private set; }
|
||||
public SKPaint? Paint { get; private set; }
|
||||
@ -26,6 +28,9 @@ namespace Artemis.Core
|
||||
if (IsOpen)
|
||||
throw new ArtemisCoreException("Cannot open render context because it is already open");
|
||||
|
||||
if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds))
|
||||
Invalidate();
|
||||
|
||||
if (!_valid || Canvas == null)
|
||||
{
|
||||
SKRect pathBounds = path.Bounds;
|
||||
@ -43,6 +48,8 @@ namespace Artemis.Core
|
||||
|
||||
Canvas.ClipPath(Path);
|
||||
|
||||
_lastParentBounds = parent?.Bounds ?? new SKRect();
|
||||
_lastBounds = path.Bounds;
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
@ -58,7 +65,7 @@ namespace Artemis.Core
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("Renderer");
|
||||
|
||||
|
||||
Canvas?.Restore();
|
||||
Paint?.Dispose();
|
||||
Paint = null;
|
||||
|
||||
@ -253,13 +253,13 @@ namespace Artemis.Core.Services
|
||||
PluginInfo pluginInfo = CoreJson.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile))!;
|
||||
|
||||
if (pluginInfo.Guid == Constants.CorePluginInfo.Guid)
|
||||
throw new ArtemisPluginException($"Plugin cannot use reserved GUID {pluginInfo.Guid}");
|
||||
throw new ArtemisPluginException($"Plugin {pluginInfo} cannot use reserved GUID {pluginInfo.Guid}");
|
||||
|
||||
lock (_plugins)
|
||||
{
|
||||
// Ensure the plugin is not already loaded
|
||||
if (_plugins.Any(p => p.Guid == pluginInfo.Guid))
|
||||
throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
|
||||
throw new ArtemisCoreException($"Cannot load plugin {pluginInfo} because it is using a GUID already used by another plugin");
|
||||
}
|
||||
|
||||
// Load the entity and fall back on creating a new one
|
||||
|
||||
@ -133,6 +133,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="9.3.0" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.0.1" />
|
||||
<PackageReference Include="gong-wpf-dragdrop" Version="2.3.2" />
|
||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf.NetCore" Version="1.0.14" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||
|
||||
@ -7,18 +7,39 @@
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<StackPanel Margin="16">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 0 0 20">
|
||||
Update available
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
Margin="0 20 0 20"
|
||||
TextWrapping="Wrap">
|
||||
A new Artemis update is available! 🥳<LineBreak/>
|
||||
You are currently running build <Run Text="{Binding CurrentBuild, Mode=OneWay}" /> while the latest build is <Run Text="{Binding LatestBuild, Mode=OneWay}" />. <LineBreak/> <LineBreak/>
|
||||
Updating Artemis will give you the latest bug(fixes), features and improvements.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 20">
|
||||
Changes
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Visibility="{Binding RetrievingChanges, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 10">
|
||||
Retrieving changes...
|
||||
</TextBlock>
|
||||
<ProgressBar IsIndeterminate="True" />
|
||||
</StackPanel>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Changes}" Visibility="{Binding RetrievingChanges, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Foreground="{DynamicResource MaterialDesignBodyLight}"></TextBlock>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 8 0 0">
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Focusable="False"
|
||||
|
||||
@ -1,31 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Services;
|
||||
using Artemis.UI.Services.Models.UpdateService;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Dialogs
|
||||
{
|
||||
public class UpdateDialogViewModel : DialogViewModelBase
|
||||
{
|
||||
private readonly JToken _buildInfo;
|
||||
private readonly DevOpsBuild _buildInfo;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private bool _canUpdate = true;
|
||||
private bool _retrievingChanges;
|
||||
|
||||
public UpdateDialogViewModel(JToken buildInfo, IUpdateService updateService, IDialogService dialogService)
|
||||
public UpdateDialogViewModel(DevOpsBuild buildInfo, IUpdateService updateService, IDialogService dialogService, IMessageService messageService)
|
||||
{
|
||||
_buildInfo = buildInfo;
|
||||
_updateService = updateService;
|
||||
_dialogService = dialogService;
|
||||
|
||||
_messageService = messageService;
|
||||
|
||||
CurrentBuild = Constants.BuildInfo.BuildNumberDisplay;
|
||||
LatestBuild = buildInfo?.SelectToken("value[0].buildNumber")?.Value<string>();
|
||||
LatestBuild = buildInfo.BuildNumber;
|
||||
Changes = new BindableCollection<string>();
|
||||
|
||||
Task.Run(GetBuildChanges);
|
||||
}
|
||||
|
||||
public string CurrentBuild { get; }
|
||||
public string LatestBuild { get; }
|
||||
public BindableCollection<string> Changes { get; }
|
||||
|
||||
public bool RetrievingChanges
|
||||
{
|
||||
get => _retrievingChanges;
|
||||
set => SetAndNotify(ref _retrievingChanges, value);
|
||||
}
|
||||
|
||||
public bool CanUpdate
|
||||
{
|
||||
@ -33,6 +50,41 @@ namespace Artemis.UI.Screens.Settings.Dialogs
|
||||
set => SetAndNotify(ref _canUpdate, value);
|
||||
}
|
||||
|
||||
private async Task GetBuildChanges()
|
||||
{
|
||||
try
|
||||
{
|
||||
RetrievingChanges = true;
|
||||
Task<DevOpsBuild> currentTask = _updateService.GetBuildInfo(1, CurrentBuild);
|
||||
Task<DevOpsBuild> latestTask = _updateService.GetBuildInfo(1, LatestBuild);
|
||||
|
||||
DevOpsBuild current = await currentTask;
|
||||
DevOpsBuild latest = await latestTask;
|
||||
|
||||
if (current != null && latest != null)
|
||||
{
|
||||
GitHubDifference difference = await _updateService.GetBuildDifferences(current, latest);
|
||||
|
||||
// Only take commits with one parents (no merges)
|
||||
Changes.Clear();
|
||||
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
|
||||
.SelectMany(c => c.Commit.Message.Split("\n"))
|
||||
.Select(m => m.Trim())
|
||||
.Where(m => !string.IsNullOrWhiteSpace(m))
|
||||
.OrderBy(m => m)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_messageService.ShowMessage($"Failed to retrieve build changes - {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
RetrievingChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Update()
|
||||
{
|
||||
try
|
||||
|
||||
277
src/Artemis.UI/Services/Models/UpdateService/DevOpsBuilds.cs
Normal file
277
src/Artemis.UI/Services/Models/UpdateService/DevOpsBuilds.cs
Normal file
@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.UI.Services.Models.UpdateService
|
||||
{
|
||||
public class DevOpsBuilds
|
||||
{
|
||||
[JsonProperty("count")]
|
||||
public long Count { get; set; }
|
||||
|
||||
[JsonProperty("value")]
|
||||
public List<DevOpsBuild> Builds { get; set; }
|
||||
}
|
||||
|
||||
public class DevOpsBuild
|
||||
{
|
||||
[JsonProperty("_links")]
|
||||
public BuildLinks Links { get; set; }
|
||||
|
||||
[JsonProperty("properties")]
|
||||
public Properties Properties { get; set; }
|
||||
|
||||
[JsonProperty("tags")]
|
||||
public List<object> Tags { get; set; }
|
||||
|
||||
[JsonProperty("validationResults")]
|
||||
public List<object> ValidationResults { get; set; }
|
||||
|
||||
[JsonProperty("plans")]
|
||||
public List<Plan> Plans { get; set; }
|
||||
|
||||
[JsonProperty("triggerInfo")]
|
||||
public TriggerInfo TriggerInfo { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("buildNumber")]
|
||||
public string BuildNumber { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("result")]
|
||||
public string Result { get; set; }
|
||||
|
||||
[JsonProperty("queueTime")]
|
||||
public DateTimeOffset QueueTime { get; set; }
|
||||
|
||||
[JsonProperty("startTime")]
|
||||
public DateTimeOffset StartTime { get; set; }
|
||||
|
||||
[JsonProperty("finishTime")]
|
||||
public DateTimeOffset FinishTime { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("definition")]
|
||||
public Definition Definition { get; set; }
|
||||
|
||||
[JsonProperty("buildNumberRevision")]
|
||||
public long BuildNumberRevision { get; set; }
|
||||
|
||||
[JsonProperty("project")]
|
||||
public Project Project { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("sourceBranch")]
|
||||
public string SourceBranch { get; set; }
|
||||
|
||||
[JsonProperty("sourceVersion")]
|
||||
public string SourceVersion { get; set; }
|
||||
|
||||
[JsonProperty("priority")]
|
||||
public string Priority { get; set; }
|
||||
|
||||
[JsonProperty("reason")]
|
||||
public string Reason { get; set; }
|
||||
|
||||
[JsonProperty("requestedFor")]
|
||||
public LastChangedBy RequestedFor { get; set; }
|
||||
|
||||
[JsonProperty("requestedBy")]
|
||||
public LastChangedBy RequestedBy { get; set; }
|
||||
|
||||
[JsonProperty("lastChangedDate")]
|
||||
public DateTimeOffset LastChangedDate { get; set; }
|
||||
|
||||
[JsonProperty("lastChangedBy")]
|
||||
public LastChangedBy LastChangedBy { get; set; }
|
||||
|
||||
[JsonProperty("orchestrationPlan")]
|
||||
public Plan OrchestrationPlan { get; set; }
|
||||
|
||||
[JsonProperty("logs")]
|
||||
public Logs Logs { get; set; }
|
||||
|
||||
[JsonProperty("repository")]
|
||||
public Repository Repository { get; set; }
|
||||
|
||||
[JsonProperty("keepForever")]
|
||||
public bool KeepForever { get; set; }
|
||||
|
||||
[JsonProperty("retainedByRelease")]
|
||||
public bool RetainedByRelease { get; set; }
|
||||
|
||||
[JsonProperty("triggeredByBuild")]
|
||||
public object TriggeredByBuild { get; set; }
|
||||
}
|
||||
|
||||
public class Definition
|
||||
{
|
||||
[JsonProperty("drafts")]
|
||||
public List<object> Drafts { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("path")]
|
||||
public string Path { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("queueStatus")]
|
||||
public string QueueStatus { get; set; }
|
||||
|
||||
[JsonProperty("revision")]
|
||||
public long Revision { get; set; }
|
||||
|
||||
[JsonProperty("project")]
|
||||
public Project Project { get; set; }
|
||||
}
|
||||
|
||||
public class Project
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("state")]
|
||||
public string State { get; set; }
|
||||
|
||||
[JsonProperty("revision")]
|
||||
public long Revision { get; set; }
|
||||
|
||||
[JsonProperty("visibility")]
|
||||
public string Visibility { get; set; }
|
||||
|
||||
[JsonProperty("lastUpdateTime")]
|
||||
public DateTimeOffset LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
public class LastChangedBy
|
||||
{
|
||||
[JsonProperty("displayName")]
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("_links")]
|
||||
public LastChangedByLinks Links { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[JsonProperty("uniqueName")]
|
||||
public object UniqueName { get; set; }
|
||||
|
||||
[JsonProperty("imageUrl")]
|
||||
public object ImageUrl { get; set; }
|
||||
|
||||
[JsonProperty("descriptor")]
|
||||
public string Descriptor { get; set; }
|
||||
}
|
||||
|
||||
public class LastChangedByLinks
|
||||
{
|
||||
[JsonProperty("avatar")]
|
||||
public Badge Avatar { get; set; }
|
||||
}
|
||||
|
||||
public class Badge
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public Uri Href { get; set; }
|
||||
}
|
||||
|
||||
public class BuildLinks
|
||||
{
|
||||
[JsonProperty("self")]
|
||||
public Badge Self { get; set; }
|
||||
|
||||
[JsonProperty("web")]
|
||||
public Badge Web { get; set; }
|
||||
|
||||
[JsonProperty("sourceVersionDisplayUri")]
|
||||
public Badge SourceVersionDisplayUri { get; set; }
|
||||
|
||||
[JsonProperty("timeline")]
|
||||
public Badge Timeline { get; set; }
|
||||
|
||||
[JsonProperty("badge")]
|
||||
public Badge Badge { get; set; }
|
||||
}
|
||||
|
||||
public class Logs
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
}
|
||||
|
||||
public class Plan
|
||||
{
|
||||
[JsonProperty("planId")]
|
||||
public Guid PlanId { get; set; }
|
||||
}
|
||||
|
||||
public class Properties
|
||||
{
|
||||
}
|
||||
|
||||
public class Repository
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("clean")]
|
||||
public object Clean { get; set; }
|
||||
|
||||
[JsonProperty("checkoutSubmodules")]
|
||||
public bool CheckoutSubmodules { get; set; }
|
||||
}
|
||||
|
||||
public class TriggerInfo
|
||||
{
|
||||
[JsonProperty("ci.sourceBranch")]
|
||||
public string CiSourceBranch { get; set; }
|
||||
|
||||
[JsonProperty("ci.sourceSha")]
|
||||
public string CiSourceSha { get; set; }
|
||||
|
||||
[JsonProperty("ci.message")]
|
||||
public string CiMessage { get; set; }
|
||||
|
||||
[JsonProperty("ci.triggerRepository")]
|
||||
public string CiTriggerRepository { get; set; }
|
||||
}
|
||||
}
|
||||
243
src/Artemis.UI/Services/Models/UpdateService/GitHubDifference.cs
Normal file
243
src/Artemis.UI/Services/Models/UpdateService/GitHubDifference.cs
Normal file
@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.UI.Services.Models.UpdateService
|
||||
{
|
||||
public class GitHubDifference
|
||||
{
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("permalink_url")]
|
||||
public Uri PermalinkUrl { get; set; }
|
||||
|
||||
[JsonProperty("diff_url")]
|
||||
public Uri DiffUrl { get; set; }
|
||||
|
||||
[JsonProperty("patch_url")]
|
||||
public Uri PatchUrl { get; set; }
|
||||
|
||||
[JsonProperty("base_commit")]
|
||||
public BaseCommitClass BaseCommit { get; set; }
|
||||
|
||||
[JsonProperty("merge_base_commit")]
|
||||
public BaseCommitClass MergeBaseCommit { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("ahead_by")]
|
||||
public long AheadBy { get; set; }
|
||||
|
||||
[JsonProperty("behind_by")]
|
||||
public long BehindBy { get; set; }
|
||||
|
||||
[JsonProperty("total_commits")]
|
||||
public long TotalCommits { get; set; }
|
||||
|
||||
[JsonProperty("commits")]
|
||||
public List<BaseCommitClass> Commits { get; set; }
|
||||
|
||||
[JsonProperty("files")]
|
||||
public List<File> Files { get; set; }
|
||||
}
|
||||
|
||||
public class BaseCommitClass
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId { get; set; }
|
||||
|
||||
[JsonProperty("commit")]
|
||||
public BaseCommitCommit Commit { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("comments_url")]
|
||||
public Uri CommentsUrl { get; set; }
|
||||
|
||||
[JsonProperty("author")]
|
||||
public BaseCommitAuthor Author { get; set; }
|
||||
|
||||
[JsonProperty("committer")]
|
||||
public BaseCommitAuthor Committer { get; set; }
|
||||
|
||||
[JsonProperty("parents")]
|
||||
public List<Parent> Parents { get; set; }
|
||||
}
|
||||
|
||||
public class BaseCommitAuthor
|
||||
{
|
||||
[JsonProperty("login")]
|
||||
public string Login { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId { get; set; }
|
||||
|
||||
[JsonProperty("avatar_url")]
|
||||
public Uri AvatarUrl { get; set; }
|
||||
|
||||
[JsonProperty("gravatar_id")]
|
||||
public string GravatarId { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("followers_url")]
|
||||
public Uri FollowersUrl { get; set; }
|
||||
|
||||
[JsonProperty("following_url")]
|
||||
public string FollowingUrl { get; set; }
|
||||
|
||||
[JsonProperty("gists_url")]
|
||||
public string GistsUrl { get; set; }
|
||||
|
||||
[JsonProperty("starred_url")]
|
||||
public string StarredUrl { get; set; }
|
||||
|
||||
[JsonProperty("subscriptions_url")]
|
||||
public Uri SubscriptionsUrl { get; set; }
|
||||
|
||||
[JsonProperty("organizations_url")]
|
||||
public Uri OrganizationsUrl { get; set; }
|
||||
|
||||
[JsonProperty("repos_url")]
|
||||
public Uri ReposUrl { get; set; }
|
||||
|
||||
[JsonProperty("events_url")]
|
||||
public string EventsUrl { get; set; }
|
||||
|
||||
[JsonProperty("received_events_url")]
|
||||
public Uri ReceivedEventsUrl { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("site_admin")]
|
||||
public bool SiteAdmin { get; set; }
|
||||
}
|
||||
|
||||
public class BaseCommitCommit
|
||||
{
|
||||
[JsonProperty("author")]
|
||||
public PurpleAuthor Author { get; set; }
|
||||
|
||||
[JsonProperty("committer")]
|
||||
public PurpleAuthor Committer { get; set; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
[JsonProperty("tree")]
|
||||
public Tree Tree { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("comment_count")]
|
||||
public long CommentCount { get; set; }
|
||||
|
||||
[JsonProperty("verification")]
|
||||
public Verification Verification { get; set; }
|
||||
}
|
||||
|
||||
public class PurpleAuthor
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("date")]
|
||||
public DateTimeOffset Date { get; set; }
|
||||
}
|
||||
|
||||
public class Tree
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
}
|
||||
|
||||
public class Verification
|
||||
{
|
||||
[JsonProperty("verified")]
|
||||
public bool Verified { get; set; }
|
||||
|
||||
[JsonProperty("reason")]
|
||||
public string Reason { get; set; }
|
||||
|
||||
[JsonProperty("signature")]
|
||||
public string Signature { get; set; }
|
||||
|
||||
[JsonProperty("payload")]
|
||||
public string Payload { get; set; }
|
||||
}
|
||||
|
||||
public class Parent
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
}
|
||||
|
||||
public class File
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("filename")]
|
||||
public string Filename { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("additions")]
|
||||
public long Additions { get; set; }
|
||||
|
||||
[JsonProperty("deletions")]
|
||||
public long Deletions { get; set; }
|
||||
|
||||
[JsonProperty("changes")]
|
||||
public long Changes { get; set; }
|
||||
|
||||
[JsonProperty("blob_url")]
|
||||
public Uri BlobUrl { get; set; }
|
||||
|
||||
[JsonProperty("raw_url")]
|
||||
public Uri RawUrl { get; set; }
|
||||
|
||||
[JsonProperty("contents_url")]
|
||||
public Uri ContentsUrl { get; set; }
|
||||
|
||||
[JsonProperty("patch")]
|
||||
public string Patch { get; set; }
|
||||
|
||||
[JsonProperty("previous_filename", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string PreviousFilename { get; set; }
|
||||
}
|
||||
}
|
||||
@ -4,16 +4,21 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Screens.Settings.Dialogs;
|
||||
using Artemis.UI.Services.Models.UpdateService;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Flurl;
|
||||
using Flurl.Http;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serilog;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Artemis.UI.Services
|
||||
{
|
||||
@ -53,12 +58,8 @@ namespace Artemis.UI.Services
|
||||
{
|
||||
_logger.Information("Checking for updates");
|
||||
|
||||
JToken buildInfo = await GetBuildInfo(1);
|
||||
JToken buildNumberToken = buildInfo?.SelectToken("value[0].buildNumber");
|
||||
|
||||
if (buildNumberToken == null)
|
||||
throw new ArtemisUIException("Failed to find build number at \"value[0].buildNumber\"");
|
||||
double buildNumber = buildNumberToken.Value<double>();
|
||||
DevOpsBuild buildInfo = await GetBuildInfo(1);
|
||||
double buildNumber = double.Parse(buildInfo.BuildNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
string buildNumberDisplay = buildNumber.ToString(CultureInfo.InvariantCulture);
|
||||
_logger.Information("Latest build is {buildNumber}, we're running {localBuildNumber}", buildNumberDisplay, Constants.BuildInfo.BuildNumberDisplay);
|
||||
@ -91,21 +92,16 @@ namespace Artemis.UI.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task OfferUpdate(JToken buildInfo)
|
||||
private async Task OfferUpdate(DevOpsBuild buildInfo)
|
||||
{
|
||||
await _dialogService.ShowDialog<UpdateDialogViewModel>(new Dictionary<string, object> {{"buildInfo", buildInfo}});
|
||||
}
|
||||
|
||||
public async Task<bool> IsUpdateAvailable()
|
||||
{
|
||||
JToken buildInfo = await GetBuildInfo(1);
|
||||
JToken buildNumberToken = buildInfo?.SelectToken("value[0].buildNumber");
|
||||
|
||||
if (buildNumberToken != null)
|
||||
return buildNumberToken.Value<double>() > Constants.BuildInfo.BuildNumber;
|
||||
|
||||
_logger.Warning("IsUpdateAvailable: Failed to find build number at \"value[0].buildNumber\"");
|
||||
return false;
|
||||
DevOpsBuild buildInfo = await GetBuildInfo(1);
|
||||
double buildNumber = double.Parse(buildInfo.BuildNumber, CultureInfo.InvariantCulture);
|
||||
return buildNumber > Constants.BuildInfo.BuildNumber;
|
||||
}
|
||||
|
||||
public async Task ApplyUpdate()
|
||||
@ -113,8 +109,7 @@ namespace Artemis.UI.Services
|
||||
_logger.Information("ApplyUpdate: Applying update");
|
||||
|
||||
// Ensure the installer is up-to-date, get installer build info
|
||||
JToken buildInfo = await GetBuildInfo(6);
|
||||
JToken finishTimeToken = buildInfo?.SelectToken("value[0].finishTime");
|
||||
DevOpsBuild buildInfo = await GetBuildInfo(6);
|
||||
string installerPath = Path.Combine(Constants.ApplicationFolder, "Installer", "Artemis.Installer.exe");
|
||||
|
||||
// Always update installer if it is missing ^^
|
||||
@ -123,9 +118,7 @@ namespace Artemis.UI.Services
|
||||
// Compare the creation date of the installer with the build date and update if needed
|
||||
else
|
||||
{
|
||||
if (finishTimeToken == null)
|
||||
_logger.Warning("ApplyUpdate: Failed to find build finish time at \"value[0].finishTime\", not updating the installer.");
|
||||
else if (File.GetLastWriteTime(installerPath) < finishTimeToken.Value<DateTime>())
|
||||
if (File.GetLastWriteTime(installerPath) < buildInfo.FinishTime)
|
||||
await UpdateInstaller();
|
||||
}
|
||||
|
||||
@ -146,7 +139,47 @@ namespace Artemis.UI.Services
|
||||
else
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<DevOpsBuild> GetBuildInfo(int buildDefinition, string buildNumber = null)
|
||||
{
|
||||
Url request = ApiUrl.AppendPathSegments("build", "builds")
|
||||
.SetQueryParam("definitions", buildDefinition)
|
||||
.SetQueryParam("resultFilter", "succeeded")
|
||||
.SetQueryParam("$top", 1)
|
||||
.SetQueryParam("api-version", "6.1-preview.6");
|
||||
|
||||
if (buildNumber != null)
|
||||
request = request.SetQueryParam("buildNumber", buildNumber);
|
||||
|
||||
try
|
||||
{
|
||||
DevOpsBuilds result = await request.GetJsonAsync<DevOpsBuilds>();
|
||||
try
|
||||
{
|
||||
return result.Builds.FirstOrDefault();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (FlurlHttpException e)
|
||||
{
|
||||
_logger.Warning("GetBuildInfo: Getting build info, request returned {statusCode}", e.StatusCode);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<GitHubDifference> GetBuildDifferences(DevOpsBuild a, DevOpsBuild b)
|
||||
{
|
||||
return await "https://api.github.com"
|
||||
.AppendPathSegments("repos", "Artemis-RGB", "Artemis", "compare")
|
||||
.AppendPathSegment(a.SourceVersion + "..." + b.SourceVersion)
|
||||
.WithHeader("User-Agent", "Artemis 2")
|
||||
.WithHeader("Accept", "application/vnd.github.v3+json")
|
||||
.GetJsonAsync<GitHubDifference>();
|
||||
}
|
||||
|
||||
private async Task UpdateInstaller()
|
||||
@ -170,35 +203,6 @@ namespace Artemis.UI.Services
|
||||
await httpResponseMessage.Content.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
private async Task<JObject> GetBuildInfo(int buildDefinition)
|
||||
{
|
||||
string latestBuildUrl = ApiUrl + $"build/builds?definitions={buildDefinition}&resultFilter=succeeded&$top=1&api-version=6.1-preview.6";
|
||||
_logger.Debug("GetBuildInfo: Getting build info from {latestBuildUrl}", latestBuildUrl);
|
||||
|
||||
// Make the request
|
||||
using HttpClient client = new();
|
||||
HttpResponseMessage httpResponseMessage = await client.GetAsync(latestBuildUrl);
|
||||
|
||||
// Ensure it returned correctly
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.Warning("GetBuildInfo: Getting build info, request returned {statusCode}", httpResponseMessage.StatusCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse the response
|
||||
string response = await httpResponseMessage.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
return JObject.Parse(response);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void CheckForUpdatesOnSettingChanged(object sender, EventArgs e)
|
||||
@ -225,6 +229,8 @@ namespace Artemis.UI.Services
|
||||
|
||||
Task<bool> OfferUpdateIfFound();
|
||||
Task<bool> IsUpdateAvailable();
|
||||
Task<DevOpsBuild> GetBuildInfo(int buildDefinition, string buildNumber = null);
|
||||
Task<GitHubDifference> GetBuildDifferences(DevOpsBuild a, DevOpsBuild b);
|
||||
Task ApplyUpdate();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"BuildId": 0,
|
||||
"BuildNumber": 13370101.1,
|
||||
"SourceBranch": "local",
|
||||
"SourceVersion": "local"
|
||||
"BuildId": 442,
|
||||
"BuildNumber": 20210130.3,
|
||||
"SourceBranch": "refs/heads/master",
|
||||
"SourceVersion": "b29ab064ae46d93141f31afaa8ee3ea9063c6263"
|
||||
}
|
||||
@ -8,6 +8,17 @@
|
||||
"resolved": "9.3.0",
|
||||
"contentHash": "C44l6Ih+YwpED/TsXfl6LIq6Z4wLXahstnr6T70uUg1Hs7/bLBKKAo9Nl0sLhVjDE8TA+fF+O3IM4nDrwabcSQ=="
|
||||
},
|
||||
"Flurl.Http": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.1, )",
|
||||
"resolved": "3.0.1",
|
||||
"contentHash": "wt4RrmYOPWu8v0sjuuSfhYBFzjfrCz4nJP48Kw2HFGssq0U6pMBPFHcL3zbsBFALJipAjgRET5mf+0Xz4ZnZpQ==",
|
||||
"dependencies": {
|
||||
"Flurl": "3.0.1",
|
||||
"Newtonsoft.Json": "12.0.2",
|
||||
"System.Text.Encoding.CodePages": "4.5.1"
|
||||
}
|
||||
},
|
||||
"gong-wpf-dragdrop": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.3.2, )",
|
||||
@ -209,6 +220,11 @@
|
||||
"Unosquare.Swan.Lite": "3.0.0"
|
||||
}
|
||||
},
|
||||
"Flurl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.1",
|
||||
"contentHash": "i7CuPSikVroBaWG8sPvO707Ex9C6BP5+r4JufKNU1FGMmiFgLJvNo1ttUg6ZiXIzUNknvIb1VUTIO9iEDucibg=="
|
||||
},
|
||||
"HidSharp": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.0",
|
||||
@ -1029,6 +1045,11 @@
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.2",
|
||||
"contentHash": "wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ=="
|
||||
},
|
||||
"System.Runtime.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1255,6 +1276,15 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "4J2JQXbftjPMppIHJ7IC+VXQ9XfEagN92vZZNoG12i+zReYlim5dMoXFC1Zzg7tsnKDM7JPo5bYfFK4Jheq44w==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.2",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.2"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user