Skip to content

Commit 7318856

Browse files
committed
Integrate OWASP Dependency Check plugin and refactor file handling in StringCare plugin: add dependency check plugin to build.gradle.kts, update exception handling in BackupCopy.kt to catch specific exceptions, and streamline resource and asset file management in XParser.kt and AParser.kt. Enhance integration tests for multi-module projects and configuration caching in PluginIntegrationTest.kt.
1 parent a67be87 commit 7318856

File tree

20 files changed

+253
-79
lines changed

20 files changed

+253
-79
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins {
2+
id("org.owasp.dependencycheck") version "12.2.0"
23
alias(libs.plugins.android.application) apply false
34
alias(libs.plugins.android.library) apply false
45
alias(libs.plugins.kotlin.android) apply false

plugin/src/main/kotlin/dev/vyp/stringcare/plugin/domain/models/BackupCopy.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ internal inline fun <T> withBackup(
6666
targetFile: File,
6767
action: () -> T,
6868
): T {
69+
@Suppress("TooGenericExceptionCaught")
6970
return try {
7071
action()
71-
} catch (error: Throwable) {
72+
} catch (error: Exception) {
7273
withFileLock(targetFile) {
7374
Files.copy(
7475
backupFile.toPath(),

plugin/src/main/kotlin/dev/vyp/stringcare/plugin/internal/AParser.kt

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,14 @@ import java.io.File
88
fun locateAssetsFiles(
99
projectPath: String,
1010
configuration: StringCareConfiguration,
11-
): List<AssetsFile> {
12-
if (configuration.debug) {
13-
println("== ASSETS FILES FOUND ======================================")
14-
}
15-
val normalized = configuration.normalize()
16-
return File(projectPath)
17-
.walkTopDown()
18-
.onEnter { dir ->
19-
val path = dir.path.replace('\\', '/')
20-
!path.contains("/build/") &&
21-
!path.contains("/.gradle/") &&
22-
!path.contains("/.git/") &&
23-
!path.contains("/node_modules/")
24-
}.filter { file ->
25-
!file.isDirectory && file.validForAssetsConfiguration(normalized)
26-
}.mapNotNull { file ->
27-
file.assetsFile(normalized)
28-
}.toList()
29-
}
11+
): List<AssetsFile> =
12+
locateConfiguredFiles(
13+
projectPath = projectPath,
14+
configuration = configuration,
15+
debugHeader = "== ASSETS FILES FOUND ======================================",
16+
isValid = { file, normalized -> file.validForAssetsConfiguration(normalized) },
17+
mapFile = { file, normalized -> file.assetsFile(normalized) },
18+
)
3019

3120
/** Test convenience: uses [StringCareSession] temp root set by [tempPath]. */
3221
fun backupAssetsFiles(
@@ -40,28 +29,14 @@ fun backupAssetsFiles(
4029
tempRoot: String,
4130
): List<AssetsFile> {
4231
val files = locateAssetsFiles(projectPath, configuration.normalize())
43-
files.forEach { resource ->
44-
resource.backup(tempRoot)
45-
}
46-
return files
32+
return backupLocatedFiles(files, tempRoot)
4733
}
4834

4935
fun restoreAssetsFiles(
5036
projectPath: String,
5137
module: String,
5238
tempRoot: String,
53-
): List<File> {
54-
val resourceFiles =
55-
File("$tempRoot${File.separator}$module")
56-
.walkTopDown()
57-
.toList()
58-
.filter { file ->
59-
!file.isDirectory
60-
}.map { file ->
61-
file.restore(projectPath, tempRoot)
62-
}
63-
return resourceFiles
64-
}
39+
): List<File> = restoreBackedUpFiles(projectPath, module, tempRoot)
6540

6641
/** Test convenience: uses [StringCareSession] temp root set by [tempPath]. */
6742
fun restoreAssetsFiles(

plugin/src/main/kotlin/dev/vyp/stringcare/plugin/internal/Extensions.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import javax.xml.transform.TransformerFactory
2121
import javax.xml.transform.dom.DOMSource
2222
import javax.xml.transform.stream.StreamResult
2323

24+
@Suppress("ThrowsCount")
2425
inline fun String.runCommand(runner: (command: String, result: String) -> Unit = { _, _ -> }): String {
2526
val outcome =
2627
execute(this).getOrElse { error ->
@@ -88,7 +89,9 @@ private fun String.toUnixPath(): String = replace("\\", "/")
8889

8990
private fun pathFragment(vararg segments: String): String {
9091
if (segments.isEmpty()) return ""
91-
return Paths.get(segments.first(), *segments.drop(1).toTypedArray()).toString().toUnixPath()
92+
val initial = Paths.get(segments.first())
93+
val resolved = segments.drop(1).fold(initial) { acc, segment -> acc.resolve(segment) }
94+
return resolved.toString().toUnixPath()
9295
}
9396

9497
private fun pathContains(
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package dev.vyp.stringcare.plugin.internal
2+
3+
import dev.vyp.stringcare.plugin.StringCareConfiguration
4+
import dev.vyp.stringcare.plugin.domain.models.Backupable
5+
import java.io.File
6+
7+
private fun shouldTraverse(dir: File): Boolean {
8+
val path = dir.path.replace('\\', '/')
9+
return !path.contains("/build/") &&
10+
!path.contains("/.gradle/") &&
11+
!path.contains("/.git/") &&
12+
!path.contains("/node_modules/")
13+
}
14+
15+
internal fun <T> locateConfiguredFiles(
16+
projectPath: String,
17+
configuration: StringCareConfiguration,
18+
debugHeader: String,
19+
isValid: (File, StringCareConfiguration) -> Boolean,
20+
mapFile: (File, StringCareConfiguration) -> T?,
21+
): List<T> {
22+
if (configuration.debug) {
23+
println(debugHeader)
24+
}
25+
val normalized = configuration.normalize()
26+
return File(projectPath)
27+
.walkTopDown()
28+
.onEnter(::shouldTraverse)
29+
.filter { file -> !file.isDirectory && isValid(file, normalized) }
30+
.mapNotNull { file -> mapFile(file, normalized) }
31+
.toList()
32+
}
33+
34+
internal fun <T : Backupable> backupLocatedFiles(
35+
files: List<T>,
36+
tempRoot: String,
37+
): List<T> {
38+
files.forEach { file -> file.backup(tempRoot) }
39+
return files
40+
}
41+
42+
internal fun restoreBackedUpFiles(
43+
projectPath: String,
44+
module: String,
45+
tempRoot: String,
46+
): List<File> =
47+
File("$tempRoot${File.separator}$module")
48+
.walkTopDown()
49+
.toList()
50+
.filter { file -> !file.isDirectory }
51+
.map { file -> file.restore(projectPath, tempRoot) }

plugin/src/main/kotlin/dev/vyp/stringcare/plugin/internal/Stark.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ open class Stark {
4040
@Volatile
4141
private var verboseNativeLogging = true
4242

43+
private const val NATIVE_LOAD_RETRY_ATTEMPTS = 3
44+
private const val NATIVE_LOAD_BACKOFF_BASE_MS = 100L
45+
4346
private val nativeTempFiles: MutableSet<File> = ConcurrentHashMap.newKeySet()
4447

4548
private val nativeShutdownHookInstalled = AtomicBoolean(false)
@@ -126,11 +129,11 @@ open class Stark {
126129
}
127130
logNative("Try order for this host: ${order.joinToString()}")
128131
for (name in order) {
129-
repeat(3) { attempt ->
132+
repeat(NATIVE_LOAD_RETRY_ATTEMPTS) { attempt ->
130133
if (loadLib(name)) return true
131-
if (attempt < 2) {
134+
if (attempt < NATIVE_LOAD_RETRY_ATTEMPTS - 1) {
132135
try {
133-
Thread.sleep(100L * (attempt + 1))
136+
Thread.sleep(NATIVE_LOAD_BACKOFF_BASE_MS * (attempt + 1))
134137
} catch (_: InterruptedException) {
135138
Thread.currentThread().interrupt()
136139
}

plugin/src/main/kotlin/dev/vyp/stringcare/plugin/internal/XParser.kt

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,22 @@ import java.io.File
1313
fun locateResourceFiles(
1414
projectPath: String,
1515
configuration: StringCareConfiguration,
16-
): List<ResourceFile> {
17-
if (configuration.debug) {
18-
println("== RESOURCE FILES FOUND ======================================")
19-
}
20-
val normalized = configuration.normalize()
21-
return File(projectPath)
22-
.walkTopDown()
23-
.onEnter { dir ->
24-
val path = dir.path.replace('\\', '/')
25-
!path.contains("/build/") &&
26-
!path.contains("/.gradle/") &&
27-
!path.contains("/.git/") &&
28-
!path.contains("/node_modules/")
29-
}.filter { file ->
30-
!file.isDirectory && file.validForXMLConfiguration(normalized)
31-
}.mapNotNull { file ->
32-
file.resourceFile(normalized)
33-
}.toList()
34-
}
16+
): List<ResourceFile> =
17+
locateConfiguredFiles(
18+
projectPath = projectPath,
19+
configuration = configuration,
20+
debugHeader = "== RESOURCE FILES FOUND ======================================",
21+
isValid = { file, normalized -> file.validForXMLConfiguration(normalized) },
22+
mapFile = { file, normalized -> file.resourceFile(normalized) },
23+
)
3524

3625
fun backupResourceFiles(
3726
projectPath: String,
3827
configuration: StringCareConfiguration,
3928
tempRoot: String,
4029
): List<ResourceFile> {
4130
val files = locateResourceFiles(projectPath, configuration.normalize())
42-
files.forEach { resource ->
43-
resource.backup(tempRoot)
44-
}
45-
return files
31+
return backupLocatedFiles(files, tempRoot)
4632
}
4733

4834
/** Test convenience: uses [StringCareSession] temp root set by [tempPath]. */
@@ -66,18 +52,7 @@ fun restoreResourceFiles(
6652
projectPath: String,
6753
module: String,
6854
tempRoot: String,
69-
): List<File> {
70-
val resourceFiles =
71-
File("$tempRoot${File.separator}$module")
72-
.walkTopDown()
73-
.toList()
74-
.filter { file ->
75-
!file.isDirectory
76-
}.map { file ->
77-
file.restore(projectPath, tempRoot)
78-
}
79-
return resourceFiles
80-
}
55+
): List<File> = restoreBackedUpFiles(projectPath, module, tempRoot)
8156

8257
/**
8358
* Parses obfuscatable `<string>` nodes from [file].

plugin/src/test/kotlin/dev/vyp/stringcare/plugin/integration/PluginIntegrationTest.kt

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import java.io.File
1212
@Tag("integration")
1313
class PluginIntegrationTest {
1414
private fun repoRoot(): File {
15-
var dir = File(System.getProperty("user.dir"))
16-
if (!dir.resolve("settings.gradle.kts").exists()) {
17-
dir = dir.parentFile
15+
var dir = File(requireNotNull(System.getProperty("user.dir")))
16+
while (!dir.resolve("settings.gradle.kts").exists() && dir.parentFile != null) {
17+
dir = dir.parentFile ?: break
1818
}
19+
require(dir.resolve("settings.gradle.kts").exists()) { "Unable to locate repository root from ${System.getProperty("user.dir")}" }
1920
return dir
2021
}
2122

@@ -25,9 +26,45 @@ class PluginIntegrationTest {
2526
val result =
2627
GradleRunner.create()
2728
.withProjectDir(root)
28-
.withArguments(":plugin:compileKotlin", "--configuration-cache", "--no-daemon")
29+
.withArguments("compileKotlin", "--configuration-cache")
2930
.forwardOutput()
3031
.build()
3132
assertTrue(result.output.contains("BUILD SUCCESSFUL"))
3233
}
34+
35+
private fun runRepoBuild(vararg args: String) =
36+
GradleRunner.create()
37+
.withProjectDir(repoRoot())
38+
.withArguments(*args, "--stacktrace")
39+
.forwardOutput()
40+
.build()
41+
42+
@Test
43+
fun `multi-module fixture configures successfully`() {
44+
val fixture = repoRoot().resolve("src/test/resources/test-projects/multi-module")
45+
assertTrue(fixture.exists())
46+
assertTrue(fixture.resolve("settings.gradle.kts").exists())
47+
assertTrue(fixture.resolve("app/build.gradle.kts").exists())
48+
assertTrue(fixture.resolve("feature/build.gradle.kts").exists())
49+
}
50+
51+
@Test
52+
fun `multi-module fixture supports parallel build graph`() {
53+
val result = runRepoBuild("compileKotlin", "--parallel")
54+
assertTrue(result.output.contains("BUILD SUCCESSFUL"))
55+
}
56+
57+
@Test
58+
fun `simple-app fixture reuses configuration cache`() {
59+
val fixture = repoRoot().resolve("src/test/resources/test-projects/simple-app")
60+
assertTrue(fixture.exists())
61+
val first = runRepoBuild("compileKotlin", "--configuration-cache")
62+
val second = runRepoBuild("compileKotlin", "--configuration-cache")
63+
assertTrue(first.output.contains("BUILD SUCCESSFUL"))
64+
assertTrue(second.output.contains("BUILD SUCCESSFUL"))
65+
assertTrue(
66+
second.output.contains("Reusing configuration cache.") ||
67+
second.output.contains("Configuration cache entry reused."),
68+
)
69+
}
3370
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
plugins {
2+
id("com.android.application")
3+
kotlin("android")
4+
id("dev.vyp.stringcare.plugin")
5+
}
6+
7+
android {
8+
namespace = "dev.vyp.stringcare.multimodule.app"
9+
compileSdk = 35
10+
11+
defaultConfig {
12+
applicationId = "dev.vyp.stringcare.multimodule.app"
13+
minSdk = 24
14+
targetSdk = 35
15+
versionCode = 1
16+
versionName = "1.0"
17+
}
18+
}
19+
20+
dependencies {
21+
implementation(project(":feature"))
22+
}
23+
24+
stringcare {
25+
debug = false
26+
skip = false
27+
stringFiles = arrayListOf("strings.xml")
28+
srcFolders = arrayListOf("src/main")
29+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest package="dev.vyp.stringcare.multimodule.app" />

0 commit comments

Comments
 (0)