summaryrefslogtreecommitdiff
path: root/build-logic
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2023-03-21 13:28:16 +0530
committerHarsh Shandilya <me@msfjarvis.dev>2023-03-21 13:45:12 +0530
commite8bd4c9bc05e738fa55ddc3e0265741dc94b3cd6 (patch)
treea0fd18f45ff3be724a60964a1767fd0dd83a205f /build-logic
parent9f554376e7dad8b9b3d53d4251f7ab26bbdaa4eb (diff)
feat(build): pull out Crowdin tasks to their own classes
Diffstat (limited to 'build-logic')
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt40
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt61
2 files changed, 101 insertions, 0 deletions
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt
new file mode 100644
index 00000000..070ec2e9
--- /dev/null
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt
@@ -0,0 +1,40 @@
+package app.passwordstore.gradle.crowdin
+
+import java.util.concurrent.TimeUnit
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.gradle.api.DefaultTask
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
+
+@DisableCachingByDefault(because = "This calls into a remote API and has nothing to cache")
+abstract class BuildOnApiTask : DefaultTask() {
+
+ @get:Input abstract val crowdinIdentifier: Property<String>
+ @get:Internal abstract val crowdinLogin: Property<String>
+ @get:Internal abstract val crowdinKey: Property<String>
+
+ @TaskAction
+ fun doWork() {
+ val client =
+ OkHttpClient.Builder()
+ .connectTimeout(5, TimeUnit.MINUTES)
+ .writeTimeout(5, TimeUnit.MINUTES)
+ .readTimeout(5, TimeUnit.MINUTES)
+ .callTimeout(10, TimeUnit.MINUTES)
+ .build()
+ val url =
+ CROWDIN_BUILD_API_URL.format(crowdinIdentifier.get(), crowdinLogin.get(), crowdinKey.get())
+ val request = Request.Builder().url(url).get().build()
+ client.newCall(request).execute().close()
+ }
+
+ private companion object {
+
+ private const val CROWDIN_BUILD_API_URL =
+ "https://api.crowdin.com/api/project/%s/export?login=%s&account-key=%s"
+ }
+}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt
new file mode 100644
index 00000000..daa38134
--- /dev/null
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt
@@ -0,0 +1,61 @@
+package app.passwordstore.gradle.crowdin
+
+import java.io.File
+import javax.xml.parsers.DocumentBuilderFactory
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
+import org.w3c.dom.Document
+
+@DisableCachingByDefault(because = "The task runs quickly and has complicated semantics")
+abstract class StringCleanupTask : DefaultTask() {
+
+ @get:InputDirectory abstract val sourceDirectory: DirectoryProperty
+
+ @TaskAction
+ fun clean() {
+ val sourceSets = arrayOf("main", "nonFree")
+ for (sourceSet in sourceSets) {
+ val fileTreeWalk = sourceDirectory.dir("$sourceSet/res").get().asFile.walkTopDown()
+ val valuesDirectories =
+ fileTreeWalk.filter { it.isDirectory }.filter { it.name.startsWith("values") }
+ val stringFiles = fileTreeWalk.filter { it.name == "strings.xml" }
+ val sourceFile =
+ stringFiles.firstOrNull { it.path.endsWith("values/strings.xml") }
+ ?: throw GradleException("No root strings.xml found in '$sourceSet' sourceSet")
+ val sourceDoc = parseDocument(sourceFile)
+ val baselineStringCount = countStrings(sourceDoc)
+ val threshold = 0.80 * baselineStringCount
+ stringFiles.forEach { file ->
+ if (file != sourceFile) {
+ val doc = parseDocument(file)
+ val stringCount = countStrings(doc)
+ if (stringCount < threshold) {
+ file.delete()
+ }
+ }
+ }
+ valuesDirectories.forEach { dir ->
+ if (dir.listFiles().isNullOrEmpty()) {
+ dir.delete()
+ }
+ }
+ }
+ }
+
+ private fun parseDocument(file: File): Document {
+ val dbFactory = DocumentBuilderFactory.newInstance()
+ val documentBuilder = dbFactory.newDocumentBuilder()
+ return documentBuilder.parse(file)
+ }
+
+ private fun countStrings(document: Document): Int {
+ // Normalization is beneficial for us
+ // https://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
+ document.documentElement.normalize()
+ return document.getElementsByTagName("string").length
+ }
+}