@@ -27,19 +27,22 @@ public class GoComponentDetector : FileComponentDetector
2727
2828 private readonly ICommandLineInvocationService commandLineInvocationService ;
2929 private readonly IEnvironmentVariableService envVarService ;
30+ private readonly IFileUtilityService fileUtilityService ;
3031
3132 public GoComponentDetector (
3233 IComponentStreamEnumerableFactory componentStreamEnumerableFactory ,
3334 IObservableDirectoryWalkerFactory walkerFactory ,
3435 ICommandLineInvocationService commandLineInvocationService ,
3536 IEnvironmentVariableService envVarService ,
36- ILogger < GoComponentDetector > logger )
37+ ILogger < GoComponentDetector > logger ,
38+ IFileUtilityService fileUtilityService )
3739 {
3840 this . ComponentStreamEnumerableFactory = componentStreamEnumerableFactory ;
3941 this . Scanner = walkerFactory ;
4042 this . commandLineInvocationService = commandLineInvocationService ;
4143 this . envVarService = envVarService ;
4244 this . Logger = logger ;
45+ this . fileUtilityService = fileUtilityService ;
4346 }
4447
4548 public override string Id => "Go" ;
@@ -50,7 +53,7 @@ public GoComponentDetector(
5053
5154 public override IEnumerable < ComponentType > SupportedComponentTypes { get ; } = [ ComponentType . Go ] ;
5255
53- public override int Version => 7 ;
56+ public override int Version => 8 ;
5457
5558 protected override Task < IObservable < ProcessRequest > > OnPrepareDetectionAsync (
5659 IObservable < ProcessRequest > processRequests ,
@@ -246,7 +249,7 @@ private async Task<bool> UseGoCliToScanAsync(string location, ISingleFileCompone
246249 return false ;
247250 }
248251
249- this . RecordBuildDependencies ( goDependenciesProcess . StdOut , singleFileComponentRecorder ) ;
252+ this . RecordBuildDependencies ( goDependenciesProcess . StdOut , singleFileComponentRecorder , projectRootDirectory . FullName ) ;
250253
251254 var generateGraphProcess = await this . commandLineInvocationService . ExecuteCommandAsync ( "go" , null , workingDirectory : projectRootDirectory , new List < string > { "mod" , "graph" } . ToArray ( ) ) ;
252255 if ( generateGraphProcess . ExitCode == 0 )
@@ -284,6 +287,7 @@ private async Task ParseGoModFileAsync(
284287 GoGraphTelemetryRecord goGraphTelemetryRecord )
285288 {
286289 using var reader = new StreamReader ( file . Stream ) ;
290+ var startString = "require " ;
287291
288292 // There can be multiple require( ) sections in go 1.17+. loop over all of them.
289293 while ( ! reader . EndOfStream )
@@ -299,9 +303,9 @@ private async Task ParseGoModFileAsync(
299303
300304 // In go >= 1.17, direct dependencies are listed as "require x/y v1.2.3", and transitive dependencies
301305 // are listed in the require () section
302- if ( line . StartsWith ( "require " ) )
306+ if ( line . StartsWith ( startString ) )
303307 {
304- this . TryRegisterDependencyFromModLine ( line [ 8 ..] , singleFileComponentRecorder ) ;
308+ this . TryRegisterDependencyFromModLine ( line [ startString . Length ..] , singleFileComponentRecorder ) ;
305309 }
306310
307311 line = await reader . ReadLineAsync ( ) ;
@@ -421,14 +425,16 @@ private bool IsModuleInBuildList(ISingleFileComponentRecorder singleFileComponen
421425 return singleFileComponentRecorder . GetComponent ( component . Id ) != null ;
422426 }
423427
424- private void RecordBuildDependencies ( string goListOutput , ISingleFileComponentRecorder singleFileComponentRecorder )
428+ private void RecordBuildDependencies ( string goListOutput , ISingleFileComponentRecorder singleFileComponentRecorder , string projectRootDirectoryFullName )
425429 {
426430 var goBuildModules = new List < GoBuildModule > ( ) ;
427431 var reader = new JsonTextReader ( new StringReader ( goListOutput ) )
428432 {
429433 SupportMultipleContent = true ,
430434 } ;
431435
436+ using var record = new GoReplaceTelemetryRecord ( ) ;
437+
432438 while ( reader . Read ( ) )
433439 {
434440 var serializer = new JsonSerializer ( ) ;
@@ -439,13 +445,45 @@ private void RecordBuildDependencies(string goListOutput, ISingleFileComponentRe
439445
440446 foreach ( var dependency in goBuildModules )
441447 {
448+ var dependencyName = $ "{ dependency . Path } { dependency . Version } ";
449+
442450 if ( dependency . Main )
443451 {
444452 // main is the entry point module (superfluous as we already have the file location)
445453 continue ;
446454 }
447455
448- var goComponent = new GoComponent ( dependency . Path , dependency . Version ) ;
456+ if ( dependency . Replace ? . Path != null && dependency . Replace . Version == null )
457+ {
458+ var dirName = projectRootDirectoryFullName ;
459+ var combinedPath = Path . Combine ( dirName , dependency . Replace . Path , "go.mod" ) ;
460+ var goModFilePath = Path . GetFullPath ( combinedPath ) ;
461+ if ( this . fileUtilityService . Exists ( goModFilePath ) )
462+ {
463+ this . Logger . LogInformation ( "go Module {GoModule} is being replaced with module at path {GoModFilePath}" , dependencyName , goModFilePath ) ;
464+ record . GoModPathAndVersion = dependencyName ;
465+ record . GoModReplacement = goModFilePath ;
466+ continue ;
467+ }
468+
469+ this . Logger . LogWarning ( "go.mod file {GoModFilePath} does not exist in the relative path given for replacement" , goModFilePath ) ;
470+ record . GoModPathAndVersion = goModFilePath ;
471+ record . GoModReplacement = null ;
472+ }
473+
474+ GoComponent goComponent ;
475+ if ( dependency . Replace ? . Path != null && dependency . Replace . Version != null )
476+ {
477+ var dependencyReplacementName = $ "{ dependency . Replace . Path } { dependency . Replace . Version } ";
478+ goComponent = new GoComponent ( dependency . Replace . Path , dependency . Replace . Version ) ;
479+ this . Logger . LogInformation ( "go Module {GoModule} being replaced with module {GoModuleReplacement}" , dependencyName , dependencyReplacementName ) ;
480+ record . GoModPathAndVersion = dependencyName ;
481+ record . GoModReplacement = dependencyReplacementName ;
482+ }
483+ else
484+ {
485+ goComponent = new GoComponent ( dependency . Path , dependency . Version ) ;
486+ }
449487
450488 if ( dependency . Indirect )
451489 {
@@ -485,5 +523,7 @@ private class GoBuildModule
485523 public string Version { get ; set ; }
486524
487525 public bool Indirect { get ; set ; }
526+
527+ public GoBuildModule Replace { get ; set ; }
488528 }
489529}
0 commit comments