diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectDslExtension.kt b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectDslExtension.kt index 8042da476ec..b413e024006 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectDslExtension.kt +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectDslExtension.kt @@ -89,9 +89,6 @@ abstract class DataConnectDslExtension @Inject constructor(objectFactory: Object var version: String? var file: File? var regularFile: RegularFile? - var fileSizeInBytes: Long? - var sha512DigestHex: String? - var verificationEnabled: Boolean } private class DataConnectExecutableBuilderImpl(initialValues: DataConnectExecutable?) : @@ -121,29 +118,16 @@ abstract class DataConnectDslExtension @Inject constructor(objectFactory: Object _regularFile = value } - override var fileSizeInBytes: Long? = null - override var sha512DigestHex: String? = null - override var verificationEnabled: Boolean = true - fun updateFrom(info: DataConnectExecutable.File) { file = info.file - updateFrom(info.verificationInfo) } fun updateFrom(info: DataConnectExecutable.RegularFile) { regularFile = info.file - updateFrom(info.verificationInfo) } fun updateFrom(info: DataConnectExecutable.Version) { version = info.version - updateFrom(info.verificationInfo) - } - - fun updateFrom(info: DataConnectExecutable.VerificationInfo?) { - verificationEnabled = info !== null - fileSizeInBytes = info?.fileSizeInBytes - sha512DigestHex = info?.sha512DigestHex } init { @@ -159,9 +143,6 @@ abstract class DataConnectDslExtension @Inject constructor(objectFactory: Object val version = version val file = file val regularFile = regularFile - val fileSizeInBytes = fileSizeInBytes - val sha512DigestHex = sha512DigestHex - val verificationEnabled = verificationEnabled if (version === null && file === null && regularFile === null) { return null @@ -195,41 +176,12 @@ abstract class DataConnectDslExtension @Inject constructor(objectFactory: Object ) } - val verificationInfo: DataConnectExecutable.VerificationInfo? = - if (!verificationEnabled) { - null - } else if (fileSizeInBytes === null && sha512DigestHex === null) { - if (version !== null) { - DataConnectExecutable.VerificationInfo.forVersion(version) - } else { - throw DataConnectGradleException( - "8s9venv4ch", - "Both 'fileSizeInBytes' and 'sha512DigestHex' were null" + - " but _both_ must be set when verificationEnabled==true" + - " and file!=null or regularFile!=null" + - " (file=$file regularFile=$regularFile)" - ) - } - } else if (fileSizeInBytes === null || sha512DigestHex === null) { - throw DataConnectGradleException( - "gjzykv9pqq", - "Both 'fileSizeInBytes' and 'sha512DigestHex' have to be set or both unset" + - " when verificationEnabled==true, but one of them was set and the other was not" + - " (fileSizeInBytes=$fileSizeInBytes, sha512DigestHex=$sha512DigestHex)" - ) - } else { - DataConnectExecutable.VerificationInfo( - fileSizeInBytes = fileSizeInBytes, - sha512DigestHex = sha512DigestHex, - ) - } - return if (version !== null) { - DataConnectExecutable.Version(version = version, verificationInfo = verificationInfo) + DataConnectExecutable.Version(version = version) } else if (file !== null) { - DataConnectExecutable.File(file = file, verificationInfo = verificationInfo) + DataConnectExecutable.File(file = file) } else if (regularFile !== null) { - DataConnectExecutable.RegularFile(file = regularFile, verificationInfo = verificationInfo) + DataConnectExecutable.RegularFile(file = regularFile) } else { throw DataConnectGradleException( "yg49q5nzxt", diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutable.kt b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutable.kt index 57363320b31..7ec40e794c0 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutable.kt +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutable.kt @@ -16,65 +16,27 @@ package com.google.firebase.dataconnect.gradle.plugin import java.io.InputStream -import java.io.Serializable import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream -// The following command was used to generate the `serialVersionUID` constants for each class. -// serialver -classpath \ -// plugin/build/classes/kotlin/main:$(find $HOME/.gradle/wrapper/dists -name -// gradle-core-api-8.5.jar -printf '%p:') \ -// com.google.firebase.dataconnect.gradle.plugin.DataConnectExecutableInput\${VerificationInfo,File,RegularFile,Version} - sealed interface DataConnectExecutable { - data class VerificationInfo(val fileSizeInBytes: Long, val sha512DigestHex: String) : - Serializable { - - companion object { - fun forVersion(version: String): VerificationInfo { - val versions = VersionsJson.load().versions - val versionInfo = - versions[version] - ?: throw DataConnectGradleException( - "3svd27ch8y", - "File size and SHA512 digest is not known for version: $version" - ) - return VerificationInfo(versionInfo.size, versionInfo.sha512DigestHex) - } - } - } + data class File(val file: java.io.File) : DataConnectExecutable - data class File(val file: java.io.File, val verificationInfo: VerificationInfo?) : - DataConnectExecutable + data class RegularFile(val file: org.gradle.api.file.RegularFile) : DataConnectExecutable - data class RegularFile( - val file: org.gradle.api.file.RegularFile, - val verificationInfo: VerificationInfo? - ) : DataConnectExecutable - - data class Version(val version: String, val verificationInfo: VerificationInfo?) : - DataConnectExecutable { + data class Version(val version: String) : DataConnectExecutable { companion object { - - private val defaultVersion: String - get() = VersionsJson.load().default - - fun forVersionWithDefaultVerificationInfo(version: String): Version { - val verificationInfo = DataConnectExecutable.VerificationInfo.forVersion(version) - return Version(version, verificationInfo) - } - - fun forDefaultVersionWithDefaultVerificationInfo(): Version = - forVersionWithDefaultVerificationInfo(defaultVersion) + val default: Version + get() = Version(VersionsJson.load().default) } } @OptIn(ExperimentalSerializationApi::class) object VersionsJson { - private const val RESOURCE_PATH = + const val RESOURCE_PATH = "com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json" fun load(): Root = openFile().use { Json.decodeFromStream(it) } diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableDownloadTask.kt b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableDownloadTask.kt index 95249d9d645..c0268edbb39 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableDownloadTask.kt +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableDownloadTask.kt @@ -15,7 +15,6 @@ */ package com.google.firebase.dataconnect.gradle.plugin -import com.google.firebase.dataconnect.gradle.plugin.DataConnectExecutable.VerificationInfo import java.io.File import java.net.HttpURLConnection import java.net.URL @@ -24,6 +23,8 @@ import java.util.regex.Pattern import kotlin.time.Duration.Companion.seconds import kotlin.time.DurationUnit import kotlin.time.toDuration +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -41,8 +42,6 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() { @get:Input @get:Optional abstract val version: Property - @get:Input @get:Optional abstract val verificationInfo: Property - @get:Internal abstract val buildDirectory: DirectoryProperty @get:OutputFile abstract val outputFile: RegularFileProperty @@ -51,13 +50,11 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() { fun run() { val inputFile: File? = inputFile.orNull?.asFile val version: String? = version.orNull - val verificationInfo: VerificationInfo? = verificationInfo.orNull val buildDirectory: File = buildDirectory.get().asFile val outputFile: File = outputFile.get().asFile logger.info("inputFile: {}", inputFile) logger.info("version: {}", version) - logger.info("verificationInfo: {}", verificationInfo) logger.info("buildDirectory: {}", buildDirectory) logger.info("outputFile: {}", outputFile) @@ -75,6 +72,7 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() { runWithFile(inputFile = inputFile, outputFile = outputFile) } else if (version !== null) { runWithVersion(version = version, outputFile = outputFile) + verifyOutputFile(outputFile, version) } else { throw DataConnectGradleException( "chc94cq7vx", @@ -82,30 +80,68 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() { " but exactly _one_ of them is required to be specified" ) } - - if (verificationInfo !== null) { - verifyOutputFile(outputFile, verificationInfo) - } } - private fun verifyOutputFile(outputFile: File, verificationInfo: VerificationInfo) { + private fun verifyOutputFile(outputFile: File, version: String) { logger.info("Verifying file size and SHA512 digest of file: {}", outputFile) val fileInfo = FileInfo.forFile(outputFile) - if (fileInfo.sizeInBytes != verificationInfo.fileSizeInBytes) { - throw DataConnectGradleException( - "zjdpbsjv42", - "File $outputFile has an unexpected size (in bytes): actual=" + + + val verificationInfoJsonString = + jsonPrettyPrint.encodeToString( + DataConnectExecutable.VersionsJson.VerificationInfo( + size = fileInfo.sizeInBytes, + sha512DigestHex = fileInfo.sha512DigestHex, + ) + ) + + val verificationInfoByVersion = DataConnectExecutable.VersionsJson.load().versions + val verificationInfo = verificationInfoByVersion[version] + if (verificationInfo === null) { + val message = + "verification information for ${outputFile.absolutePath}" + + " (version $version) is not known; known versions are: " + + verificationInfoByVersion.keys.sorted().joinToString(", ") + logger.error("ERROR: $message") + logger.error( + "To update ${DataConnectExecutable.VersionsJson.RESOURCE_PATH} with" + + " information about this version, add this JSON blob: $verificationInfoJsonString" + ) + throw DataConnectGradleException("ym8assbfgw", message) + } + + val verificationErrors = mutableListOf() + if (fileInfo.sizeInBytes != verificationInfo.size) { + logger.error( + "ERROR: File ${outputFile.absolutePath} has an unexpected size (in bytes): actual is " + fileInfo.sizeInBytes.toStringWithThousandsSeparator() + - " expected=" + - verificationInfo.fileSizeInBytes.toStringWithThousandsSeparator() + " but expected " + + verificationInfo.size.toStringWithThousandsSeparator() ) - } else if (fileInfo.sha512DigestHex != verificationInfo.sha512DigestHex) { - throw DataConnectGradleException( - "3yyma4dqga", - "File $outputFile has an unexpected SHA512 digest:" + - " actual=${fileInfo.sha512DigestHex} expected=${verificationInfo.sha512DigestHex}" + verificationErrors.add("file size mismatch") + } + if (fileInfo.sha512DigestHex != verificationInfo.sha512DigestHex) { + logger.error( + "ERROR: File ${outputFile.absolutePath} has an unexpected SHA512 digest:" + + " actual is ${fileInfo.sha512DigestHex}" + + " but expected ${verificationInfo.sha512DigestHex}" ) + verificationErrors.add("SHA512 digest mismatch") + } + + if (verificationErrors.isEmpty()) { + logger.info("Verifying file size and SHA512 digest succeeded") + return } + + logger.error( + "To update ${DataConnectExecutable.VersionsJson.RESOURCE_PATH} with" + + " information about this version, add this JSON blob: $verificationInfoJsonString" + ) + + throw DataConnectGradleException( + "x9dfwhjr9c", + "Verification of ${outputFile.absolutePath} failed: ${verificationErrors.joinToString(", ")}" + ) } data class FileInfo(val sizeInBytes: Long, val sha512DigestHex: String) { @@ -199,4 +235,8 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() { } } } + + private companion object { + val jsonPrettyPrint = Json { prettyPrint = true } + } } diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectGradlePlugin.kt b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectGradlePlugin.kt index 02a10b10032..e53eb432322 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectGradlePlugin.kt +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectGradlePlugin.kt @@ -110,17 +110,6 @@ abstract class DataConnectGradlePlugin : Plugin { } ) ) - verificationInfo.set( - dataConnectExecutable.map( - TransformerInterop { - when (it) { - is DataConnectExecutable.File -> it.verificationInfo - is DataConnectExecutable.RegularFile -> it.verificationInfo - is DataConnectExecutable.Version -> it.verificationInfo - } - } - ) - ) outputFile.set( dataConnectExecutable.map { when (it) { diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectLocalSettings.kt b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectLocalSettings.kt index cfa265bd409..2d452104457 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectLocalSettings.kt +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectLocalSettings.kt @@ -29,9 +29,9 @@ class DataConnectLocalSettings(project: Project) { ) { settingName, settingValue, project -> if (settingName == KEY_DATA_CONNECT_EXECUTABLE_FILE) { val regularFile = project.layout.projectDirectory.file(settingValue) - DataConnectExecutable.RegularFile(regularFile, verificationInfo = null) + DataConnectExecutable.RegularFile(regularFile) } else if (settingName == KEY_DATA_CONNECT_EXECUTABLE_VERSION) { - DataConnectExecutable.Version.forVersionWithDefaultVerificationInfo(settingValue) + DataConnectExecutable.Version(settingValue) } else { throw IllegalStateException( "fileValue==null && versionValue==null (error code rbhmsd524t)" diff --git a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectProviders.kt b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectProviders.kt index 698f5b28ae2..5ece4c1413e 100644 --- a/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectProviders.kt +++ b/firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectProviders.kt @@ -35,11 +35,11 @@ class DataConnectProviders( val fileValueFromGradleProperty: Provider = project.providers.gradleProperty(fileGradlePropertyName).map { val regularFile = project.layout.projectDirectory.file(it) - DataConnectExecutable.RegularFile(regularFile, verificationInfo = null) + DataConnectExecutable.RegularFile(regularFile) } val versionValueFromGradleProperty: Provider = project.providers.gradleProperty(versionGradlePropertyName).map { - DataConnectExecutable.Version.forVersionWithDefaultVerificationInfo(it) + DataConnectExecutable.Version(it) } val valueFromVariant: Provider = variantExtension.dataConnectExecutable val valueFromProject: Provider = @@ -50,7 +50,7 @@ class DataConnectProviders( .orElse(versionValueFromGradleProperty) .orElse(valueFromVariant) .orElse(valueFromProject) - .orElse(DataConnectExecutable.Version.forDefaultVersionWithDefaultVerificationInfo()) + .orElse(DataConnectExecutable.Version.default) } val postgresConnectionUrl: Provider = run {