Skip to content

Commit

Permalink
dataconnect: improve gradle plugin errors when the version of the cli…
Browse files Browse the repository at this point in the history
… is not found (#6333)
  • Loading branch information
dconeybe authored Oct 1, 2024
1 parent 834f27f commit bb5a4d4
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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?) :
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Root>(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -41,8 +42,6 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {

@get:Input @get:Optional abstract val version: Property<String>

@get:Input @get:Optional abstract val verificationInfo: Property<VerificationInfo>

@get:Internal abstract val buildDirectory: DirectoryProperty

@get:OutputFile abstract val outputFile: RegularFileProperty
Expand All @@ -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)

Expand All @@ -75,37 +72,76 @@ 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",
"Neither 'inputFile' nor 'version' were specified," +
" 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<String>()
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) {
Expand Down Expand Up @@ -199,4 +235,8 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {
}
}
}

private companion object {
val jsonPrettyPrint = Json { prettyPrint = true }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,6 @@ abstract class DataConnectGradlePlugin : Plugin<Project> {
}
)
)
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ class DataConnectProviders(
val fileValueFromGradleProperty: Provider<DataConnectExecutable> =
project.providers.gradleProperty(fileGradlePropertyName).map {
val regularFile = project.layout.projectDirectory.file(it)
DataConnectExecutable.RegularFile(regularFile, verificationInfo = null)
DataConnectExecutable.RegularFile(regularFile)
}
val versionValueFromGradleProperty: Provider<DataConnectExecutable> =
project.providers.gradleProperty(versionGradlePropertyName).map {
DataConnectExecutable.Version.forVersionWithDefaultVerificationInfo(it)
DataConnectExecutable.Version(it)
}
val valueFromVariant: Provider<DataConnectExecutable> = variantExtension.dataConnectExecutable
val valueFromProject: Provider<DataConnectExecutable> =
Expand All @@ -50,7 +50,7 @@ class DataConnectProviders(
.orElse(versionValueFromGradleProperty)
.orElse(valueFromVariant)
.orElse(valueFromProject)
.orElse(DataConnectExecutable.Version.forDefaultVersionWithDefaultVerificationInfo())
.orElse(DataConnectExecutable.Version.default)
}

val postgresConnectionUrl: Provider<String> = run {
Expand Down

0 comments on commit bb5a4d4

Please sign in to comment.