feature: show commit gpg sign status (#614)
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions
Localization Check / localization-check (push) Waiting to run

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2024-10-29 21:03:45 +08:00
parent 5c92fbdb37
commit 279b1819a3
No known key found for this signature in database
7 changed files with 131 additions and 1 deletions

View file

@ -0,0 +1,30 @@
namespace SourceGit.Commands
{
public class QueryCommitSignInfo : Command
{
public QueryCommitSignInfo(string repo, string sha)
{
WorkingDirectory = repo;
Context = repo;
var allowedSignersFile = new Config(repo).Get("gpg.ssh.allowedSignersFile");
if (string.IsNullOrEmpty(allowedSignersFile))
Args = $"-c gpg.ssh.allowedSignersFile=/dev/null show --no-show-signature --pretty=format:\"%G? %GK\" -s {sha}";
else
Args = $"show --no-show-signature --pretty=format:\"%G? %GK\" -s {sha}";
}
public Models.CommitSignInfo Result()
{
var rs = ReadToEnd();
if (!rs.IsSuccess)
return null;
var raw = rs.StdOut.Trim();
if (raw.Length > 1)
return new Models.CommitSignInfo() { VerifyResult = raw[0], Key = raw.Substring(2) };
return null;
}
}
}

View file

@ -0,0 +1,58 @@
using Avalonia.Media;
namespace SourceGit.Models
{
public class CommitSignInfo
{
public string Key { get; set; } = string.Empty;
public char VerifyResult { get; set; } = 'N';
public IBrush Brush
{
get
{
switch (VerifyResult)
{
case 'G':
case 'U':
return Brushes.Green;
case 'X':
case 'Y':
case 'R':
return Brushes.DarkOrange;
case 'B':
case 'E':
return Brushes.Red;
default:
return Brushes.Transparent;
}
}
}
public string ToolTip
{
get
{
switch (VerifyResult)
{
case 'G':
return $"Good Signature.\n\nKey: {Key}";
case 'B':
return $"Bad Signature.\n\nKey: {Key}";
case 'U':
return $"Good Signature with unknown validity.\n\nKey: {Key}";
case 'X':
return $"Good Signature but has expired.\n\nKey: {Key}";
case 'Y':
return $"Good Signature made by expired key.\n\nKey: {Key}";
case 'R':
return $"Good signature made by a revoked key.\n\nKey: {Key}";
case 'E':
return $"Signature cannot be checked.\n\nKey: {Key}";
default:
return "No signature.";
}
}
}
}
}

View file

@ -121,6 +121,7 @@
<StreamGeometry x:Key="Icons.Undo">M762 1024C876 818 895 504 448 514V768L64 384l384-384v248c535-14 595 472 314 776z</StreamGeometry> <StreamGeometry x:Key="Icons.Undo">M762 1024C876 818 895 504 448 514V768L64 384l384-384v248c535-14 595 472 314 776z</StreamGeometry>
<StreamGeometry x:Key="Icons.Unlock">M832 464H332V240c0-31 25-56 56-56h248c31 0 56 25 56 56v68c0 4 4 8 8 8h56c4 0 8-4 8-8v-68c0-71-57-128-128-128H388c-71 0-128 57-128 128v224h-68c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32V496c0-18-14-32-32-32zM540 701v53c0 4-4 8-8 8h-40c-4 0-8-4-8-8v-53c-12-9-20-23-20-39 0-27 22-48 48-48s48 22 48 48c0 16-8 30-20 39z</StreamGeometry> <StreamGeometry x:Key="Icons.Unlock">M832 464H332V240c0-31 25-56 56-56h248c31 0 56 25 56 56v68c0 4 4 8 8 8h56c4 0 8-4 8-8v-68c0-71-57-128-128-128H388c-71 0-128 57-128 128v224h-68c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32V496c0-18-14-32-32-32zM540 701v53c0 4-4 8-8 8h-40c-4 0-8-4-8-8v-53c-12-9-20-23-20-39 0-27 22-48 48-48s48 22 48 48c0 16-8 30-20 39z</StreamGeometry>
<StreamGeometry x:Key="Icons.Up">M170 831l343-342L855 831l105-105-448-448L64 726 170 831z</StreamGeometry> <StreamGeometry x:Key="Icons.Up">M170 831l343-342L855 831l105-105-448-448L64 726 170 831z</StreamGeometry>
<StreamGeometry x:Key="Icons.Verified">M880 128A722 722 0 01555 13a77 77 0 00-85 0 719 719 0 01-325 115c-40 4-71 38-71 80v369c0 246 329 446 439 446 110 0 439-200 439-446V207c0-41-31-76-71-80zM465 692a36 36 0 01-53 0L305 579a42 42 0 010-57 36 36 0 0153 0l80 85L678 353a36 36 0 0153 0 42 42 0 01-0 57L465 692z</StreamGeometry>
<StreamGeometry x:Key="Icons.Waiting">M812 864h-29V654c0-21-11-40-28-52l-133-88 134-89c18-12 28-31 28-52V164h28c18 0 32-14 32-32s-14-32-32-32H212c-18 0-32 14-32 32s14 32 32 32h30v210c0 21 11 40 28 52l133 88-134 89c-18 12-28 31-28 52V864H212c-18 0-32 14-32 32s14 32 32 32h600c18 0 32-14 32-32s-14-32-32-32zM441 566c18-12 28-31 28-52s-11-40-28-52L306 373V164h414v209l-136 90c-18 12-28 31-28 52 0 21 11 40 28 52l135 89V695c-9-7-20-13-32-19-30-15-93-41-176-41-63 0-125 14-175 38-12 6-22 12-31 18v-36l136-90z</StreamGeometry> <StreamGeometry x:Key="Icons.Waiting">M812 864h-29V654c0-21-11-40-28-52l-133-88 134-89c18-12 28-31 28-52V164h28c18 0 32-14 32-32s-14-32-32-32H212c-18 0-32 14-32 32s14 32 32 32h30v210c0 21 11 40 28 52l133 88-134 89c-18 12-28 31-28 52V864H212c-18 0-32 14-32 32s14 32 32 32h600c18 0 32-14 32-32s-14-32-32-32zM441 566c18-12 28-31 28-52s-11-40-28-52L306 373V164h414v209l-136 90c-18 12-28 31-28 52 0 21 11 40 28 52l135 89V695c-9-7-20-13-32-19-30-15-93-41-176-41-63 0-125 14-175 38-12 6-22 12-31 18v-36l136-90z</StreamGeometry>
<StreamGeometry x:Key="Icons.Whitespace">M0 512M1024 512M512 0M512 1024M762 412v100h-500v-100h-150v200h800v-200h-150z</StreamGeometry> <StreamGeometry x:Key="Icons.Whitespace">M0 512M1024 512M512 0M512 1024M762 412v100h-500v-100h-150v200h800v-200h-150z</StreamGeometry>
<StreamGeometry x:Key="Icons.Window.Close">M519 459 222 162a37 37 0 10-52 52l297 297L169 809a37 37 0 1052 52l297-297 297 297a37 37 0 1052-52l-297-297 297-297a37 37 0 10-52-52L519 459z</StreamGeometry> <StreamGeometry x:Key="Icons.Window.Close">M519 459 222 162a37 37 0 10-52 52l297 297L169 809a37 37 0 1052 52l297-297 297 297a37 37 0 1052-52l-297-297 297-297a37 37 0 10-52-52L519 459z</StreamGeometry>

View file

@ -45,6 +45,12 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _fullMessage, value); private set => SetProperty(ref _fullMessage, value);
} }
public Models.CommitSignInfo SignInfo
{
get => _signInfo;
private set => SetProperty(ref _signInfo, value);
}
public List<Models.Change> Changes public List<Models.Change> Changes
{ {
get => _changes; get => _changes;
@ -131,6 +137,7 @@ namespace SourceGit.ViewModels
_visibleChanges.Clear(); _visibleChanges.Clear();
if (_selectedChanges != null) if (_selectedChanges != null)
_selectedChanges.Clear(); _selectedChanges.Clear();
_signInfo = null;
_searchChangeFilter = null; _searchChangeFilter = null;
_diffContext = null; _diffContext = null;
_viewRevisionFileContent = null; _viewRevisionFileContent = null;
@ -474,6 +481,7 @@ namespace SourceGit.ViewModels
{ {
_changes = null; _changes = null;
FullMessage = string.Empty; FullMessage = string.Empty;
SignInfo = null;
Changes = []; Changes = [];
VisibleChanges = null; VisibleChanges = null;
SelectedChanges = null; SelectedChanges = null;
@ -488,6 +496,12 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Invoke(() => FullMessage = fullMessage); Dispatcher.UIThread.Invoke(() => FullMessage = fullMessage);
}); });
Task.Run(() =>
{
var signInfo = new Commands.QueryCommitSignInfo(_repo.FullPath, _commit.SHA).Result();
Dispatcher.UIThread.Invoke(() => SignInfo = signInfo);
});
if (_cancelToken != null) if (_cancelToken != null)
_cancelToken.Requested = true; _cancelToken.Requested = true;
@ -637,6 +651,7 @@ namespace SourceGit.ViewModels
private int _activePageIndex = 0; private int _activePageIndex = 0;
private Models.Commit _commit = null; private Models.Commit _commit = null;
private string _fullMessage = string.Empty; private string _fullMessage = string.Empty;
private Models.CommitSignInfo _signInfo = null;
private List<Models.Change> _changes = null; private List<Models.Change> _changes = null;
private List<Models.Change> _visibleChanges = null; private List<Models.Change> _visibleChanges = null;
private List<Models.Change> _selectedChanges = null; private List<Models.Change> _selectedChanges = null;

View file

@ -60,6 +60,22 @@
Margin="12,0,4,0" Margin="12,0,4,0"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<ContentControl Content="{Binding #ThisControl.SignInfo}">
<ContentControl.Styles>
<Style Selector="ToolTip">
<Setter Property="MaxWidth" Value="800"/>
</Style>
</ContentControl.Styles>
<ContentControl.DataTemplates>
<DataTemplate DataType="m:CommitSignInfo">
<Border Width="24" Background="Transparent" ToolTip.Tip="{Binding ToolTip}">
<Path Width="14" Height="14" Data="{StaticResource Icons.Verified}" Fill="{Binding Brush}"/>
</Border>
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<Button Classes="icon_button" Width="24" Cursor="Hand" Click="OnCopyCommitSHA" ToolTip.Tip="{DynamicResource Text.Copy}"> <Button Classes="icon_button" Width="24" Cursor="Hand" Click="OnCopyCommitSHA" ToolTip.Tip="{DynamicResource Text.Copy}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Copy}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.Copy}"/>
</Button> </Button>

View file

@ -17,6 +17,15 @@ namespace SourceGit.Views
set => SetValue(MessageProperty, value); set => SetValue(MessageProperty, value);
} }
public static readonly StyledProperty<Models.CommitSignInfo> SignInfoProperty =
AvaloniaProperty.Register<CommitBaseInfo, Models.CommitSignInfo>(nameof(SignInfo));
public Models.CommitSignInfo SignInfo
{
get => GetValue(SignInfoProperty);
set => SetValue(SignInfoProperty, value);
}
public static readonly StyledProperty<bool> SupportsContainsInProperty = public static readonly StyledProperty<bool> SupportsContainsInProperty =
AvaloniaProperty.Register<CommitBaseInfo, bool>(nameof(SupportsContainsIn)); AvaloniaProperty.Register<CommitBaseInfo, bool>(nameof(SupportsContainsIn));

View file

@ -21,6 +21,7 @@
<!-- Base Information --> <!-- Base Information -->
<v:CommitBaseInfo Content="{Binding Commit}" <v:CommitBaseInfo Content="{Binding Commit}"
Message="{Binding FullMessage}" Message="{Binding FullMessage}"
SignInfo="{Binding SignInfo}"
SupportsContainsIn="True" SupportsContainsIn="True"
WebLinks="{Binding WebLinks}" WebLinks="{Binding WebLinks}"
IssueTrackerRules="{Binding IssueTrackerRules}"/> IssueTrackerRules="{Binding IssueTrackerRules}"/>