Skip to content
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
@@ -0,0 +1,44 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.dataconnect.gradle.plugin

import java.util.Locale

enum class CpuArchitecture {
AMD64,
ARM64;

companion object {
fun current(): CpuArchitecture? {
val arch = System.getProperty("os.arch")
return if (arch === null) null else forName(arch)
}

fun forName(arch: String): CpuArchitecture? =
forNameWithLowercaseArch(arch.lowercase(Locale.ROOT))

// This logic was adapted from
// https://github.com/gradle/gradle/blob/4457734e73fc567a43ccf96185341432b636bc47/platforms/core-runtime/base-services/src/main/java/org/gradle/internal/os/OperatingSystem.java#L357-L369
private fun forNameWithLowercaseArch(arch: String): CpuArchitecture? =
when (arch) {
"x86_64",
"amd64" -> CpuArchitecture.AMD64
"aarch64" -> CpuArchitecture.ARM64
else -> null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ object DataConnectExecutableVersionsRegistry {
data class VersionInfo(
@Serializable(with = LooseVersionSerializer::class) val version: Version,
@Serializable(with = OperatingSystemSerializer::class) val os: OperatingSystem,
@Serializable(with = CpuArchitectureSerializer::class) val arch: CpuArchitecture? = null,
val size: Long,
val sha512DigestHex: String,
)
Expand All @@ -95,11 +96,39 @@ object DataConnectExecutableVersionsRegistry {
encoder.encodeString(value.serializedValue)
}

private object CpuArchitectureSerializer : KSerializer<CpuArchitecture> {
override val descriptor =
PrimitiveSerialDescriptor(
"com.google.firebase.dataconnect.gradle.plugin.CpuArchitecture",
PrimitiveKind.STRING,
)

override fun deserialize(decoder: Decoder): CpuArchitecture =
decoder.decodeString().let { serializedValue ->
CpuArchitecture.entries.singleOrNull { it.serializedValue == serializedValue }
?: throw DataConnectGradleException(
"yxnvjm2nxe",
"Unknown CPU architecture: $serializedValue " +
"(must be one of ${CpuArchitecture.entries.joinToString { it.serializedValue }})"
)
}

override fun serialize(encoder: Encoder, value: CpuArchitecture) =
encoder.encodeString(value.serializedValue)
}

val OperatingSystem.serializedValue: String
get() =
when (this) {
OperatingSystem.Windows -> "windows"
OperatingSystem.MacOS -> "macos"
OperatingSystem.Linux -> "linux"
}

val CpuArchitecture.serializedValue: String
get() =
when (this) {
CpuArchitecture.AMD64 -> "amd64"
CpuArchitecture.ARM64 -> "arm64"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ abstract class UpdateDataConnectExecutableVersionsTask : DefaultTask() {
private data class CloudStorageVersionInfo(
val version: Version,
val operatingSystem: OperatingSystem,
val cpuArchitecture: CpuArchitecture?,
val blob: Blob,
)

Expand Down Expand Up @@ -171,7 +172,7 @@ abstract class UpdateDataConnectExecutableVersionsTask : DefaultTask() {
return null
}

val versionString = match.groups[2]?.value
val versionString = match.groups["version"]?.value
val version = versionString?.toVersionOrNull(strict = false)
if (version === null) {
logger.info(
Expand Down Expand Up @@ -205,7 +206,7 @@ abstract class UpdateDataConnectExecutableVersionsTask : DefaultTask() {
}

val operatingSystem =
when (val operatingSystemString = match.groups[1]?.value) {
when (val operatingSystemString = match.groups["os"]?.value) {
"linux" -> OperatingSystem.Linux
"macos" -> OperatingSystem.MacOS
"windows" -> OperatingSystem.Windows
Expand All @@ -221,15 +222,32 @@ abstract class UpdateDataConnectExecutableVersionsTask : DefaultTask() {
}
}

return CloudStorageVersionInfo(version, operatingSystem, blob = this)
val cpuArchitecture =
when (val cpuArchitectureString = match.groups["arch"]?.value) {
null -> null
"amd64" -> CpuArchitecture.AMD64
"arm64" -> CpuArchitecture.ARM64
else -> {
logger.info(
"WARNING: Ignoring Data Connect executable file: {} " +
"(unknown CPU architecture name: {} (in match for regex {}))",
name,
cpuArchitectureString,
fileNameRegex
)
return null
}
}

return CloudStorageVersionInfo(version, operatingSystem, cpuArchitecture, blob = this)
}

private fun CloudStorageVersionInfo.toRegistryVersionInfo(workDirectory: File): VersionInfo {
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)

logger.lifecycle(
"Downloading version {} ({} bytes, created {})",
"$version-${operatingSystem.serializedValue}",
toLogString(),
blob.size.toStringWithThousandsSeparator(),
dateFormatter.format(blob.createTimeOffsetDateTime.atZoneSameInstant(ZoneId.systemDefault()))
)
Expand All @@ -244,31 +262,58 @@ abstract class UpdateDataConnectExecutableVersionsTask : DefaultTask() {
"never happen; if it _does_ happen it _could_ indicate a compromised " +
"downloaded binary [y5967yd2cf]"
}
return VersionInfo(version, operatingSystem, fileInfo.sizeInBytes, fileInfo.sha512DigestHex)
return VersionInfo(
version,
operatingSystem,
cpuArchitecture,
fileInfo.sizeInBytes,
fileInfo.sha512DigestHex
)
}

private companion object {

val versionInfoComparator =
compareBy<VersionInfo> { it.version }.thenByDescending { it.os.serializedValue }
compareBy<VersionInfo> { it.version }
.thenByDescending { it.os.serializedValue }
.thenBy { it.arch?.serializedValue }

val cloudStorageVersionInfoComparator =
compareBy<CloudStorageVersionInfo> { it.version }
.thenByDescending { it.operatingSystem.serializedValue }

@JvmName("toLogStringCloudStorageVersionInfo")
fun Iterable<CloudStorageVersionInfo>.toLogString(): String = joinToString {
"${it.version}-${it.operatingSystem.serializedValue}"
}

@JvmName("toLogStringVersionInfo")
fun Iterable<VersionInfo>.toLogString(): String = joinToString {
"${it.version}-${it.os.serializedValue}"
.thenBy { it.cpuArchitecture?.serializedValue }

@JvmName("iterableOfCloudStorageVersionInfoToLogString")
fun Iterable<CloudStorageVersionInfo>.toLogString(): String = joinToString { it.toLogString() }

@JvmName("cloudStorageVersionInfoToLogString")
fun CloudStorageVersionInfo.toLogString(): String =
createLogStringFromVersionOsArch(version, operatingSystem, cpuArchitecture)

@JvmName("iterableOfVersionInfoToLogString")
fun Iterable<VersionInfo>.toLogString(): String = joinToString { it.toLogString() }

@JvmName("versionInfoToLogString")
fun VersionInfo.toLogString(): String = createLogStringFromVersionOsArch(version, os, arch)

fun createLogStringFromVersionOsArch(
version: Version,
operatingSystem: OperatingSystem,
cpuArchitecture: CpuArchitecture?
): String = buildString {
append(version)
append('-')
append(operatingSystem.serializedValue)
if (cpuArchitecture !== null) {
append('-')
append(cpuArchitecture.serializedValue)
}
}

val invalidVersions = setOf("1.15.0".toVersion())
val minVersion = "1.3.4".toVersion()
val fileNameRegex = ".*dataconnect-emulator-([^-]+)-v(.*)".toRegex()
val fileNameRegex =
".*dataconnect-emulator-(?<os>[^-]+)(-(?<arch>[^-]+))?-v(?<version>.*)".toRegex()

/**
* Creates and returns a new list that contains all elements of the receiving [Iterable] that
Expand All @@ -278,7 +323,9 @@ abstract class UpdateDataConnectExecutableVersionsTask : DefaultTask() {
registry: DataConnectExecutableVersionsRegistry.Root
): List<CloudStorageVersionInfo> = filterNot { cloudStorageVersion ->
registry.versions.any {
it.version == cloudStorageVersion.version && it.os == cloudStorageVersion.operatingSystem
it.version == cloudStorageVersion.version &&
it.os == cloudStorageVersion.operatingSystem &&
it.arch == cloudStorageVersion.cpuArchitecture
}
}

Expand Down