summaryrefslogtreecommitdiff
path: root/build-logic/src/main/kotlin
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2024-08-18 13:40:12 +0530
committerGitHub <noreply@github.com>2024-08-18 08:10:12 +0000
commit71161e20f8610b989e7a0d803249e17362e10ddc (patch)
treedf45bc53ac65e3ec00739af3a7a93d6348f03032 /build-logic/src/main/kotlin
parent919f708df28c98a8a124554717710e149b138a1b (diff)
fix: rework Crowdin integration (#3175)
* chore: set up Crowdin configuration * fix(app): sync strings from Crowdin Closes #3174 * fix(ci): use crowdin/github-action instead of homebrew setup * fix(build): remove obsolete Crowdin plugin
Diffstat (limited to 'build-logic/src/main/kotlin')
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/OkHttp.kt6
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt64
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinExtension.kt22
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinPlugin.kt70
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt66
-rw-r--r--build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/api/ListProjects.kt14
6 files changed, 0 insertions, 242 deletions
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/OkHttp.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/OkHttp.kt
index e3d33432..7f26d427 100644
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/OkHttp.kt
+++ b/build-logic/src/main/kotlin/app/passwordstore/gradle/OkHttp.kt
@@ -13,12 +13,6 @@ object OkHttp {
private val certificatePinner =
CertificatePinner.Builder()
.add(
- "api.crowdin.com",
- "sha256/qKpGqFXXIteblI82BcMyRX0eC2o7lpL9XVInWKIG7rc=",
- "sha256/DxH4tt40L+eduF6szpY6TONlxhZhBd+pJ9wbHlQ2fuw=",
- "sha256/++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=",
- )
- .add(
"publicsuffix.org",
"sha256/Ov/MkC2OkVtTp9MdY+uXOKAuV2Birfdeazval8seMZM=",
"sha256/jQJTbIh0grw0/1TkHSumWb+Fs0Ggogr621gT3PvPKG0=",
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
deleted file mode 100644
index 0fc143d1..00000000
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/BuildOnApiTask.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.gradle.crowdin
-
-import app.passwordstore.gradle.OkHttp
-import app.passwordstore.gradle.crowdin.api.ListProjects
-import com.squareup.moshi.Moshi
-import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-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 moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
- val projectAdapter = moshi.adapter(ListProjects::class.java)
- val projectRequest =
- Request.Builder()
- .url("$CROWDIN_BASE_URL/projects")
- .header("Authorization", "Bearer ${crowdinKey.get()}")
- .get()
- .build()
- OkHttp.CLIENT.newCall(projectRequest).execute().use { response ->
- val projects = projectAdapter.fromJson(response.body.source())
- if (projects != null) {
- val identifier =
- projects.projects
- .first { data -> data.project.identifier == crowdinIdentifier.get() }
- .project
- .id
- .toString()
- val buildRequest =
- Request.Builder()
- .url(CROWDIN_BUILD_API_URL.format(identifier))
- .header("Authorization", "Bearer ${crowdinKey.get()}")
- .post("{}".toRequestBody("application/json".toMediaType()))
- .build()
- OkHttp.CLIENT.newCall(buildRequest).execute().close()
- }
- }
- }
-
- private companion object {
-
- private const val CROWDIN_BASE_URL = "https://api.crowdin.com/api/v2"
- private const val CROWDIN_BUILD_API_URL = "$CROWDIN_BASE_URL/projects/%s/translations/builds"
- }
-}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinExtension.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinExtension.kt
deleted file mode 100644
index b78d66d3..00000000
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinExtension.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.gradle.crowdin
-
-import org.gradle.api.provider.Property
-
-/** Extension for configuring [CrowdinDownloadPlugin] */
-interface CrowdinExtension {
-
- /** Configure the project name on Crowdin */
- val crowdinIdentifier: Property<String>
-
- /**
- * Don't delete downloaded and extracted translation archives from build directory.
- *
- * Useful for debugging.
- */
- val skipCleanup: Property<Boolean>
-}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinPlugin.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinPlugin.kt
deleted file mode 100644
index f127507a..00000000
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/CrowdinPlugin.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.gradle.crowdin
-
-import de.undercouch.gradle.tasks.download.Download
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.Delete
-import org.gradle.kotlin.dsl.create
-import org.gradle.kotlin.dsl.register
-
-@Suppress("Unused")
-class CrowdinDownloadPlugin : Plugin<Project> {
-
- override fun apply(project: Project) {
- with(project) {
- val extension = extensions.create<CrowdinExtension>("crowdin")
- val login = providers.environmentVariable("CROWDIN_LOGIN")
- val key = providers.environmentVariable("CROWDIN_PROJECT_KEY")
- val buildOnApi =
- if (login.isPresent && key.isPresent) {
- tasks.register<BuildOnApiTask>("buildOnApi") {
- crowdinIdentifier.set(extension.crowdinIdentifier)
- crowdinLogin.set(login)
- crowdinKey.set(key)
- }
- } else {
- null
- }
- val downloadCrowdin =
- tasks.register<Download>("downloadCrowdin") {
- if (buildOnApi != null) dependsOn(buildOnApi)
- src(
- "https://crowdin.com/backend/download/project/${extension.crowdinIdentifier.get()}.zip"
- )
- dest(layout.buildDirectory.file("translations.zip"))
- overwrite(true)
- }
- val extractCrowdin =
- tasks.register<Copy>("extractCrowdin") {
- from(zipTree(downloadCrowdin.map { it.outputFiles.first() }))
- into(layout.buildDirectory.dir("translations"))
- }
- val extractStrings =
- tasks.register<Copy>("extractStrings") {
- from(extractCrowdin.map { it.destinationDir })
- into(layout.projectDirectory.dir("src"))
- }
- val removeIncompleteStrings =
- tasks.register<StringCleanupTask>("removeIncompleteStrings") {
- sourceDirectory.set(
- objects.directoryProperty().fileProvider(extractStrings.map { it.destinationDir })
- )
- }
- tasks.register<Delete>("crowdin") {
- dependsOn(removeIncompleteStrings)
- delete =
- if (extension.skipCleanup.getOrElse(false)) {
- emptySet()
- } else {
- setOf(extractStrings.map { it.source }, downloadCrowdin.map { it.outputFiles })
- }
- }
- }
- }
-}
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
deleted file mode 100644
index 7f91368c..00000000
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/StringCleanupTask.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-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
- }
-}
diff --git a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/api/ListProjects.kt b/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/api/ListProjects.kt
deleted file mode 100644
index 933d7e02..00000000
--- a/build-logic/src/main/kotlin/app/passwordstore/gradle/crowdin/api/ListProjects.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.gradle.crowdin.api
-
-import com.squareup.moshi.Json
-
-data class ListProjects(@Json(name = "data") val projects: List<ProjectData>)
-
-data class ProjectData(@Json(name = "data") val project: Project)
-
-data class Project(val id: Long, val identifier: String)