Skip to content

Commit

Permalink
Fix UI block during scan and cancel auto scan when loading the extens…
Browse files Browse the repository at this point in the history
…ion tool panel (#52)
  • Loading branch information
kerenr-jfrog authored Jan 13, 2025
1 parent 2a5a786 commit c256bc7
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-vsix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
ref: ${{ inputs.ref }}

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1
uses: microsoft/setup-msbuild@v2
with:
vs-version: 'latest'

Expand Down
10 changes: 5 additions & 5 deletions JFrogVSExtension/JFrogVSExtension.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>2</WarningLevel>
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
Expand Down Expand Up @@ -238,18 +238,18 @@
<Version>17.1.32210.191</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Interop">
<Version>17.1.32210.191</Version>
<Version>17.12.40391</Version>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.SDK.Analyzers">
<Version>16.10.10</Version>
<Version>17.7.47</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Shell.15.0">
<Version>17.1.32210.191</Version>
</PackageReference>
<PackageReference Include="Microsoft.VSSDK.BuildTools">
<Version>17.1.4054</Version>
<Version>17.12.2069</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
3 changes: 1 addition & 2 deletions JFrogVSExtension/MainPanelControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,13 @@
</StackPanel>
</Border>
</Popup>

</WrapPanel>
</DockPanel>
</Border>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="150"/>
<ColumnDefinition Width="*" MinWidth="250"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="2*" MinWidth="300"/>
</Grid.ColumnDefinitions>
Expand Down
8 changes: 2 additions & 6 deletions JFrogVSExtension/MainPanelControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,12 @@ private void Details_Loaded(object sender, RoutedEventArgs e)
{
}

#pragma warning disable VSTHRD100 // Avoid async void methods - Signature expected by event handler.
private async void Tree_Loaded(object sender, RoutedEventArgs e)
#pragma warning restore VSTHRD100 // Avoid async void methods
private void Tree_Loaded(object sender, RoutedEventArgs e)
{
if (isAllFilterChecked)
{
InitCheckbox();
}
await ((MainViewModel)this.DataContext).LoadAsync();
}

private void InitCheckbox()
Expand All @@ -151,9 +148,8 @@ public async Task LoadAsync()
{
if (isAllFilterChecked)
{
InitCheckbox();
await Task.Run(() => InitCheckbox());
}
await ((MainViewModel)this.DataContext).LoadAsync();
}

public async Task CloseAsync()
Expand Down
6 changes: 1 addition & 5 deletions JFrogVSExtension/UI/MainPanel/MainPanelPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke

protected override void Dispose(bool disposing)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (_solutionEventsCookie != 0)
{
_solution.UnadviseSolutionEvents(_solutionEventsCookie);
Expand Down Expand Up @@ -143,11 +144,6 @@ public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)

public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
{
MainPanel mainPanel = MainPanel.GetInstance();
if (mainPanel != null)
{
_ = mainPanel.LoadAsync();
}
return VSConstants.S_OK;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
HorizontalAlignment="Left"
VerticalAlignment="Top"
MinWidth="75"
MaxWidth="1000"
MaxWidth="2000"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Background="Transparent"
BorderThickness="0"
Margin="0"
BorderThickness="0"
HorizontalGridLinesBrush="Transparent"
VerticalGridLinesBrush="Transparent"
RowHeaderWidth="0">
Expand All @@ -55,7 +55,7 @@
<Setter Property="Background" Value="Transparent"/>
</Style>
</DataGrid.Resources>

<DataGrid.Columns>
<DataGridTemplateColumn Header="Severity" MinWidth="75" MaxWidth="500" CanUserSort="True" SortMemberPath="Severity">
<DataGridTemplateColumn.CellTemplate >
Expand All @@ -77,7 +77,7 @@
</DataGridTextColumn>
<DataGridTextColumn Header="Issue Type" Binding="{Binding IssueType}" MinWidth="75" MaxWidth="500" CanUserSort="True"/>
<DataGridTextColumn Header="Fixed Versions" Binding="{Binding FixedVersions}" MinWidth="75" MaxWidth="500" CanUserSort="True"/>
<DataGridTextColumn Header="Component" Binding="{Binding Component}" MinWidth="100" Width="*" MaxWidth="500" CanUserSort="True">
<DataGridTextColumn Header="Component" Binding="{Binding Component}" MinWidth="100" Width="*" CanUserSort="True">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
Expand Down
6 changes: 4 additions & 2 deletions JFrogVSExtension/UI/Panel/DataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ private Severity GetTopComponentSeverity(Severity topSeverity, Component depComp
public async Task<Artifacts> GetSecurityIssuesAsync(bool reScan, Projects projects, string solutionDir)
{
var componentsSet = new HashSet<Components>();
var workingDirs = new List<string>();
workingDirs.Add(solutionDir);
var workingDirs = new List<string>
{
solutionDir
};
if (!reScan)
{
foreach (Project project in projects.All)
Expand Down
8 changes: 6 additions & 2 deletions JFrogVSExtension/UI/Panel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ public MainViewModel()
{
Severities = new HashSet<Severity>();
ResetSeverities();
SeveritiesFromFilter = Severities;
SeveritiesFromFilter = Severities;
}

public async Task RefreshAsync()
{
Tree = new TreeViewModel();
Tree = new TreeViewModel
{
ScanStatus = "Xray scan in progress..."
};
await Tree.LoadAsync(RefreshType.Hard, SeveritiesFromFilter);
Tree.ScanStatus = "";
}

public void ExpandAll()
Expand Down
15 changes: 9 additions & 6 deletions JFrogVSExtension/UI/Panel/Tree/Tree.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
d:DesignHeight="250" d:DesignWidth="300"
Background="{DynamicResource {x:Static vsp:EnvironmentColors.ToolWindowBackgroundBrushKey}}"
Foreground="{DynamicResource {x:Static vsp:EnvironmentColors.ToolWindowTextBrushKey}}">

<UserControl.Resources>
<Style TargetType="{x:Type TreeView}">
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static vsp:TreeViewColors.BackgroundBrushKey}}"/>
Expand All @@ -22,19 +22,22 @@
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsp:EnvironmentColors.ToolWindowTextBrushKey}}"/>
</Style>
</UserControl.Resources>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<Grid Grid.Row="0">
<Border BorderThickness="0,0,0,1" BorderBrush="{DynamicResource {x:Static vsp:EnvironmentColors.BrandedUIBorderBrushKey}}">
<TextBlock Text="Components Tree" Padding="3"/>
<Grid>
<TextBlock x:Name="componentsTreeHeader" Text="Components Tree" Padding="3" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding ScanStatus}" Padding="3" HorizontalAlignment="Right" TextAlignment="Right" Foreground="{DynamicResource {x:Static vsp:EnvironmentColors.ControlLinkTextBrushKey}}"/>
</Grid>
</Border>
</Grid>

<Grid Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TreeView Grid.Row="1"
ItemsSource="{Binding Artifacts}"
Expand All @@ -55,7 +58,7 @@
<Setter Property="Margin" Value="0,3,0,0"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsp:TreeViewColors.GlyphBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static vsp:TreeViewColors.BackgroundBrushKey}}"/>

</Style>
</TreeView.ItemContainerStyle>

Expand Down
5 changes: 4 additions & 1 deletion JFrogVSExtension/UI/Panel/Tree/TreeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class TreeViewModel : BaseViewModel
public ObservableCollection<Issue> IssueDetails { get; set; }
public Component SelectedComponent { get; set; }
public Boolean EnableRefreshButton { get; set; }

public string ScanStatus { get; set; }
public string SelectedKey
{
get { return selectedKey; }
Expand All @@ -53,6 +55,7 @@ public string SelectedKey
public TreeViewModel()
{
this.EnableRefreshButton = true;
ScanStatus = ""; // Default value
}

#endregion
Expand Down Expand Up @@ -85,7 +88,7 @@ public async Task LoadAsync(RefreshType refreshType, HashSet<Severity> severitie
// 4. Get response and build the dependencies tree.

// Running CLI - this is the returned output.
String returnedText = await Task.Run(() => Util.GetCLIOutputAsync("rt nuget-deps-tree",solutionDir));
String returnedText = await Util.GetCLIOutputAsync("rt nuget-deps-tree",solutionDir);

// Load projects from output.
Project[] nugetProjects = Util.LoadNugetProjects(returnedText);
Expand Down
4 changes: 2 additions & 2 deletions JFrogVSExtension/Utils/ScanManager/ScanManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private async Task ConfigCLIAsync(string xrayUrl, string artifactoryUrl, string
public async Task<string> PreformScanAsync(List<string> workingDirs)
{
var workingDirsString = workingDirs.Count() > 1 ? string.Join(", ", workingDirs) : workingDirs.First();
var cliAuditCommand = $"audit --format=\"json\" --server-id=\"{CliServerId}\" --licenses --fail=\"false\" --working-dirs=\"{workingDirsString}\"";
var cliAuditCommand = $"audit --sca --format=\"json\" --server-id=\"{CliServerId}\" --licenses --fail=\"false\" --working-dirs=\"{workingDirsString}\"";

switch (Policy)
{
Expand All @@ -57,7 +57,7 @@ public async Task<string> PreformScanAsync(List<string> workingDirs)
cliAuditCommand += $" --watches=\"{watches}\"";
break;
}
return await Util.GetCLIOutputAsync(cliAuditCommand, workingDirs.First(), false, cliEnv);
return await Util.GetCLIOutputAsync(cliAuditCommand, workingDirs.First(), false, cliEnv);
}
}
}
83 changes: 47 additions & 36 deletions JFrogVSExtension/Utils/Util.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using JFrogVSExtension.Data;
using JFrogVSExtension.Logger;
using JFrogVSExtension.Xray;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Threading;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -54,6 +56,7 @@ private static Project LoadNpmProject(string packageJsonPath)
var npmProjectTree = GetProcessOutputAsync("cmd.exe", "/C npm ls --json --all --long --package-lock-only", fileInfo.DirectoryName);

var npmProj = JsonConvert.DeserializeObject<NpmLsNode>(npmProjectTree.Result);

var project = new Project()
{
name = $"{npmProj.name}:{npmProj.version}",
Expand Down Expand Up @@ -109,46 +112,54 @@ public static async Task<string> GetCLIOutputAsync(string command, string workin

public static async Task<string> GetProcessOutputAsync(string pathToExe, string command, string workingDir = "", bool configCommand = false, Dictionary<string, string> envVars = null)
{
//Create process
Process pProcess = new Process();

// strCommand is path and file name of command to run
pProcess.StartInfo.FileName = pathToExe;

// strCommandParameters are parameters to pass to program
// Here we will run the nuget command for the cli
pProcess.StartInfo.Arguments = command;
// Avoid printing commands with credentials
var commandString = configCommand ? "config command" : command;

pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.CreateNoWindow = true;
// Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.RedirectStandardError = true;
pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pProcess.StartInfo.WorkingDirectory = workingDir;
if (envVars != null)
return await Task.Run(async () =>
{
foreach (var envVar in envVars)
//Create process
Process pProcess = new Process
{
StartInfo = new ProcessStartInfo
{
// strCommand is path and file name of command to run
FileName = pathToExe,
// strCommandParameters are parameters to pass to program
// Here we will run the nuget command for the cli
Arguments = command,
UseShellExecute = false,
// Set output of program to be written to process output stream
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = workingDir
}
};

// Avoid printing commands with credentials
var commandString = configCommand ? "config command" : command;

if (envVars != null)
{
pProcess.StartInfo.EnvironmentVariables[envVar.Key] = envVar.Value;
foreach (var envVar in envVars)
{
pProcess.StartInfo.EnvironmentVariables[envVar.Key] = envVar.Value;
}
}
}
StringBuilder strOutput = new StringBuilder();
StringBuilder error = new StringBuilder();
StringBuilder strOutput = new StringBuilder();
StringBuilder error = new StringBuilder();

// Saving the response from the CLI to a StringBuilder.

var tcsOutput = new TaskCompletionSource<bool>();
var tcsError = new TaskCompletionSource<bool>();


// Saving the response from the CLI to a StringBuilder.
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
// Get program output
// The json returned from the CLI
pProcess.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputWaitHandle.Set();
tcsOutput.SetResult(true);
}
else
{
Expand All @@ -159,7 +170,7 @@ public static async Task<string> GetProcessOutputAsync(string pathToExe, string
{
if (e.Data == null)
{
errorWaitHandle.Set();
tcsError.SetResult(true);
}
else
{
Expand All @@ -172,11 +183,11 @@ public static async Task<string> GetProcessOutputAsync(string pathToExe, string
pProcess.BeginOutputReadLine();
pProcess.BeginErrorReadLine();
// Waits for maximal 1 hour
pProcess.WaitForExit(60*60*1000);
pProcess.WaitForExit(60 * 60 * 1000);

// Wait for the entire output to be written
if (outputWaitHandle.WaitOne(1) &&
errorWaitHandle.WaitOne(1))

// Wait for the entire output to be written
if (tcsOutput.Task.IsCompleted && tcsError.Task.IsCompleted)
{
// Process completed. Check process.ExitCode here.
if (pProcess.ExitCode != 0)
Expand All @@ -198,7 +209,7 @@ public static async Task<string> GetProcessOutputAsync(string pathToExe, string
await OutputLog.ShowMessageAsync("Process timeout");
throw new IOException($"Process timeout, {pathToExe} {commandString}");
}
}
});
}

private static string GetAssemblyLocalPathFrom(Type type)
Expand Down

0 comments on commit c256bc7

Please sign in to comment.