Skip to content
This repository was archived by the owner on Oct 14, 2024. It is now read-only.

Use more modern path walking APIs #83

Merged
merged 3 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/kotlin-cli-util.api
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ public final class slack/cli/AppleSiliconCompat$Arch$Companion {
public final class slack/cli/CliUtilKt {
public static final fun cleanLineFormatting (Ljava/util/List;)Ljava/util/List;
public static final fun filterByExtension (Lkotlin/sequences/Sequence;Ljava/lang/String;)Lkotlin/sequences/Sequence;
public static final fun filterByExtensionPath (Lkotlin/sequences/Sequence;Ljava/lang/String;)Lkotlin/sequences/Sequence;
public static final fun filterByName (Lkotlin/sequences/Sequence;Ljava/lang/String;Z)Lkotlin/sequences/Sequence;
public static synthetic fun filterByName$default (Lkotlin/sequences/Sequence;Ljava/lang/String;ZILjava/lang/Object;)Lkotlin/sequences/Sequence;
public static final fun filterByNamePath (Lkotlin/sequences/Sequence;Ljava/lang/String;Z)Lkotlin/sequences/Sequence;
public static synthetic fun filterByNamePath$default (Lkotlin/sequences/Sequence;Ljava/lang/String;ZILjava/lang/Object;)Lkotlin/sequences/Sequence;
public static final fun skipBuildAndCacheDirs (Lkotlin/io/FileTreeWalk;)Lkotlin/io/FileTreeWalk;
public static final fun skipBuildAndCacheDirs (Lkotlin/io/path/FileVisitorBuilder;)V
public static final fun walkEachFile (Ljava/nio/file/Path;IZLkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
public static synthetic fun walkEachFile$default (Ljava/nio/file/Path;IZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/sequences/Sequence;
}

public final class slack/cli/CliktExtensionsKt {
Expand Down
59 changes: 59 additions & 0 deletions src/main/kotlin/slack/cli/CliUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
package slack.cli

import java.io.File
import java.nio.file.FileVisitResult
import java.nio.file.Path
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.FileVisitorBuilder
import kotlin.io.path.extension
import kotlin.io.path.name
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.visitFileTree

/**
* Skips `build` and cache directories (starting with `.`, like `.gradle`) in [FileTreeWalks]
Expand All @@ -27,6 +35,38 @@ public fun FileTreeWalk.skipBuildAndCacheDirs(): FileTreeWalk {
return onEnter { dir -> !dir.name.startsWith(".") && dir.name != "build" }
}

/**
* Skips `build` and cache directories (starting with `.`, like `.gradle`) in [FileTreeWalks]
* [FileTreeWalk].
*/
@ExperimentalPathApi
public fun FileVisitorBuilder.skipBuildAndCacheDirs() {
return onPreVisitDirectory { dir, _ ->
if (dir.name.startsWith(".") || dir.name == "build") {
FileVisitResult.SKIP_SUBTREE
} else {
FileVisitResult.CONTINUE
}
}
}

@OptIn(ExperimentalPathApi::class)
public fun Path.walkEachFile(
maxDepth: Int = Int.MAX_VALUE,
followLinks: Boolean = false,
builderAction: FileVisitorBuilder.() -> Unit = {},
): Sequence<Path> {
val files = mutableListOf<Path>()
visitFileTree(maxDepth, followLinks) {
builderAction()
onVisitFile { file, _ ->
files.add(file)
FileVisitResult.CONTINUE
}
}
return files.asSequence()
}

/** Filters by a specific [extension]. */
public fun Sequence<File>.filterByExtension(extension: String): Sequence<File> {
return filter { it.extension == extension }
Expand All @@ -44,6 +84,25 @@ public fun Sequence<File>.filterByName(
}
}

/** Filters by a specific [extension]. */
@JvmName("filterByExtensionPath")
public fun Sequence<Path>.filterByExtension(extension: String): Sequence<Path> {
return filter { it.extension == extension }
}

/** Filters by a specific [name]. */
@JvmName("filterByNamePath")
public fun Sequence<Path>.filterByName(
name: String,
withoutExtension: Boolean = true
): Sequence<Path> {
return if (withoutExtension) {
filter { it.nameWithoutExtension == name }
} else {
filter { it.name == name }
}
}

public fun List<String>.cleanLineFormatting(): List<String> {
val cleanedBlankLines = mutableListOf<String>()
var blankLineCount = 0
Expand Down
11 changes: 4 additions & 7 deletions src/main/kotlin/slack/cli/gradle/GradleSettingsVerifierCli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import kotlin.system.exitProcess
import slack.cli.CommandFactory
import slack.cli.projectDirOption
import slack.cli.skipBuildAndCacheDirs
import slack.cli.walkEachFile

/** A CLI that verifies a given settings file has only valid projects. */
public class GradleSettingsVerifierCli : CliktCommand(help = DESCRIPTION) {
Expand Down Expand Up @@ -94,20 +95,16 @@ public class GradleSettingsVerifierCli : CliktCommand(help = DESCRIPTION) {
val projectsViaBuildFiles =
projectDir
.absolute()
.toFile()
.walkTopDown()
.skipBuildAndCacheDirs()
.walkEachFile { skipBuildAndCacheDirs() }
.filter { it.name == "build.gradle.kts" }
.associate {
val path = it.toPath()
// Get the gradle path relative to the root project dir as the key
.associateBy { path -> // Get the gradle path relative to the root project dir as the key
val gradlePath =
":" +
path.parent // project dir
.relativeTo(projectDir)
.toString()
.replace(File.separator, ":")
gradlePath to path
gradlePath
}
.filterValues { it.parent != projectDir }
.plus(implicitPaths)
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/slack/cli/lint/LintBaselineMergerCli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import io.github.detekt.sarif4k.Tool
import io.github.detekt.sarif4k.ToolComponent
import io.github.detekt.sarif4k.Version
import java.nio.file.Path
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.createFile
import kotlin.io.path.createParentDirectories
import kotlin.io.path.deleteIfExists
Expand All @@ -63,6 +64,7 @@ import slack.cli.projectDirOption
import slack.cli.sarif.BASELINE_SUPPRESSION
import slack.cli.sarif.levelOption
import slack.cli.skipBuildAndCacheDirs
import slack.cli.walkEachFile

/** A CLI that merges lint baseline xml files into one. */
public class LintBaselineMergerCli : CliktCommand(DESCRIPTION) {
Expand Down Expand Up @@ -165,13 +167,11 @@ public class LintBaselineMergerCli : CliktCommand(DESCRIPTION) {
SarifSerializer.toJson(outputSarif).let { outputFile.writeText(it) }
}

@OptIn(ExperimentalPathApi::class)
private fun parseIssues(): Map<LintIssues.LintIssue, Path> {
val issues = mutableMapOf<LintIssues.LintIssue, Path>()
projectDir
.toFile()
.walkTopDown()
.skipBuildAndCacheDirs()
.map { it.toPath() }
.walkEachFile { skipBuildAndCacheDirs() }
.filter { it.name == baselineFileName }
.forEach { file ->
if (verbose) println("Parsing $file")
Expand Down
29 changes: 17 additions & 12 deletions src/main/kotlin/slack/cli/sarif/MergeSarifReports.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,25 @@ import com.google.auto.service.AutoService
import io.github.detekt.sarif4k.SarifSchema210
import io.github.detekt.sarif4k.SarifSerializer
import java.nio.file.Path
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.absolute
import kotlin.io.path.absolutePathString
import kotlin.io.path.createParentDirectories
import kotlin.io.path.deleteIfExists
import kotlin.io.path.exists
import kotlin.io.path.extension
import kotlin.io.path.isRegularFile
import kotlin.io.path.name
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.readText
import kotlin.io.path.relativeTo
import kotlin.io.path.walk
import kotlin.io.path.writeText
import kotlin.system.exitProcess
import slack.cli.CommandFactory
import slack.cli.projectDirOption
import slack.cli.skipBuildAndCacheDirs
import slack.cli.walkEachFile

public class MergeSarifReports : CliktCommand(help = DESCRIPTION) {

Expand Down Expand Up @@ -92,23 +100,22 @@ public class MergeSarifReports : CliktCommand(help = DESCRIPTION) {
outputFile.createParentDirectories()
}

@OptIn(ExperimentalPathApi::class)
private fun findBuildFiles(): List<Path> {
log("Finding build files in ${projectDir.toFile().canonicalFile}")
val buildFiles =
projectDir
.toFile()
.canonicalFile
.walkTopDown()
.skipBuildAndCacheDirs()
.absolute()
.walkEachFile { skipBuildAndCacheDirs() }
.filter { it.name == "build.gradle.kts" }
.map { it.toPath() }
.toList()
log("${buildFiles.size} build files found")
return buildFiles
}

private fun String.prefixPathWith(prefix: String) = "$prefix/$this"

@OptIn(ExperimentalPathApi::class)
private fun findSarifFiles(): List<Path> {
require(filePrefix != null || argFiles.isNotEmpty()) {
"Must specify either --file-prefix or pass files as arguments"
Expand All @@ -129,13 +136,11 @@ public class MergeSarifReports : CliktCommand(help = DESCRIPTION) {
buildFiles.asSequence().flatMap { buildFile ->
val reportsDir = buildFile.parent.resolve("build/reports")
if (reportsDir.exists()) {
reportsDir
.toFile()
.walkTopDown()
.filter {
it.isFile && it.extension == "sarif" && it.nameWithoutExtension.startsWith(prefix)
}
.map { it.toPath() }
reportsDir.walk().filter {
it.isRegularFile() &&
it.extension == "sarif" &&
it.nameWithoutExtension.startsWith(prefix)
}
} else {
emptySequence()
}
Expand Down