Skip to content

Commit

Permalink
Merge pull request #21 from timheuer/secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
timheuer authored Jul 21, 2023
2 parents 6df53cf + b44c0f2 commit 1589abb
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 10 deletions.
22 changes: 14 additions & 8 deletions src/GitHubActionsVS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,20 @@
<Compile Include="ToolWindows\MessagePayload.cs" />
<Compile Include="ToolWindows\MessageCommand.cs" />
<Compile Include="ToolWindows\ToolWindowMessenger.cs" />
<Compile Include="UserControls\AddEditSecret.xaml.cs">
<DependentUpon>AddEditSecret.xaml</DependentUpon>
</Compile>
<Compile Include="VSCommandTable.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>VSCommandTable.vsct</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Content Include="libsodium.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Resource Include="Resources\codicon.ttf" />
<Content Include="LICENSE">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Expand All @@ -81,18 +88,10 @@
<SubType>Designer</SubType>
<Generator>VsixManifestGenerator</Generator>
</None>
<Content Include="lib\win32\arm64\git2-e632535.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Content Include="lib\win32\x64\git2-e632535.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="lib\win32\x86\git2-e632535.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Icon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
Expand All @@ -111,6 +110,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControls\AddEditSecret.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
Expand Down Expand Up @@ -150,6 +153,9 @@
<PackageReference Include="Octokit">
<Version>7.0.1</Version>
</PackageReference>
<PackageReference Include="Sodium.Core">
<Version>1.3.3</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
Expand Down
1 change: 1 addition & 0 deletions src/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
[assembly: ComVisible(false)]

[assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\LibGit2Sharp.dll")]
[assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\Sodium.Core.dll")]

namespace System.Runtime.CompilerServices;
public class IsExternalInit { }
24 changes: 23 additions & 1 deletion src/ToolWindows/GHActionsToolWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,29 @@
<Expander Header="Settings">
<TreeView BorderThickness="0">
<TreeViewItem Header="Secrets" HeaderTemplate="{DynamicResource SecretsHeaderTemplate}">
<TreeViewItem Header="Repository Secrets" x:Name="tvSecrets"/>
<TreeViewItem x:Name="tvSecrets">
<TreeViewItem.Header>
<TextBlock Text="Repository Secrets">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Secret" Click="AddSecret_Click" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit Secret" Click="EditSecret_Click" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}}" />
<MenuItem Header="Delete Secret" Click="DeleteSecret_Click" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeViewItem>
</TreeView>
</Expander>
Expand Down
94 changes: 93 additions & 1 deletion src/ToolWindows/GHActionsToolWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using GitHubActionsVS.UserControls;
using Application = System.Windows.Application;
using System.Windows.Media;
using MessageBox = Community.VisualStudio.Toolkit.MessageBox;

namespace GitHubActionsVS;

Expand Down Expand Up @@ -65,6 +69,12 @@ public async Task GetRepoInfoAsync()

// find the git folder
var solution = await VS.Solutions.GetCurrentSolutionAsync();
if (solution is null)
{
Debug.WriteLine("No solution found");
ShowInfoMessage("No project or solution loaded");
return;
}
var projectPath = solution?.FullPath;

_repoInfo.FindGitFolder(projectPath, out string gitPath);
Expand Down Expand Up @@ -135,7 +145,7 @@ private async Task LoadDataAsync()
if (runs.TotalCount > 0)
{
// creating simplified model of the GH info for the treeview

// iterate throught the runs
foreach (var run in runs.WorkflowRuns)
{
Expand Down Expand Up @@ -257,5 +267,87 @@ private void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
parent.RaiseEvent(eventArg);
}
}

private async void AddSecret_Click(object sender, RoutedEventArgs e)
{
await UpsertRepositorySecret(string.Empty);
}

private async void EditSecret_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = (MenuItem)sender;
TextBlock tvi = GetParentTreeViewItem(menuItem);
if (tvi is not null)
{
string header = tvi.Text.ToString();
string secretName = header.Substring(0, header.IndexOf(" ("));
await UpsertRepositorySecret(secretName);
}
}

private TextBlock GetParentTreeViewItem(MenuItem menuItem)
{
var contextMenu = menuItem.CommandParameter as ContextMenu;
if (contextMenu is not null)
{
var treeViewItem = contextMenu.PlacementTarget as TextBlock;
if (treeViewItem is not null)
{
return treeViewItem;
}
}
return null;
}

private async void DeleteSecret_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = (MenuItem)sender;
TextBlock tvi = GetParentTreeViewItem(menuItem);

if (tvi is not null)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
// confirm the delete first
int result = VsShellUtilities.ShowMessageBox(ServiceProvider.GlobalProvider, "Are you sure you want to delete this secret?", "Confirm Delete", Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_QUERY, Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_YESNOCANCEL, Microsoft.VisualStudio.Shell.Interop.OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_THIRD);

var confirmResult = (MessageBoxResult)result;

if (confirmResult == MessageBoxResult.Yes)
{
string header = tvi.Text.ToString();
string secretName = header.Substring(0, header.IndexOf(" ("));

GitHubClient client = GetGitHubClient();
await client.Repository.Actions.Secrets.Delete(_repoInfo.RepoOwner, _repoInfo.RepoName, secretName);
await RefreshSecretsAsync(client);
}
}
}

private async Task UpsertRepositorySecret(string secretName)
{
AddEditSecret addEditSecret = new AddEditSecret(secretName)
{
Owner = Application.Current.MainWindow
};
bool? result = addEditSecret.ShowDialog();
if (result == true)
{
GitHubClient client = GetGitHubClient();
var pubKey = await client.Repository.Actions.Secrets.GetPublicKey(_repoInfo.RepoOwner, _repoInfo.RepoName);

UpsertRepositorySecret encryptedSecret = new UpsertRepositorySecret();
if (pubKey != null)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(addEditSecret.SecretValue);
var key = Convert.FromBase64String(pubKey.Key);
var sealedKeyBox = Sodium.SealedPublicKeyBox.Create(bytes, key);
encryptedSecret.KeyId = pubKey.KeyId;
encryptedSecret.EncryptedValue = Convert.ToBase64String(sealedKeyBox);
_ = await client.Repository.Actions.Secrets.CreateOrUpdate(_repoInfo.RepoOwner, _repoInfo.RepoName, addEditSecret.SecretName, encryptedSecret);
}
await RefreshSecretsAsync(client);
}
}
}

28 changes: 28 additions & 0 deletions src/UserControls/AddEditSecret.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Window x:Class="GitHubActionsVS.UserControls.AddEditSecret"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GitHubActionsVS.Helpers"
Title="Add/Edit Secret" Height="220" Width="500" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
toolkit:Themes.UseVsTheme="True" Icon="{x:Null}">
<Grid Margin="0,10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Name:" HorizontalAlignment="Right" VerticalAlignment="Top" FontWeight="SemiBold" />
<TextBox Margin="0,0,10,0" Grid.Row="0" Grid.Column="1" Name="txtName" VerticalAlignment="Top" HorizontalAlignment="Stretch" MinWidth="150"/>
<Label Margin="0,5,0,0" Grid.Row="1" Grid.Column="0" Content="Secret:" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="SemiBold" />
<TextBox Margin="0,5,10,0" Grid.Row="1" Grid.Column="1" Name="txtSecret" AcceptsReturn="True" VerticalContentAlignment="Top" Height="100" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" MinWidth="210" />

<StackPanel Grid.Row="2" Grid.Column="2" HorizontalAlignment="Right" Orientation="Horizontal" Margin="0,5,0,0">
<Button Content="Save" Margin="10,0,0,0" VerticalAlignment="Center" Width="75" Height="23" Name="btnCreate" Click="Save_Click" />
<Button Content="Cancel" Margin="10,0" VerticalAlignment="Center" Width="75" Height="23" IsDefault="True" Name="btnCancel" Click="Cancel_Click" />
</StackPanel>
</Grid>
</Window>
43 changes: 43 additions & 0 deletions src/UserControls/AddEditSecret.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Text.RegularExpressions;
using System.Windows;

namespace GitHubActionsVS.UserControls;
/// <summary>
/// Interaction logic for AddEditSecret.xaml
/// </summary>
public partial class AddEditSecret : Window
{
public AddEditSecret(string secretName)
{
InitializeComponent();
txtName.Text = secretName;
txtName.IsEnabled = string.IsNullOrWhiteSpace(secretName);
Title = string.IsNullOrWhiteSpace(secretName) ? "Add Secret" : "Edit Secret";
btnCreate.Content = string.IsNullOrWhiteSpace(secretName) ? "Create" : "Update";
}

public string SecretName => txtName.Text.Trim();
public string SecretValue => txtSecret.Text.Trim();

private void Save_Click(object sender, RoutedEventArgs e)
{
// Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. Must start with a letter ([a-z], [A-Z]) or underscores (_).
Regex rx = new Regex("^[a-zA-Z_][a-zA-Z0-9_]*$");
if (rx.IsMatch(txtName.Text))
{
DialogResult = true;
Close();
}
else
{
Community.VisualStudio.Toolkit.MessageBox mb = new();
mb.ShowError("Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. Must start with a letter ([a-z], [A-Z]) or underscores (_).", "Invalid Secret Name");
}
}

private void Cancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
}
Binary file removed src/lib/win32/arm64/git2-e632535.dll
Binary file not shown.
Binary file removed src/lib/win32/x86/git2-e632535.dll
Binary file not shown.
Binary file added src/libsodium.dll
Binary file not shown.

0 comments on commit 1589abb

Please sign in to comment.