diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/BaseFirebaseLibraryPlugin.kt b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/BaseFirebaseLibraryPlugin.kt index 86b34c8eee8..08263df8763 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/BaseFirebaseLibraryPlugin.kt +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/BaseFirebaseLibraryPlugin.kt @@ -78,9 +78,9 @@ abstract class BaseFirebaseLibraryPlugin : Plugin { protected fun getIsPomValidTask(project: Project, firebaseLibrary: FirebaseLibraryExtension) { project.tasks.register("isPomDependencyValid") { - pomFilePath.value(project.file("build/publications/mavenAar/pom-default.xml")) - groupId.value(firebaseLibrary.groupId.get()) - artifactId.value(firebaseLibrary.artifactId.get()) + pomFile.set(project.layout.buildDirectory.file("publications/mavenAar/pom-default.xml")) + groupId.set(firebaseLibrary.groupId.get()) + artifactId.set(firebaseLibrary.artifactId.get()) dependsOn("generatePomFileForMavenAarPublication") } } diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/PomValidator.kt b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/PomValidator.kt index 70f87117fef..1cdf3d162cf 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/PomValidator.kt +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/PomValidator.kt @@ -14,65 +14,85 @@ package com.google.firebase.gradle.plugins -import java.io.File import java.net.URL -import javax.xml.parsers.DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.TaskAction -import org.w3c.dom.Document import org.w3c.dom.Element -import org.w3c.dom.Node -import org.w3c.dom.NodeList +/** + * Ensures that pom dependencies are not accidently downgraded. + * + * Compares the latest pom at gmaven for the given artifact with the one generate for the current + * release. + * + * @property pomFile The pom file for the current release + * @property artifactId The artifactId for the pom parent + * @property groupId The groupId for the pom parent + * @throws GradleException if a dependency is found with a degraded version + */ abstract class PomValidator : DefaultTask() { - @get:InputFile abstract val pomFilePath: Property + @get:InputFile abstract val pomFile: RegularFileProperty @get:Input abstract val artifactId: Property @get:Input abstract val groupId: Property @TaskAction fun run() { - val gMavenHelper = GmavenHelper(groupId.get(), artifactId.get()) - val latestReleasedVersion = gMavenHelper.getLatestReleasedVersion() - val releasedVersionPomUrl = gMavenHelper.getPomFileForVersion(latestReleasedVersion) - var output: String = diffWithPomFileUrl(releasedVersionPomUrl).trim() - if (output.isNotEmpty()) { - throw GradleException("${output}\nPlease fix the above errors") + var diff = diffWithPomFromURL(getLatestReleasePomUrl()) + + if (diff.isNotEmpty()) { + throw GradleException("Dependency version errors found:\n${diff}") } } - fun getMapFromXml(pomNodeList: NodeList): Map { - val pomMap = mutableMapOf() - for (i in 0..pomNodeList.length - 1) { - val node: Node = pomNodeList.item(i) - if (node.getNodeType() == Node.ELEMENT_NODE) { - val element = node as Element - val artifact = element.getElementsByTagName("artifactId").item(0).getTextContent() - val version = element.getElementsByTagName("version").item(0).getTextContent() - pomMap[artifact] = version - } + private fun getLatestReleasePomUrl() = + with(GmavenHelper(groupId.get(), artifactId.get())) { + getPomFileForVersion(getLatestReleasedVersion()) } - return pomMap - } - fun diffWithPomFileUrl(pomUrl: String): String { - val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() - val oldPomBuilder: DocumentBuilder = factory.newDocumentBuilder() - val oldPomDoc: Document = oldPomBuilder.parse(URL(pomUrl).openStream()) - val currentPomBuilder: DocumentBuilder = factory.newDocumentBuilder() - val currentPomDoc: Document = currentPomBuilder.parse(pomFilePath.get()) - val oldPomMap = getMapFromXml(oldPomDoc.getElementsByTagName("dependency")) - val currentPomMap = getMapFromXml(currentPomDoc.getElementsByTagName("dependency")) + private fun getMapOfDependencies(doc: Element) = + doc + .findElementsByTag("dependency") + .associate { it.textByTag("artifactId") to it.textByTag("version") } + .filterKeys { it !in IGNORED_DEPENDENCIES } + .mapValues { + ModuleVersion.fromStringOrNull(it.value) + ?: throw RuntimeException("Invalid module version found for '${it.key}': ${it.value}") + } + + data class DependencyDiff( + val artifactId: String, + val oldVersion: ModuleVersion, + val currentVersion: ModuleVersion + ) - return currentPomMap - .filter { - (oldPomMap.get(it.key) != null) && (oldPomMap.get(it.key)!!.trim()) > it.value.trim() + fun diffWithPomFromURL(url: String): String { + val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + + val oldPom = documentBuilder.parse(URL(url).openStream()) + val currentPom = documentBuilder.parse(pomFile.get().asFile) + + val oldDependencies = getMapOfDependencies(oldPom.documentElement) + val currentDependencies = getMapOfDependencies(currentPom.documentElement) + + return currentDependencies + .map { DependencyDiff(it.key, oldDependencies.getOrDefault(it.key, it.value), it.value) } + .filter { it.oldVersion > it.currentVersion } + .map { + "Dependency on ${it.artifactId} has been degraded from ${it.oldVersion} to ${it.currentVersion}" } - .map { "Artifacts ${it.key} has been degraded to ${it.value} from ${oldPomMap.get(it.key)}" } .joinToString("\n") } + + companion object { + val IGNORED_DEPENDENCIES = + listOf( + "javax.inject" // javax.inject doesn't respect SemVer and doesn't update + ) + } }