1616package slack.cli.sarif
1717
1818import com.github.ajalt.clikt.core.CliktCommand
19+ import com.github.ajalt.clikt.parameters.arguments.argument
20+ import com.github.ajalt.clikt.parameters.arguments.multiple
1921import com.github.ajalt.clikt.parameters.options.flag
2022import com.github.ajalt.clikt.parameters.options.option
2123import com.github.ajalt.clikt.parameters.options.required
22- import com.github.ajalt.clikt.parameters.types.file
24+ import com.github.ajalt.clikt.parameters.types.path
2325import io.github.detekt.sarif4k.SarifSchema210
2426import io.github.detekt.sarif4k.SarifSerializer
25- import java.io.File
27+ import java.nio.file.Path
2628import kotlin.io.path.absolutePathString
29+ import kotlin.io.path.createParentDirectories
30+ import kotlin.io.path.deleteIfExists
31+ import kotlin.io.path.exists
32+ import kotlin.io.path.readText
33+ import kotlin.io.path.relativeTo
34+ import kotlin.io.path.writeText
2735import kotlin.system.exitProcess
2836import slack.cli.projectDirOption
2937import slack.cli.skipBuildAndCacheDirs
@@ -34,8 +42,10 @@ public class MergeSarifReports :
3442 ) {
3543
3644 private val projectDir by projectDirOption()
37- private val outputFile: File by option(" --output-file" ).file().required()
38- private val filePrefix by option(" --file-prefix" ).required()
45+ private val outputFile by option(" --output-file" ).path().required()
46+ private val filePrefix by option(" --file-prefix" )
47+ private val argFiles by
48+ argument(" --files" ).path(mustExist = true , canBeDir = false , mustBeReadable = true ).multiple()
3949 private val verbose by option(" --verbose" , " -v" ).flag()
4050 private val remapSrcRoots by
4151 option(
@@ -68,13 +78,11 @@ public class MergeSarifReports :
6878 }
6979
7080 private fun prepareOutput () {
71- if (outputFile.exists()) {
72- outputFile.delete()
73- }
74- outputFile.parentFile?.mkdirs()
81+ outputFile.deleteIfExists()
82+ outputFile.createParentDirectories()
7583 }
7684
77- private fun findBuildFiles (): List <File > {
85+ private fun findBuildFiles (): List <Path > {
7886 log(" Finding build files in ${projectDir.toFile().canonicalFile} " )
7987 val buildFiles =
8088 projectDir
@@ -83,32 +91,49 @@ public class MergeSarifReports :
8391 .walkTopDown()
8492 .skipBuildAndCacheDirs()
8593 .filter { it.name == " build.gradle.kts" }
94+ .map { it.toPath() }
8695 .toList()
8796 log(" ${buildFiles.size} build files found" )
8897 return buildFiles
8998 }
9099
91100 private fun String.prefixPathWith (prefix : String ) = " $prefix /$this "
92101
93- private fun findSarifFiles (): List <File > {
94- // Find build files first, this gives us an easy hook to then go looking in build/reports dirs.
95- // Otherwise we don't have a way to easily exclude populated build dirs that would take forever.
96- val buildFiles = findBuildFiles()
102+ private fun findSarifFiles (): List <Path > {
103+ require(filePrefix != null || argFiles.isNotEmpty()) {
104+ " Must specify either --file-prefix or pass files as arguments "
105+ }
97106
98- log(" Finding sarif files" )
99- return buildFiles
100- .asSequence()
101- .flatMap { buildFile ->
102- val reportsDir = File (buildFile.parentFile, " build/reports" )
103- if (reportsDir.exists()) {
104- reportsDir.walkTopDown().filter {
105- it.isFile && it.extension == " sarif" && it.nameWithoutExtension.startsWith(filePrefix)
107+ val files = mutableListOf<Path >()
108+
109+ files + = argFiles
110+
111+ filePrefix?.let { prefix ->
112+ // Find build files first, this gives us an easy hook to then go looking in build/reports
113+ // dirs.
114+ // Otherwise we don't have a way to easily exclude populated build dirs that would take
115+ // forever.
116+ val buildFiles = findBuildFiles()
117+
118+ log(" Finding sarif files" )
119+ files + =
120+ buildFiles.asSequence().flatMap { buildFile ->
121+ val reportsDir = buildFile.parent.resolve(" build/reports" )
122+ if (reportsDir.exists()) {
123+ reportsDir
124+ .toFile()
125+ .walkTopDown()
126+ .filter {
127+ it.isFile && it.extension == " sarif" && it.nameWithoutExtension.startsWith(prefix)
128+ }
129+ .map { it.toPath() }
130+ } else {
131+ emptySequence()
106132 }
107- } else {
108- emptySequence()
109133 }
110- }
111- .toList()
134+ }
135+
136+ return files
112137 }
113138
114139 /* *
@@ -143,16 +168,16 @@ public class MergeSarifReports :
143168 * libraries/lib/src/main/java/com/example/app/LibActivity.kt
144169 * ```
145170 */
146- private fun SarifSchema210.remapSrcRoots (sarifFile : File ): SarifSchema210 {
147- // <module>/───────────────────────────────── ┐
148- // build/─────────────────────┐ │
149- // reports/──────┐ │ │
150- // ▼ ▼ ▼
151- val module = sarifFile.parentFile.parentFile.parentFile
152- check(File ( module, " build.gradle.kts" ).exists()) {
171+ private fun SarifSchema210.remapSrcRoots (sarifFile : Path ): SarifSchema210 {
172+ // <module>/─────────────────────────┐
173+ // build/─────────────────┐ │
174+ // reports/──────┐ │ │
175+ // ▼ ▼ ▼
176+ val module = sarifFile.parent.parent.parent
177+ check(module.resolve( " build.gradle.kts" ).exists()) {
153178 " Expected to find build.gradle.kts in $module "
154179 }
155- val modulePrefix = module.toRelativeString (projectDir.toFile() )
180+ val modulePrefix = module.relativeTo (projectDir).toString( )
156181 return copy(
157182 runs =
158183 runs.map { run ->
@@ -232,7 +257,7 @@ public class MergeSarifReports :
232257 )
233258 }
234259
235- private fun loadSarifs (inputs : List <File >): List <SarifSchema210 > {
260+ private fun loadSarifs (inputs : List <Path >): List <SarifSchema210 > {
236261 return inputs.map { sarifFile ->
237262 log(" Parsing $sarifFile " )
238263 val parsed = SarifSerializer .fromJson(sarifFile.readText())
@@ -249,7 +274,7 @@ public class MergeSarifReports :
249274 }
250275 }
251276
252- private fun merge (inputs : List <File >) {
277+ private fun merge (inputs : List <Path >) {
253278 log(" Parsing ${inputs.size} sarif files" )
254279 val sarifs = loadSarifs(inputs)
255280
0 commit comments