From e8bd4c9bc05e738fa55ddc3e0265741dc94b3cd6 Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Tue, 21 Mar 2023 13:28:16 +0530 Subject: feat(build): pull out Crowdin tasks to their own classes --- .../passwordstore/gradle/crowdin/BuildOnApiTask.kt | 40 ++++++++++++++ .../gradle/crowdin/StringCleanupTask.kt | 61 ++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt create mode 100644 build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt (limited to 'build-logic/src/main/kotlin') 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 + @get:Internal abstract val crowdinLogin: Property + @get:Internal abstract val crowdinKey: Property + + @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 + } +} -- cgit v1.2.3