Skip to content

dataconnect: improve gradle plugin errors when the version of the cli is not found #6333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading