diff --git a/.editorconfig b/.editorconfig index 66d093589..aa0d438c9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -463,9 +463,26 @@ dotnet_diagnostic.SA1623.severity = none # https://github.com/DotNetAnalyzers/StyleCopAnalyzers ########################################## +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1009.md +# Closing parenthesis should be spaced correctly dotnet_diagnostic.SA1009.severity = none + +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1101.md +# Local calls should be prefixed with `this` +dotnet_diagnostic.SA1101.severity = warning + +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1111.md +# Closing parenthesis should be on the line of last parameter dotnet_diagnostic.SA1111.severity = none +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1201.md +# Elements should be in the correct order +dotnet_diagnostic.SA1201.severity = warning + +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1202.md +# Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + # https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1600.md # Elements should be documented dotnet_diagnostic.SA1600.severity = suggestion @@ -612,9 +629,6 @@ dotnet_diagnostic.CA1837.severity = suggestion # CA1024: Use properties where appropriate dotnet_diagnostic.CA1024.severity = suggestion -# SA1202: Elements should be ordered by access -dotnet_diagnostic.SA1202.severity = suggestion - # IDE0022: Use expression body for methods dotnet_diagnostic.IDE0022.severity = suggestion diff --git a/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs b/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs index 7669726d4..df7d12fc8 100644 --- a/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs +++ b/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs @@ -17,18 +17,6 @@ namespace Microsoft.ComponentDetection.Contracts; /// Specialized base class for file based component detection. public abstract class FileComponentDetector : IComponentDetector { - /// - /// Gets or sets the factory for handing back component streams to File detectors. - /// - protected IComponentStreamEnumerableFactory ComponentStreamEnumerableFactory { get; set; } - - protected IObservableDirectoryWalkerFactory Scanner { get; set; } - - /// - /// Gets or sets the logger for writing basic logging message to both console and file. - /// - protected ILogger Logger { get; set; } - public IComponentRecorder ComponentRecorder { get; private set; } /// @@ -46,25 +34,37 @@ public abstract class FileComponentDetector : IComponentDetector /// Gets the version of this component detector. public abstract int Version { get; } + public virtual bool NeedsAutomaticRootDependencyCalculation { get; protected set; } + /// - /// Gets the folder names that will be skipped by the Component Detector. + /// List of any any additional properties as key-value pairs that we would like to capture for the detector. /// - protected virtual IList SkippedFolders => []; + public List<(string PropertyKey, string PropertyValue)> AdditionalProperties { get; set; } = []; + + protected ConcurrentDictionary Telemetry { get; set; } = []; /// - /// Gets or sets the active scan request -- only populated after a ScanDirectoryAsync is invoked. If ScanDirectoryAsync is overridden, - /// the overrider should ensure this property is populated. + /// Gets or sets the factory for handing back component streams to File detectors. /// - protected ScanRequest CurrentScanRequest { get; set; } + protected IComponentStreamEnumerableFactory ComponentStreamEnumerableFactory { get; set; } - public virtual bool NeedsAutomaticRootDependencyCalculation { get; protected set; } + protected IObservableDirectoryWalkerFactory Scanner { get; set; } - protected ConcurrentDictionary Telemetry { get; set; } = []; + /// + /// Gets or sets the logger for writing basic logging message to both console and file. + /// + protected ILogger Logger { get; set; } /// - /// List of any any additional properties as key-value pairs that we would like to capture for the detector. + /// Gets the folder names that will be skipped by the Component Detector. /// - public List<(string PropertyKey, string PropertyValue)> AdditionalProperties { get; set; } = []; + protected virtual IList SkippedFolders => []; + + /// + /// Gets or sets the active scan request -- only populated after a ScanDirectoryAsync is invoked. If ScanDirectoryAsync is overridden, + /// the overrider should ensure this property is populated. + /// + protected ScanRequest CurrentScanRequest { get; set; } protected IObservable ComponentStreams { get; private set; } @@ -78,16 +78,6 @@ public async virtual Task ExecuteDetectorAsync(Sca return await this.ScanDirectoryAsync(request, cancellationToken); } - private Task ScanDirectoryAsync(ScanRequest request, CancellationToken cancellationToken = default) - { - this.CurrentScanRequest = request; - - var filteredObservable = this.Scanner.GetFilteredComponentStreamObservable(request.SourceDirectory, this.SearchPatterns, request.ComponentRecorder); - - this.Logger.LogDebug("Registered {Detector}", this.GetType().FullName); - return this.ProcessAsync(filteredObservable, request.DetectorArgs, request.MaxThreads, request.CleanupCreatedFiles, cancellationToken); - } - /// /// Gets the file streams for the Detector's declared as an . /// @@ -111,37 +101,6 @@ protected Task> GetFileStreamsAsync(DirectoryInfo /// The lockfile version. protected void RecordLockfileVersion(string lockfileVersion) => this.Telemetry["LockfileVersion"] = lockfileVersion; - private async Task ProcessAsync( - IObservable processRequests, IDictionary detectorArgs, int maxThreads, bool cleanupCreatedFiles, CancellationToken cancellationToken = default) - { - var threadsToUse = this.EnableParallelism ? Math.Min(Environment.ProcessorCount, maxThreads) : 1; - this.Telemetry["ThreadsUsed"] = $"{threadsToUse}"; - - var processor = new ActionBlock( - async processRequest => await this.OnFileFoundAsync(processRequest, detectorArgs, cleanupCreatedFiles, cancellationToken), - new ExecutionDataflowBlockOptions - { - // MaxDegreeOfParallelism is the lower of the processor count and the max threads arg that the customer passed in - MaxDegreeOfParallelism = threadsToUse, - }); - - var preprocessedObserbable = await this.OnPrepareDetectionAsync(processRequests, detectorArgs, cancellationToken); - - await preprocessedObserbable.ForEachAsync(processRequest => processor.Post(processRequest)); - - processor.Complete(); - - await processor.Completion; - - await this.OnDetectionFinishedAsync(); - - return new IndividualDetectorScanResult - { - ResultCode = ProcessingResultCode.Success, - AdditionalTelemetryDetails = this.Telemetry.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), - }; - } - /// /// Auxliary method executed before the actual scanning of a given file takes place. /// This method can be used to modify or create new ProcessRequests that later will @@ -174,4 +133,45 @@ protected virtual Task OnDetectionFinishedAsync() // Do not cleanup by default, only if the detector uses the FileComponentWithCleanup abstract class. protected virtual async Task OnFileFoundAsync(ProcessRequest processRequest, IDictionary detectorArgs, bool cleanupCreatedFiles, CancellationToken cancellationToken = default) => await this.OnFileFoundAsync(processRequest, detectorArgs, cancellationToken); + + private Task ScanDirectoryAsync(ScanRequest request, CancellationToken cancellationToken = default) + { + this.CurrentScanRequest = request; + + var filteredObservable = this.Scanner.GetFilteredComponentStreamObservable(request.SourceDirectory, this.SearchPatterns, request.ComponentRecorder); + + this.Logger.LogDebug("Registered {Detector}", this.GetType().FullName); + return this.ProcessAsync(filteredObservable, request.DetectorArgs, request.MaxThreads, request.CleanupCreatedFiles, cancellationToken); + } + + private async Task ProcessAsync( + IObservable processRequests, IDictionary detectorArgs, int maxThreads, bool cleanupCreatedFiles, CancellationToken cancellationToken = default) + { + var threadsToUse = this.EnableParallelism ? Math.Min(Environment.ProcessorCount, maxThreads) : 1; + this.Telemetry["ThreadsUsed"] = $"{threadsToUse}"; + + var processor = new ActionBlock( + async processRequest => await this.OnFileFoundAsync(processRequest, detectorArgs, cleanupCreatedFiles, cancellationToken), + new ExecutionDataflowBlockOptions + { + // MaxDegreeOfParallelism is the lower of the processor count and the max threads arg that the customer passed in + MaxDegreeOfParallelism = threadsToUse, + }); + + var preprocessedObserbable = await this.OnPrepareDetectionAsync(processRequests, detectorArgs, cancellationToken); + + await preprocessedObserbable.ForEachAsync(processRequest => processor.Post(processRequest)); + + processor.Complete(); + + await processor.Completion; + + await this.OnDetectionFinishedAsync(); + + return new IndividualDetectorScanResult + { + ResultCode = ProcessingResultCode.Success, + AdditionalTelemetryDetails = this.Telemetry.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + }; + } } diff --git a/src/Microsoft.ComponentDetection.Contracts/TypedComponent/GoComponent.cs b/src/Microsoft.ComponentDetection.Contracts/TypedComponent/GoComponent.cs index 01d1f0085..f28410827 100644 --- a/src/Microsoft.ComponentDetection.Contracts/TypedComponent/GoComponent.cs +++ b/src/Microsoft.ComponentDetection.Contracts/TypedComponent/GoComponent.cs @@ -41,8 +41,6 @@ public GoComponent() public override ComponentType Type => ComponentType.Go; - protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}"; - public override bool Equals(object obj) { return obj is GoComponent otherComponent && this.Equals(otherComponent); @@ -62,4 +60,6 @@ public override int GetHashCode() { return this.Name.GetHashCode() ^ this.Version.GetHashCode() ^ this.Hash.GetHashCode(); } + + protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}"; }