- 
                Notifications
    
You must be signed in to change notification settings  - Fork 79
 
fix(gradle): Refactored init-script-gradle.ftl to add support for configuration cache in gradle projects. (IDETECT-4812) #1531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 27 commits
f1528f2
              84672c7
              faa7342
              9ff2d45
              94725ad
              3678d0c
              a40e26b
              4614667
              aeba3a2
              dac370b
              457a8a9
              1f07694
              52511bb
              fed5e80
              3d29cdb
              4982f32
              6816b4e
              3fd97a9
              cec5310
              8b029cb
              b247c92
              7c3ef46
              32f78b5
              f829dbd
              0a4b538
              12736f5
              94913e1
              4efddb7
              1b0851f
              3e637d4
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 
          
            
          
           | 
    @@ -13,174 +13,235 @@ Set<String> projectNameIncludeFilter = convertStringToSet('${includedProjectName | |||||||||||
| Set<String> projectPathExcludeFilter = convertStringToSet('${excludedProjectPaths}') | ||||||||||||
| Set<String> projectPathIncludeFilter = convertStringToSet('${includedProjectPaths}') | ||||||||||||
| Boolean rootOnly = Boolean.parseBoolean("${rootOnlyOption}") | ||||||||||||
| 
     | 
||||||||||||
| gradle.allprojects { | ||||||||||||
| // add a new task to each project to start the process of getting the dependencies | ||||||||||||
| task gatherDependencies(type: DefaultTask) { | ||||||||||||
| // Store project name during configuration phase | ||||||||||||
| def projectName = project.name | ||||||||||||
| doLast { | ||||||||||||
| println "Gathering dependencies for " + project.name | ||||||||||||
| try { | ||||||||||||
| println "Gathering dependencies for " + projectName | ||||||||||||
| } catch (Exception e) { | ||||||||||||
                
      
                  bd-samratmuk marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||||||||||||
| println "ERROR in gatherDependencies task: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| afterEvaluate { project -> | ||||||||||||
| // after a project has been evaluated modify the dependencies task for that project to output to a specific file. | ||||||||||||
| project.tasks.getByName('dependencies') { | ||||||||||||
| ext { | ||||||||||||
| excludedProjectNames = '${excludedProjectNames}' | ||||||||||||
| includedProjectNames = '${includedProjectNames}' | ||||||||||||
| excludedConfigurationNames = '${excludedConfigurationNames}' | ||||||||||||
| includedConfigurationNames = '${includedConfigurationNames}' | ||||||||||||
| outputDirectoryPath = System.getProperty('GRADLEEXTRACTIONDIR') | ||||||||||||
| 
     | 
||||||||||||
| afterEvaluate { currentProject -> | ||||||||||||
| // Capture all needed project properties during configuration | ||||||||||||
| def projectPath = currentProject.path | ||||||||||||
| def projectName = currentProject.name | ||||||||||||
| def isRootProject = isRoot(currentProject) | ||||||||||||
| def extractionDir = System.getProperty('GRADLEEXTRACTIONDIR') | ||||||||||||
| def projectDir = currentProject.projectDir | ||||||||||||
| def projectDirPath = projectDir.canonicalPath | ||||||||||||
| 
     | 
||||||||||||
| // Root project properties | ||||||||||||
| def rootProject = currentProject.gradle.rootProject | ||||||||||||
| def rootProjectName = rootProject.name | ||||||||||||
| def rootProjectPath = rootProject.path | ||||||||||||
| def rootProjectGroup = rootProject.group.toString() | ||||||||||||
| def rootProjectVersion = rootProject.version.toString() | ||||||||||||
| def rootProjectDir = rootProject.projectDir | ||||||||||||
| def rootProjectDirPath = rootProjectDir.canonicalPath | ||||||||||||
| 
     | 
||||||||||||
| // Project metadata | ||||||||||||
| def projectGroup = currentProject.group.toString() | ||||||||||||
| def projectVersion = currentProject.version.toString() | ||||||||||||
| def projectParent = currentProject.parent ? currentProject.parent.toString() : "none" | ||||||||||||
| 
     | 
||||||||||||
| // Prepare configuration names | ||||||||||||
| def configurationNames = getFilteredConfigurationNames(currentProject, | ||||||||||||
| '${excludedConfigurationNames}', '${includedConfigurationNames}') | ||||||||||||
| 
     | 
||||||||||||
| def selectedConfigs = [] | ||||||||||||
| configurationNames.each { name -> | ||||||||||||
| try { | ||||||||||||
| def config = currentProject.configurations.findByName(name) | ||||||||||||
| if (config) { | ||||||||||||
| selectedConfigs.add(config) | ||||||||||||
| } | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "Could not process configuration: " + name | ||||||||||||
| } | ||||||||||||
| doFirst { | ||||||||||||
| generateRootProjectMetaData(project, outputDirectoryPath) | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| if((rootOnly && isRoot(project)) || (!rootOnly && shouldInclude(projectNameExcludeFilter, projectNameIncludeFilter, project.name) && shouldInclude(projectPathExcludeFilter, projectPathIncludeFilter, project.path)) ) { | ||||||||||||
| def dependencyTask = project.tasks.getByName('dependencies') | ||||||||||||
| File projectOutputFile = findProjectOutputFile(project, outputDirectoryPath) | ||||||||||||
| File projectFile = createProjectOutputFile(projectOutputFile) | ||||||||||||
| // Check if the project should be included in results | ||||||||||||
| def shouldIncludeProject = (rootOnly && isRootProject) || | ||||||||||||
| (!rootOnly && shouldInclude(projectNameExcludeFilter, projectNameIncludeFilter, projectName) && | ||||||||||||
| shouldInclude(projectPathExcludeFilter, projectPathIncludeFilter, projectPath)) | ||||||||||||
| 
     | 
||||||||||||
| if(dependencyTask.metaClass.respondsTo(dependencyTask, "setConfigurations")) { | ||||||||||||
| println "Updating configurations for task" | ||||||||||||
| // modify the configurations for the dependency task | ||||||||||||
| setConfigurations(filterConfigurations(project, excludedConfigurationNames, includedConfigurationNames)) | ||||||||||||
| // Capture output file path during configuration | ||||||||||||
| def projectFilePathConfig = computeProjectFilePath(projectPath, extractionDir) | ||||||||||||
| 
     | 
||||||||||||
| } else { | ||||||||||||
| println "Could not find method 'setConfigurations'" | ||||||||||||
| } | ||||||||||||
| // Configure the dependencies task during configuration time | ||||||||||||
| def dependenciesTask = currentProject.tasks.getByName('dependencies') | ||||||||||||
| 
     | 
||||||||||||
| // Set the configurations at configuration time if possible | ||||||||||||
| if (!selectedConfigs.isEmpty()) { | ||||||||||||
| dependenciesTask.configurations = selectedConfigs | ||||||||||||
        
    
 | 
||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| // Set the output file at configuration time if possible | ||||||||||||
| if (shouldIncludeProject) { | ||||||||||||
| // Create output file directly during configuration | ||||||||||||
| File projectFile = new File(projectFilePathConfig) | ||||||||||||
| if (projectFile.exists()) { | ||||||||||||
| projectFile.delete() | ||||||||||||
| } | ||||||||||||
| projectFile.createNewFile() | ||||||||||||
| 
         
      Comment on lines
    
      +86
     to 
      +90
    
   
  
    
 | 
||||||||||||
| 
     | 
||||||||||||
| // Set the output file during configuration phase | ||||||||||||
| try { | ||||||||||||
| dependenciesTask.outputFile = projectFile | ||||||||||||
| println "Set output file during configuration to " + projectFile.getAbsolutePath() | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "Could not set outputFile property during configuration: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| if(dependencyTask.metaClass.respondsTo(dependencyTask,"setOutputFile")) { | ||||||||||||
| println "Updating output file for task to "+projectFile.getAbsolutePath() | ||||||||||||
| // modify the output file | ||||||||||||
| setOutputFile(projectFile) | ||||||||||||
| } else { | ||||||||||||
| println "Could not find method 'setOutputFile'" | ||||||||||||
| dependenciesTask.doFirst { | ||||||||||||
| try { | ||||||||||||
| if (extractionDir == null) { | ||||||||||||
| throw new IllegalStateException("GRADLEEXTRACTIONDIR system property is not set") | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
        
    
 | 
||||||||||||
| // Create metadata file for root project | ||||||||||||
| if (isRootProject) { | ||||||||||||
| try { | ||||||||||||
| File outputDirectory = new File(extractionDir) | ||||||||||||
| outputDirectory.mkdirs() | ||||||||||||
| File rootOutputFile = new File(outputDirectory, 'rootProjectMetadata.txt') | ||||||||||||
| 
     | 
||||||||||||
| def rootProjectMetadataPieces = [] | ||||||||||||
| rootProjectMetadataPieces.add('DETECT META DATA START') | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectDirectory:" + rootProjectDirPath) | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectPath:" + rootProjectPath) | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectGroup:" + rootProjectGroup) | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectName:" + rootProjectName) | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectVersion:" + rootProjectVersion) | ||||||||||||
| rootProjectMetadataPieces.add('DETECT META DATA END') | ||||||||||||
| 
     | 
||||||||||||
| rootOutputFile.text = rootProjectMetadataPieces.join('\n') | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "ERROR while generating root project metadata: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| } | ||||||||||||
| } else { | ||||||||||||
| println "Excluding from results subproject: " + project.path | ||||||||||||
| } | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "ERROR in dependencies doFirst: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| doLast { | ||||||||||||
| if((rootOnly && isRoot(project)) || (!rootOnly && shouldInclude(projectNameExcludeFilter, projectNameIncludeFilter, project.name) && shouldInclude(projectPathExcludeFilter, projectPathIncludeFilter, project.path))) { | ||||||||||||
| File projectFile = findProjectOutputFile(project, outputDirectoryPath) | ||||||||||||
| appendProjectMetadata(project, projectFile) | ||||||||||||
| dependenciesTask.doLast { | ||||||||||||
| try { | ||||||||||||
| if(shouldIncludeProject) { | ||||||||||||
| File projectFile = new File(projectFilePathConfig) | ||||||||||||
| 
     | 
||||||||||||
| // Add metadata at the end of the file | ||||||||||||
| def metaDataPieces = [] | ||||||||||||
| metaDataPieces.add('') | ||||||||||||
| metaDataPieces.add('DETECT META DATA START') | ||||||||||||
| metaDataPieces.add("rootProjectDirectory:" + rootProjectDirPath) | ||||||||||||
| metaDataPieces.add("rootProjectGroup:" + rootProjectGroup) | ||||||||||||
| metaDataPieces.add("rootProjectPath:" + rootProjectPath) | ||||||||||||
| metaDataPieces.add("rootProjectName:" + rootProjectName) | ||||||||||||
| metaDataPieces.add("rootProjectVersion:" + rootProjectVersion) | ||||||||||||
| metaDataPieces.add("projectDirectory:" + projectDirPath) | ||||||||||||
| metaDataPieces.add("projectGroup:" + projectGroup) | ||||||||||||
| metaDataPieces.add("projectName:" + projectName) | ||||||||||||
| metaDataPieces.add("projectVersion:" + projectVersion) | ||||||||||||
| metaDataPieces.add("projectPath:" + projectPath) | ||||||||||||
| metaDataPieces.add("projectParent:" + projectParent) | ||||||||||||
| metaDataPieces.add('DETECT META DATA END') | ||||||||||||
| metaDataPieces.add('') | ||||||||||||
| 
     | 
||||||||||||
| // Append to file | ||||||||||||
| projectFile << metaDataPieces.join('\n') | ||||||||||||
| } | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "ERROR in dependencies doLast: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| // this forces the dependencies task to be run which will write the content to the modified output file | ||||||||||||
| project.gatherDependencies.finalizedBy(project.tasks.getByName('dependencies')) | ||||||||||||
| project.gatherDependencies | ||||||||||||
| 
     | 
||||||||||||
| // This forces the dependencies task to be run | ||||||||||||
| currentProject.gatherDependencies.finalizedBy(currentProject.tasks.getByName('dependencies')) | ||||||||||||
| currentProject.gatherDependencies | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| // ## START methods invoked by tasks above | ||||||||||||
| <#-- Do not parse with Freemarker because Groovy variable replacement in template strings is the same as Freemarker template syntax. --> | ||||||||||||
| <#noparse> | ||||||||||||
| def isRoot(Project project) { | ||||||||||||
| Project rootProject = project.gradle.rootProject; | ||||||||||||
| return project.name.equals(rootProject.name) | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| def generateRootProjectMetaData(Project project, String outputDirectoryPath) { | ||||||||||||
| File outputDirectory = createTaskOutputDirectory(outputDirectoryPath) | ||||||||||||
| outputDirectory.mkdirs() | ||||||||||||
| 
     | 
||||||||||||
| Project rootProject = project.gradle.rootProject; | ||||||||||||
| /* if the current project is the root project then generate the file containing | ||||||||||||
| the meta data for the root project otherwise ignore. | ||||||||||||
| */ | ||||||||||||
| if (project.name.equals(rootProject.name)) { | ||||||||||||
| File rootOutputFile = new File(outputDirectory, 'rootProjectMetadata.txt'); | ||||||||||||
| String rootProjectDirectory = rootProject.getProjectDir().getCanonicalPath() | ||||||||||||
| String rootProjectPath = rootProject.path.toString() | ||||||||||||
| String rootProjectGroup = rootProject.group.toString() | ||||||||||||
| String rootProjectName = rootProject.name.toString() | ||||||||||||
| String rootProjectVersionName = rootProject.version.toString() | ||||||||||||
| 
     | 
||||||||||||
| def rootProjectMetadataPieces = [] | ||||||||||||
| rootProjectMetadataPieces.add('DETECT META DATA START') | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectDirectory:${rootProjectDirectory}") | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectPath:${rootProjectPath}") | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectGroup:${rootProjectGroup}") | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectName:${rootProjectName}") | ||||||||||||
| rootProjectMetadataPieces.add("rootProjectVersion:${rootProjectVersionName}") | ||||||||||||
| rootProjectMetadataPieces.add('DETECT META DATA END') | ||||||||||||
| rootOutputFile << rootProjectMetadataPieces.join('\n') | ||||||||||||
| try { | ||||||||||||
| Project rootProject = project.gradle.rootProject; | ||||||||||||
| return project.name.equals(rootProject.name) | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "ERROR in isRoot: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| return false | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| def findProjectOutputFile(Project project, String outputDirectoryPath) { | ||||||||||||
| File outputDirectory = createTaskOutputDirectory(outputDirectoryPath) | ||||||||||||
| String name = project.toString() | ||||||||||||
| 
     | 
||||||||||||
| int depthCount = 0 | ||||||||||||
| for(char c: name.toCharArray()) { | ||||||||||||
| if (c == ':') { | ||||||||||||
| depthCount++ | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| String depth = String.valueOf(depthCount) | ||||||||||||
| // Get path for project file | ||||||||||||
| def computeProjectFilePath(String projectPath, String outputDirectoryPath) { | ||||||||||||
| try { | ||||||||||||
| File outputDirectory = createTaskOutputDirectory(outputDirectoryPath) | ||||||||||||
| String name = projectPath ?: "" | ||||||||||||
| 
     | 
||||||||||||
| String nameForFile = name?.replaceAll(/[^\p{IsAlphabetic}\p{Digit}]/, "_") | ||||||||||||
| File outputFile = new File(outputDirectory, "${nameForFile}_depth${depth}_dependencyGraph.txt") | ||||||||||||
| 
     | 
||||||||||||
| outputFile | ||||||||||||
| } | ||||||||||||
| 
     | 
||||||||||||
| def filterConfigurations(Project project, String excludedConfigurationNames, String includedConfigurationNames) { | ||||||||||||
| Set<String> configurationExcludeFilter = convertStringToSet(excludedConfigurationNames) | ||||||||||||
| Set<String> configurationIncludeFilter = convertStringToSet(includedConfigurationNames) | ||||||||||||
| Set<Configuration> filteredConfigurationSet = new TreeSet<Configuration>(new Comparator<Configuration>() { | ||||||||||||
| public int compare(Configuration conf1, Configuration conf2) { | ||||||||||||
| return conf1.getName().compareTo(conf2.getName()); | ||||||||||||
| int depthCount = 0 | ||||||||||||
| for(char c: name.toCharArray()) { | ||||||||||||
| if (c == ':') { | ||||||||||||
| depthCount++ | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| }) | ||||||||||||
| for (Configuration configuration : project.configurations) { | ||||||||||||
| if (shouldInclude(configurationExcludeFilter, configurationIncludeFilter, configuration.name)) { | ||||||||||||
| filteredConfigurationSet.add(configuration) | ||||||||||||
| String depth = String.valueOf(depthCount) | ||||||||||||
| // Special case for root project | ||||||||||||
| if (projectPath == ":") { | ||||||||||||
| return new File(outputDirectory, "root_project_depth${depth}_dependencyGraph.txt").getAbsolutePath() | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| // Replace all non-alphanumeric characters with underscores | ||||||||||||
| 
     | 
||||||||||||
| filteredConfigurationSet | ||||||||||||
| String nameForFile = name?.replaceAll(/[^\p{IsAlphabetic}\p{Digit}]/, "_") | ||||||||||||
| nameForFile = "project_" + nameForFile | ||||||||||||
| return new File(outputDirectory, "${nameForFile}_depth${depth}_dependencyGraph.txt").getAbsolutePath() | ||||||||||||
| } catch (Exception e) { | ||||||||||||
| println "ERROR in computeProjectFilePath: " + e.message | ||||||||||||
| e.printStackTrace() | ||||||||||||
| // Do nothing else | ||||||||||||
                
       | 
||||||||||||
| println "ERROR in computeProjectFilePath: " + e.message | |
| e.printStackTrace() | |
| // Do nothing else | |
| // Return null to indicate failure | |
| return null | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the gradlew executable fails to run, Detect still runs as per the old code, falling back to the gradle project inspector Hence we do nothing.
        
          
              
                Outdated
          
        
      
    
      
    
      Copilot
AI
    
    
    
      Sep 4, 2025 
    
  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Same unclear comment as above. The error handling should either return a default value or rethrow the exception rather than silently continuing with undefined behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already explained in the above comments.
Uh oh!
There was an error while loading. Please reload this page.