diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 8a635beff6..55dd574bcf 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -18,9 +18,6 @@ jobs: uses: gradle/gradle-build-action@v2 - name: Check Snippets run: python scripts/checksnippets.py - # TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed - - name: Remove Firebase Data Connect from CI - run: python scripts/ci_remove_fdc.py - name: Copy mock google_services.json run: ./copy_mock_google_services_json.sh - name: Build with Gradle (Pull Request) diff --git a/dataconnect/.editorconfig b/dataconnect/.editorconfig new file mode 100644 index 0000000000..a70ea32ef2 --- /dev/null +++ b/dataconnect/.editorconfig @@ -0,0 +1,5 @@ +[buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/providers/providers.kt] +ktlint_standard_filename = disabled + +[buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/util.kt] +ktlint_standard_filename = disabled diff --git a/dataconnect/.gitignore b/dataconnect/.gitignore index 9da855c735..ce97a25464 100644 --- a/dataconnect/.gitignore +++ b/dataconnect/.gitignore @@ -17,3 +17,4 @@ local.properties .firebaserc .firebase/ *.log +.kotlin diff --git a/dataconnect/README.md b/dataconnect/README.md index 8366d2dfac..0c6a3918f4 100644 --- a/dataconnect/README.md +++ b/dataconnect/README.md @@ -71,3 +71,20 @@ open `quickstart-android/dataconnect/movie-connector/queries.gql` and run the `L ### 5. Running the app Press the Run button in Android Studio to run the sample app on your device. + +### 6. (Optional) Re-generating the source code for Data Connect connectors + +This project defines a custom Gradle plugin that looks after generating the Kotlin code for the +queries and mutations defined in the GraphQL files. The code will be generated on-demand when +needed by a build. To generate the code manually, run: + +``` +../gradlew generateDebugDataConnectSources +``` + +If you are making frequent changes to the GraphQL files, it may be convenient to run the code +generation upon _every_ change, without waiting for a build. To do that, run: + +``` +../gradlew generateDebugDataConnectSources --continuous +``` diff --git a/dataconnect/app/build.gradle.kts b/dataconnect/app/build.gradle.kts index 2abc8043fc..bd6f284f20 100644 --- a/dataconnect/app/build.gradle.kts +++ b/dataconnect/app/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.kotlin.serialization) alias(libs.plugins.google.services) alias(libs.plugins.compose.compiler) + id("com.google.firebase.example.dataconnect.gradle") } android { @@ -50,9 +51,6 @@ android { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } - sourceSets.getByName("main") { - java.srcDirs("build/generated/sources") - } } dependencies { @@ -83,3 +81,21 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } + +// This "dataconnect" section is a Gradle "extension" that is added by the +// "com.google.firebase.example.dataconnect.gradle" Gradle plugin. It must be +// present to specify configuration details for the Data Connect Gradle plugin. +dataconnect { + // The version of Node.js (https://nodejs.org) to use to install and run the + // Firebase CLI. This version of Node.js will be downloaded and extracted + // for exclusive use of the Data Connect Gradle plugin. + nodeJsVersion = "20.18.1" + + // The version of the Firebase CLI (https://www.npmjs.com/package/firebase-tools) + // to use to perform the Data Connect Kotlin code generation. + firebaseCliVersion = "13.25.0" + + // The directory that contains dataconnect.yaml that specifies the Data + // Connect schema and connectors whose code to generate. + dataConnectConfigDir = file("../dataconnect") +} diff --git a/dataconnect/buildSrc/build.gradle.kts b/dataconnect/buildSrc/build.gradle.kts new file mode 100644 index 0000000000..102422d923 --- /dev/null +++ b/dataconnect/buildSrc/build.gradle.kts @@ -0,0 +1,56 @@ +import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode + +/* + * 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. + */ + +plugins { + // See https://docs.gradle.org/current/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin + `kotlin-dsl` + kotlin("plugin.serialization") version embeddedKotlinVersion +} + +java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } } + +kotlin { + explicitApi = ExplicitApiMode.Strict +} + +dependencies { + implementation(libs.android.gradlePlugin.api) + implementation(libs.snakeyaml) + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + + val ktorVersion = "2.3.13" + implementation("io.ktor:ktor-client-core:$ktorVersion") + implementation("io.ktor:ktor-client-cio:$ktorVersion") + implementation("io.ktor:ktor-client-logging:$ktorVersion") + + implementation("org.pgpainless:pgpainless-sop:1.7.2") + implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") + implementation("org.apache.commons:commons-compress:1.27.1") + + // Needed for org.apache.commons.compress.compressors.xz.XZCompressorInputStream + runtimeOnly("org.tukaani:xz:1.10") +} + +gradlePlugin { + plugins { + create("dataconnect") { + id = "com.google.firebase.example.dataconnect.gradle" + implementationClass = "com.google.firebase.example.dataconnect.gradle.DataConnectGradlePlugin" + } + } +} diff --git a/dataconnect/buildSrc/settings.gradle.kts b/dataconnect/buildSrc/settings.gradle.kts new file mode 100644 index 0000000000..89a5d455e0 --- /dev/null +++ b/dataconnect/buildSrc/settings.gradle.kts @@ -0,0 +1,15 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} + +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } + versionCatalogs { create("libs") { from(files("../../gradle/libs.versions.toml")) } } +} diff --git a/dataconnect/buildSrc/src/main/java/com/google/firebase/example/dataconnect/gradle/TransformerInterop.java b/dataconnect/buildSrc/src/main/java/com/google/firebase/example/dataconnect/gradle/TransformerInterop.java new file mode 100644 index 0000000000..d4fb88ee1a --- /dev/null +++ b/dataconnect/buildSrc/src/main/java/com/google/firebase/example/dataconnect/gradle/TransformerInterop.java @@ -0,0 +1,37 @@ +/* + * 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.example.dataconnect.gradle; + +import org.gradle.api.Transformer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +// TODO: Remove this interface and use Transformer directly once the Kotlin +// version is upgraded to a later version that doesn't require it, such as +// 1.9.25. At the time of writing, the Kotlin version in use is 1.9.22. +// +// Using this interface works around the following Kotlin compiler error: +// +// > Task :plugin:compileKotlin FAILED +// e: DataConnectGradlePlugin.kt:93:15 Type mismatch: inferred type is RegularFile? but TypeVariable(S) was expected +// e: DataConnectGradlePlugin.kt:102:15 Type mismatch: inferred type is String? but TypeVariable(S) was expected +// e: DataConnectGradlePlugin.kt:111:15 Type mismatch: inferred type is DataConnectExecutable.VerificationInfo? but TypeVariable(S) was expected +public interface TransformerInterop extends Transformer { + + @Override + @Nullable OUT transform(@NotNull IN in); + +} \ No newline at end of file diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt new file mode 100644 index 0000000000..961629fa4c --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectExtension.kt @@ -0,0 +1,46 @@ +/* + * 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.example.dataconnect.gradle + +import java.io.File + +public interface DataConnectExtension { + + /** + * The version of Node.js (https://nodejs.org) to use to install and run the + * Firebase CLI. This version of Node.js will be downloaded and extracted + * for exclusive use of the Data Connect Gradle plugin. + */ + public var nodeJsVersion: String? + + /** + * The version of the Firebase CLI (https://www.npmjs.com/package/firebase-tools) + * to use to perform the Data Connect Kotlin code generation. + */ + public var firebaseCliVersion: String? + + /** + * The directory that contains dataconnect.yaml that specifies the Data + * Connect schema and connectors whose code to generate. If the file is a + * relative directory (as opposed to an absolute directory) then it will be + * calculated relative to the evaluating project's directory. + * + * If this value is null then no Data Connect code generation will occur + * as part of the build. + */ + public var dataConnectConfigDir: File? +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradleException.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradleException.kt new file mode 100644 index 0000000000..badf9c3760 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradleException.kt @@ -0,0 +1,21 @@ +/* + * 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.example.dataconnect.gradle + +import org.gradle.api.GradleException + +public class DataConnectGradleException(message: String, cause: Throwable? = null) : GradleException(message, cause) diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt new file mode 100644 index 0000000000..548a1acd70 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectGradlePlugin.kt @@ -0,0 +1,66 @@ +/* + * 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.example.dataconnect.gradle + +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.google.firebase.example.dataconnect.gradle.tasks.DownloadNodeJsBinaryDistributionArchiveTask +import com.google.firebase.example.dataconnect.gradle.tasks.ExtractArchiveTask +import com.google.firebase.example.dataconnect.gradle.tasks.GenerateDataConnectSourcesTask +import com.google.firebase.example.dataconnect.gradle.tasks.SetupFirebaseToolsTask +import java.util.Locale +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.register + +@Suppress("unused") +public abstract class DataConnectGradlePlugin : Plugin { + + override fun apply(project: Project) { + val downloadNodeJsArchiveTask = project.tasks.register( + "dataConnectDownloadNodeJs" + ) + val extractNodeJsArchiveTask = project.tasks.register("dataConnectExtractNodeJs") + val setupFirebaseToolsTask = project.tasks.register("dataConnectSetupFirebaseTools") + + val dataConnectExtension = project.extensions.create("dataconnect", DataConnectExtension::class.java) + val configurer = DataConnectTasksConfigurer( + dataConnectExtension = dataConnectExtension, + downloadNodeJsArchiveTask = downloadNodeJsArchiveTask, + extractNodeJsArchiveTask = extractNodeJsArchiveTask, + setupFirebaseToolsTask = setupFirebaseToolsTask, + buildDirectory = project.layout.buildDirectory.dir("dataConnect"), + projectDirectory = project.layout.projectDirectory, + providerFactory = project.providers + ) + + configurer.invoke() + + val androidComponents = project.extensions.getByType() + androidComponents.onVariants { variant -> + val variantNameTitleCase = variant.name.replaceFirstChar { it.titlecase(Locale.US) } + val generateCodeTaskName = "dataConnectGenerate${variantNameTitleCase}Sources" + val generateCodeTask = project.tasks.register(generateCodeTaskName) + configurer.configureGenerateDataConnectSourcesTask(generateCodeTask, variantNameTitleCase) + + variant.sources.java!!.addGeneratedSourceDirectory( + generateCodeTask, + GenerateDataConnectSourcesTask::outputDirectory + ) + } + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectTasksConfigurer.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectTasksConfigurer.kt new file mode 100644 index 0000000000..23c22b5c6a --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/DataConnectTasksConfigurer.kt @@ -0,0 +1,145 @@ +package com.google.firebase.example.dataconnect.gradle + +import com.google.firebase.example.dataconnect.gradle.tasks.DownloadNodeJsBinaryDistributionArchiveTask +import com.google.firebase.example.dataconnect.gradle.tasks.ExtractArchiveTask +import com.google.firebase.example.dataconnect.gradle.tasks.GenerateDataConnectSourcesTask +import com.google.firebase.example.dataconnect.gradle.tasks.SetupFirebaseToolsTask +import com.google.firebase.example.dataconnect.gradle.util.NodeJsPaths +import com.google.firebase.example.dataconnect.gradle.util.OperatingSystem +import com.google.firebase.example.dataconnect.gradle.util.nodeJsPaths +import com.google.firebase.example.dataconnect.gradle.util.operatingSystem +import org.gradle.api.GradleException +import org.gradle.api.file.Directory +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.TaskProvider + +internal class DataConnectTasksConfigurer( + dataConnectExtension: DataConnectExtension, + private val downloadNodeJsArchiveTask: TaskProvider, + private val extractNodeJsArchiveTask: TaskProvider, + private val setupFirebaseToolsTask: TaskProvider, + private val buildDirectory: Provider, + projectDirectory: Directory, + providerFactory: ProviderFactory +) : () -> Unit { + + override fun invoke() { + configureDownloadNodeJsArchiveTask() + configureExtractNodeJsArchiveTask() + configureSetupFirebaseToolsTask() + } + + private val nodeJsVersion: Provider = + providerFactory.provider { + dataConnectExtension.nodeJsVersion + ?: throw GradleException( + "dataconnect.nodeJsVersion must be set in " + + "build.gradle or build.gradle.kts to " + + "specify the version of Node.js (https://nodejs.org) " + + "to install (e.g. \"20.9.0\") (error code 3acj27az2c)" + ) + } + + private val operatingSystem: Provider = providerFactory.operatingSystem() + + private val nodeJsPaths: Provider = + providerFactory.nodeJsPaths(nodeJsVersion, operatingSystem) + + private val firebaseCliVersion: Provider = providerFactory.provider { + dataConnectExtension.firebaseCliVersion + ?: throw GradleException( + "dataconnect.firebaseCliVersion must be set in " + + "build.gradle or build.gradle.kts to " + + "specify the version of the Firebase CLI npm package " + + "(https://www.npmjs.com/package/firebase-tools) to use " + + "(e.g. \"13.25.0\") (error code xbmvkc3mtr)" + ) + } + + private val nodeExecutable: Provider = extractNodeJsArchiveTask.flatMap { + providerFactory.zip(it.outputDirectory, nodeJsPaths) { outputDirectory, nodeJsPaths -> + outputDirectory.file(nodeJsPaths.nodeExecutableRelativePath) + } + } + + private val npmExecutable: Provider = extractNodeJsArchiveTask.flatMap { + providerFactory.zip(it.outputDirectory, nodeJsPaths) { outputDirectory, nodeJsPaths -> + outputDirectory.file(nodeJsPaths.npmExecutableRelativePath) + } + } + + private val dataConnectConfigDir: Provider = providerFactory.provider { + dataConnectExtension.dataConnectConfigDir?.let { + projectDirectory.dir(it.path) + } + } + + private fun configureDownloadNodeJsArchiveTask() = downloadNodeJsArchiveTask.configure { + group = TASK_GROUP + + archiveUrl.set(nodeJsPaths.map { it.archiveUrl }) + shasumsUrl.set(nodeJsPaths.map { it.shasumsUrl }) + + archiveFile.set( + providerFactory.zip(buildDirectory, nodeJsPaths) { buildDirectory, nodeJsPaths -> + buildDirectory.file(nodeJsPaths.archiveFileName) + } + ) + + shasumsFile.set( + providerFactory.zip(buildDirectory, nodeJsPaths) { buildDirectory, nodeJsPaths -> + buildDirectory.file(nodeJsPaths.shasumsFileName) + } + ) + } + + private fun configureExtractNodeJsArchiveTask() = extractNodeJsArchiveTask.configure { + group = TASK_GROUP + + pathPrefixComponentStripCount.set(1) + archiveFile.set(downloadNodeJsArchiveTask.flatMap { it.archiveFile }) + + outputDirectory.set( + providerFactory.zip(buildDirectory, nodeJsPaths) { buildDirectory, nodeJsPaths -> + buildDirectory.dir(nodeJsPaths.archiveBaseFileName) + } + ) + } + + private fun configureSetupFirebaseToolsTask() = setupFirebaseToolsTask.configure { + group = TASK_GROUP + + firebaseCliVersion.set(this@DataConnectTasksConfigurer.firebaseCliVersion) + outputDirectory.set(buildDirectory.map { it.dir("firebase-tools") }) + + nodeExecutable.set(this@DataConnectTasksConfigurer.nodeExecutable) + npmExecutable.set(this@DataConnectTasksConfigurer.npmExecutable) + } + + fun configureGenerateDataConnectSourcesTask( + task: TaskProvider, + variantName: String + ) = task.configure { + group = TASK_GROUP + + @Suppress("ktlint:standard:max-line-length") + setOnlyIf( + "dataconnect.dataConnectConfigDir is null; to enable the \"$name\" task, " + + "set dataconnect.dataConnectConfigDir in build.gradle or build.gradle.kts to " + + "the directory that defines the Data Connect schema and " + + "connectors whose Kotlin code to generate code. That is, the directory " + + "containing the dataconnect.yaml file. For details, see " + + "https://firebase.google.com/docs/data-connect/configuration-reference#dataconnect.yaml-configuration " + + "(e.g. file(\"../dataconnect\")) (message code a3ch245mbd)" + ) { dataConnectConfigDir.isPresent } + + dataConnectConfigDir.set(this@DataConnectTasksConfigurer.dataConnectConfigDir) + firebaseExecutable.set(setupFirebaseToolsTask.map { it.firebaseExecutable }) + nodeExecutable.set(this@DataConnectTasksConfigurer.nodeExecutable) + tweakedDataConnectConfigDir.set(buildDirectory.map { it.dir("variants/$variantName/config") }) + } +} + +private const val TASK_GROUP = "Firebase Data Connect" diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/DataConnectTaskBase.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/DataConnectTaskBase.kt new file mode 100644 index 0000000000..6d322158f1 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/DataConnectTaskBase.kt @@ -0,0 +1,117 @@ +/* + * 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.example.dataconnect.gradle.tasks + +import com.google.firebase.example.dataconnect.gradle.DataConnectGradleException +import com.google.firebase.example.dataconnect.gradle.tasks.DataConnectTaskBase.Worker +import com.google.firebase.example.dataconnect.gradle.util.DataConnectGradleLogger +import java.io.File +import java.nio.file.Files +import java.util.Date +import kotlin.reflect.full.allSupertypes +import kotlin.time.DurationUnit +import kotlin.time.toDuration +import org.gradle.api.DefaultTask +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction + +public abstract class DataConnectTaskBase(loggerIdPrefix: String) : DefaultTask() { + + @get:Internal + protected val dataConnectLogger: DataConnectGradleLogger = DataConnectGradleLogger( + loggerIdPrefix = loggerIdPrefix, + logger = logger + ) + + init { + val superTypeNames = this::class.allSupertypes.map { it.toString() }.sorted().joinToString(", ") + dataConnectLogger.info { + "Task $path created: class=${this::class.qualifiedName} (supertypes are: $superTypeNames)" + } + } + + @TaskAction + public fun run() { + dataConnectLogger.info { "Task $path starting execution at ${Date()}" } + val startTime = System.nanoTime().toDuration(DurationUnit.NANOSECONDS) + + val result = runCatching { newWorker().invoke() } + + val endTime = System.nanoTime().toDuration(DurationUnit.NANOSECONDS) + val elapsedTime = endTime - startTime + dataConnectLogger.info { + "Task $path completed execution in " + + elapsedTime.toString(DurationUnit.SECONDS, 2) + + " at ${Date()}" + } + + result.onFailure { + dataConnectLogger.warn("Task $path execution failed: $it") + throw it + } + } + + /** + * Creates and returns a new [Worker] object, which will be called by + * [run] to actually perform this task's work. + */ + protected abstract fun newWorker(): Worker + + public interface Worker : () -> Unit { + public val logger: DataConnectGradleLogger + } +} + +internal fun Worker.deleteDirectory(dir: File, fileSystemOperations: FileSystemOperations) { + logger.info { "Deleting directory: $dir" } + val result = fileSystemOperations.runCatching { delete { delete(dir) } } + + result.onFailure { + throw DataConnectGradleException( + "unable to delete directory: ${dir.absolutePath}: $it " + + "(error code 6trngh6x47)", + it + ) + } +} + +internal fun Worker.deleteFile(file: File) { + logger.info { "Deleting file: ${file.absolutePath}" } + val result = kotlin.runCatching { Files.deleteIfExists(file.toPath()) } + + result.onFailure { + throw DataConnectGradleException( + "unable to delete file: ${file.absolutePath}: $it " + + "(error code rprr987jqk)", + it + ) + } +} + +internal fun Worker.createDirectory(dir: File) { + logger.info { "Creating directory: $dir" } + + val result = runCatching { Files.createDirectories(dir.toPath()) } + result.onFailure { + throw DataConnectGradleException( + "unable to create directory: ${dir.absolutePath}: $it " + + "(error code j7x4sw7w95)", + it + ) + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/DownloadNodeJsBinaryDistributionArchiveTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/DownloadNodeJsBinaryDistributionArchiveTask.kt new file mode 100644 index 0000000000..5215c78cc2 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/DownloadNodeJsBinaryDistributionArchiveTask.kt @@ -0,0 +1,181 @@ +/* + * 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.example.dataconnect.gradle.tasks + +import com.google.firebase.example.dataconnect.gradle.DataConnectGradleException +import com.google.firebase.example.dataconnect.gradle.tasks.DownloadNodeJsBinaryDistributionArchiveTask.Worker +import com.google.firebase.example.dataconnect.gradle.util.DataConnectGradleLogger +import com.google.firebase.example.dataconnect.gradle.util.FileDownloader +import com.google.firebase.example.dataconnect.gradle.util.Sha256SignatureVerifier +import com.google.firebase.example.dataconnect.gradle.util.addCertificatesFromKeyListResource +import com.google.firebase.example.dataconnect.gradle.util.addHashesFromShasumsFile +import java.io.File +import javax.inject.Inject +import kotlinx.coroutines.runBlocking +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputFile + +@CacheableTask +public abstract class DownloadNodeJsBinaryDistributionArchiveTask : DataConnectTaskBase(LOGGER_ID_PREFIX) { + + /** + * The URL of the Node.js binary distribution archive file to download. + * + * This property is _required_; that is, it must be set by the time that + * this task is executed. + */ + @get:Input + public abstract val archiveUrl: Property + + /** + * The URL of the archive file to download. + * + * This property is _required_; that is, it must be set by the time that + * this task is executed. + */ + @get:Input + public abstract val shasumsUrl: Property + + /** + * The URL of the "SHASUMS256.txt.asc" file. + * + * This property is _required_; that is, it must be set by the time that + * this task is executed. + */ + @get:OutputFile + public abstract val archiveFile: RegularFileProperty + + /** + * The file to which to download the "SHASUMS256.txt.asc" file. + * + * This property is _required_; that is, it must be set by the time that + * this task is executed. + */ + @get:OutputFile + public abstract val shasumsFile: RegularFileProperty + + @get:Inject + internal abstract val providerFactory: ProviderFactory + + @get:Inject + internal abstract val fileSystemOperations: FileSystemOperations + + override fun newWorker(): DataConnectTaskBase.Worker { + return DownloadNodeJsBinaryDistributionArchiveTaskWorkerImpl( + archiveUrl = archiveUrl.get(), + shasumsUrl = shasumsUrl.get(), + archiveFile = archiveFile.get().asFile, + shasumsFile = shasumsFile.get().asFile, + fileSystemOperations = fileSystemOperations, + logger = dataConnectLogger + ) + } + + internal interface Worker : DataConnectTaskBase.Worker, AutoCloseable { + val archiveUrl: String + val shasumsUrl: String + val archiveFile: File + val shasumsFile: File + val fileSystemOperations: FileSystemOperations + val fileDownloader: FileDownloader + } + + private companion object { + const val LOGGER_ID_PREFIX = "dnb" + } +} + +private class DownloadNodeJsBinaryDistributionArchiveTaskWorkerImpl( + override val archiveUrl: String, + override val shasumsUrl: String, + override val archiveFile: File, + override val shasumsFile: File, + override val fileSystemOperations: FileSystemOperations, + override val logger: DataConnectGradleLogger +) : Worker { + override val fileDownloader = FileDownloader(logger) + + override fun invoke() = run() + + override fun close() { + fileDownloader.close() + } +} + +private fun Worker.run() { + logger.info { "archiveUrl: $archiveUrl" } + logger.info { "shasumsUrl: $shasumsUrl" } + logger.info { "archiveFile: ${archiveFile.absolutePath}" } + logger.info { "shasumsFile: ${shasumsFile.absolutePath}" } + + val files = listOf(archiveFile, shasumsFile).sorted() + files.forEach { deleteFile(it) } + val directories = files.map { it.absoluteFile.parentFile }.distinct().sorted() + directories.forEach { createDirectory(it) } + + runBlocking { + fileDownloader.download(archiveUrl, archiveFile, maxNumDownloadBytes = 200_000_000) + fileDownloader.download(shasumsUrl, shasumsFile, maxNumDownloadBytes = 100_000) + } + + verifyNodeJsReleaseSignature( + file = archiveFile, + shasumsFile = shasumsFile, + keyListResourcePath = "com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/keys.list", + logger + ) +} + +private fun verifyNodeJsReleaseSignature( + file: File, + shasumsFile: File, + keyListResourcePath: String, + logger: DataConnectGradleLogger +) { + val signatureVerifier = Sha256SignatureVerifier() + logger.info { + "Loading Node.js release signing certificates " + + "from resource: $keyListResourcePath" + } + val numCertificatesAdded = signatureVerifier.addCertificatesFromKeyListResource(keyListResourcePath) + logger.info { "Loaded $numCertificatesAdded certificates from $keyListResourcePath" } + + logger.info { "Loading SHA256 hashes from file: ${shasumsFile.absolutePath}" } + val fileNamesWithLoadedHash = signatureVerifier.addHashesFromShasumsFile(shasumsFile) + logger.info { + "Loaded ${fileNamesWithLoadedHash.size} hashes from ${shasumsFile.absolutePath} " + + "for file names: ${fileNamesWithLoadedHash.sorted()}" + } + + if (!fileNamesWithLoadedHash.contains(file.name)) { + throw DataConnectGradleException( + "hash for file name ${file.name} " + + "not found in ${shasumsFile.absolutePath} " + + "(error code yx3g25s926)" + ) + } + + file.inputStream().use { inputStream -> + logger.info { "Verifying SHA256 hash of file: ${file.absolutePath}" } + signatureVerifier.verifyHash(inputStream, file.name) + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/ExtractArchiveTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/ExtractArchiveTask.kt new file mode 100644 index 0000000000..d49048e10d --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/ExtractArchiveTask.kt @@ -0,0 +1,193 @@ +/* + * 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.example.dataconnect.gradle.tasks + +import com.google.firebase.example.dataconnect.gradle.tasks.ExtractArchiveTask.Worker +import com.google.firebase.example.dataconnect.gradle.util.ArchiveSetFileMetadataType +import com.google.firebase.example.dataconnect.gradle.util.ArchiveType +import com.google.firebase.example.dataconnect.gradle.util.DataConnectGradleLogger +import com.google.firebase.example.dataconnect.gradle.util.ExtractArchiveCallbacks +import com.google.firebase.example.dataconnect.gradle.util.extractArchive +import com.google.firebase.example.dataconnect.gradle.util.toFormattedString +import java.io.File +import java.util.concurrent.atomic.AtomicLong +import javax.inject.Inject +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "extracting an archive is a quick operation not worth caching") +public abstract class ExtractArchiveTask : DataConnectTaskBase(LOGGER_ID_PREFIX) { + + /** + * The archive file to extract, such as a ".zip" or ".tar.xz" file. + * + * This property is _required_, meaning that it must be set; that is, [Property.isPresent] must + * return `true`. + */ + @get:InputFile + public abstract val archiveFile: RegularFileProperty + + /** + * The number of path components to strip from the paths of files extracted from the archive. + * + * If this value is greater than zero, then the paths of the extracted files are written into + * the destination directory without that number of path prefixes stripped. For example, if this + * property's value is 2 and a file named `aaa/bbb/ccc/ddd/file.txt` was being extracted from + * the archive into directory `/home/foo` then it would be written to + * `/home/foo/ccc/ddd/file.txt` (with the 2 path prefix components `aaa/bbb/` removed) instead + * of `/home/foo/aaa/bbb/ccc/ddd/file.txt`. + * + * Setting this property to a positive value can be useful in the cases where the archive is + * known to contain exactly one top-level directory that is superfluous in the extracted + * directory structure. + * + * This property is _optional_; if this property is not set, that is, [Property.isPresent] + * returns `false`, then a value of `0` (zero) will be used. + * + * This property's value _must_ be greater than or equal to zero. + */ + @get:Input @get:Optional + public abstract val pathPrefixComponentStripCount: Property + + /** + * The directory into which to extract the archive. + * + * This property is _required_, meaning that it must be set; that is, [Property.isPresent] must + * return `true`. + * + * This directory will be deleted and re-created when this task is executed. + */ + @get:OutputDirectory + public abstract val outputDirectory: DirectoryProperty + + @get:Inject + internal abstract val fileSystemOperations: FileSystemOperations + + @get:Inject + internal abstract val providerFactory: ProviderFactory + + override fun newWorker(): DataConnectTaskBase.Worker = ExtractArchiveTaskWorkerImpl( + archiveFile = archiveFile.get().asFile, + outputDirectory = outputDirectory.get().asFile, + prefixStripCount = pathPrefixComponentStripCount.orNull ?: 0, + fileSystemOperations = fileSystemOperations, + logger = dataConnectLogger + ) + + internal interface Worker : DataConnectTaskBase.Worker { + val archiveFile: File + val outputDirectory: File + val prefixStripCount: Int + val fileSystemOperations: FileSystemOperations + } + + private companion object { + const val LOGGER_ID_PREFIX = "ear" + } +} + +private class ExtractArchiveTaskWorkerImpl( + override val archiveFile: File, + override val outputDirectory: File, + override val prefixStripCount: Int, + override val fileSystemOperations: FileSystemOperations, + override val logger: DataConnectGradleLogger +) : Worker { + override fun invoke() { + run() + } +} + +private fun Worker.run() { + logger.info { "archiveFile: ${archiveFile.absolutePath}" } + logger.info { "outputDirectory: ${outputDirectory.absolutePath}" } + logger.info { "prefixStripCount: $prefixStripCount" } + + if (prefixStripCount < 0) { + throw IllegalArgumentException( + "invalid prefixStripCount: $prefixStripCount " + + "(must be greater than or equal to zero) (error code mn8pp2b7mc)" + ) + } + + deleteDirectory(outputDirectory, fileSystemOperations) + createDirectory(outputDirectory) + + logger.info { "Extracting ${archiveFile.absolutePath} to ${outputDirectory.absolutePath}" } + val extractCallbacks = ExtractArchiveCallbacksImpl(archiveFile, logger) + archiveFile.extractArchive(outputDirectory) { + callbacks = extractCallbacks + prefixStripCount = this@run.prefixStripCount + } + + logger.info { + val fileCountStr = extractCallbacks.extractedFileCount.toFormattedString() + val symlinkCountStr = extractCallbacks.extractedSymlinkCount.toFormattedString() + val byteCountStr = extractCallbacks.extractedByteCount.toFormattedString() + "Extracted $fileCountStr files ($byteCountStr bytes) " + + "and $symlinkCountStr symlinks " + + "from ${archiveFile.absolutePath} to ${outputDirectory.absolutePath}" + } +} + +private class ExtractArchiveCallbacksImpl(private val file: File, private val logger: DataConnectGradleLogger) : + ExtractArchiveCallbacks { + + private val _extractedFileCount = AtomicLong(0) + val extractedFileCount: Long get() = _extractedFileCount.get() + + private val _extractedSymlinkCount = AtomicLong(0) + val extractedSymlinkCount: Long get() = _extractedSymlinkCount.get() + + private val _extractedByteCount = AtomicLong(0) + val extractedByteCount: Long get() = _extractedByteCount.get() + + override fun onExtractArchiveStarting(archiveType: ArchiveType) { + logger.info { "Detected archive type $archiveType for file: ${file.absolutePath}" } + } + + override fun onExtractFileStarting(srcPath: String, destFile: File) { + _extractedFileCount.incrementAndGet() + logger.debug { "Extracting $srcPath to ${destFile.absolutePath}" } + } + + override fun onExtractFileDone(srcPath: String, destFile: File, extractedByteCount: Long) { + _extractedByteCount.addAndGet(extractedByteCount) + logger.debug { + "Extracted ${extractedByteCount.toFormattedString()} bytes " + "from $srcPath to ${destFile.absolutePath}" + } + } + + override fun onExtractSymlink(linkPath: String, destFile: File) { + _extractedSymlinkCount.incrementAndGet() + logger.debug { "Creating symlink ${destFile.absolutePath} to $linkPath" } + } + + override fun onSetFileMetadataFailed(file: File, metadataType: ArchiveSetFileMetadataType, exception: Exception) { + logger.debug { + "Ignoring failure to set ${metadataType.name} " + "on extracted file: ${file.absolutePath}" + } + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/GenerateDataConnectSourcesTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/GenerateDataConnectSourcesTask.kt new file mode 100644 index 0000000000..a5bc01b04b --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/GenerateDataConnectSourcesTask.kt @@ -0,0 +1,178 @@ +/* + * 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.example.dataconnect.gradle.tasks + +import com.google.firebase.example.dataconnect.gradle.util.runCommand +import java.io.File +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.logging.Logger +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations +import org.yaml.snakeyaml.Yaml + +@CacheableTask +public abstract class GenerateDataConnectSourcesTask : DefaultTask() { + + @get:InputDirectory public abstract val dataConnectConfigDir: DirectoryProperty + + @get:InputFile public abstract val firebaseExecutable: RegularFileProperty + + @get:OutputDirectory public abstract val outputDirectory: DirectoryProperty + + @get:InputFile + public abstract val nodeExecutable: RegularFileProperty + + @get:Internal + public abstract val pathEnvironmentVariable: Property + + @get:Internal public abstract val tweakedDataConnectConfigDir: DirectoryProperty + + @get:Inject + internal abstract val fileSystemOperations: FileSystemOperations + + @get:Inject + internal abstract val execOperations: ExecOperations + + @TaskAction + public fun run() { + val dataConnectConfigDir = dataConnectConfigDir.get().asFile + val firebaseExecutable = firebaseExecutable.get().asFile + val nodeExecutable = nodeExecutable.orNull?.asFile + val outputDirectory = outputDirectory.get().asFile + val tweakedDataConnectConfigDir = tweakedDataConnectConfigDir.get().asFile + + logger.info("dataConnectConfigDir: {}", dataConnectConfigDir) + logger.info("firebaseExecutable: {}", firebaseExecutable) + logger.info("nodeExecutable: {}", nodeExecutable) + logger.info("outputDirectory: {}", outputDirectory) + logger.info("tweakedDataConnectConfigDir: {}", tweakedDataConnectConfigDir) + + fileSystemOperations.delete { + delete(outputDirectory) + delete(tweakedDataConnectConfigDir) + } + + if (!tweakedDataConnectConfigDir.mkdirs()) { + throw GradleException( + "Could not create directory: ${tweakedDataConnectConfigDir.absolutePath} " + + "(error code q6dyy7vhbc)" + ) + } + + fileSystemOperations.copy { + from(dataConnectConfigDir) + into(tweakedDataConnectConfigDir) + } + + tweakConnectorYamlFiles(tweakedDataConnectConfigDir, outputDirectory.absolutePath, logger) + + val commandLogFile = File(tweakedDataConnectConfigDir, "generate.log.txt") + execOperations.runCommand(commandLogFile, logger) { + if (nodeExecutable === null) { + commandLine("node") + } else { + val oldPath = pathEnvironmentVariable.orElse("") + val newPath = nodeExecutable.absoluteFile.parent + File.pathSeparator + oldPath + environment("PATH", newPath) + } + + commandLine(firebaseExecutable.absolutePath, "--debug", "dataconnect:sdk:generate") + // Specify a fake project because dataconnect:sdk:generate unnecessarily + // requires one. The actual value does not matter. + args("--project", "zzyzx") + workingDir(tweakedDataConnectConfigDir) + } + } +} + +private fun tweakConnectorYamlFiles(dir: File, newOutputDir: String, logger: Logger) { + logger.info("Tweaking connector.yaml files in {}", dir.absolutePath) + dir.walk().forEach { file -> + if (file.isFile && file.name == "connector.yaml") { + tweakConnectorYamlFile(file, newOutputDir, logger) + } else { + logger.debug("skipping file: {}", file.absolutePath) + } + } +} + +private fun tweakConnectorYamlFile(file: File, newOutputDir: String, logger: Logger) { + logger.info("Tweaking connector.yaml file: {}", file.absolutePath) + + fun Map<*, *>.withTweakedKotlinSdk() = + filterKeys { it == "kotlinSdk" } + .mapValues { (_, value) -> + val kotlinSdkMap = + value as? Map<*, *> + ?: throw GradleException( + "Parsing ${file.absolutePath} failed: \"kotlinSdk\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code m697s27yxn)" + ) + kotlinSdkMap.mapValues { (key, value) -> + if (key == "outputDir") { + newOutputDir + } else { + value + } + } + } + + fun Map<*, *>.withTweakedGenerateNode() = mapValues { (key, value) -> + if (key != "generate") { + value + } else { + val generateMap = + value as? Map<*, *> + ?: throw GradleException( + "Parsing ${file.absolutePath} failed: \"generate\" is " + + (if (value === null) "null" else value::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 9c2p857gq6)" + ) + generateMap.withTweakedKotlinSdk() + } + } + + val yaml = Yaml() + val rootObject = file.reader(Charsets.UTF_8).use { reader -> yaml.load(reader) } + + val rootMap = + rootObject as? Map<*, *> + ?: throw GradleException( + "Parsing ${file.absolutePath} failed: root is " + + (if (rootObject === null) "null" else rootObject::class.qualifiedName) + + ", but expected ${Map::class.qualifiedName} " + + "(error code 45dw8jx8jd)" + ) + + val newRootMap = rootMap.withTweakedGenerateNode() + + file.writer(Charsets.UTF_8).use { writer -> yaml.dump(newRootMap, writer) } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/SetupFirebaseToolsTask.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/SetupFirebaseToolsTask.kt new file mode 100644 index 0000000000..c03e83ae7c --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/tasks/SetupFirebaseToolsTask.kt @@ -0,0 +1,86 @@ +/* + * 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.example.dataconnect.gradle.tasks + +import com.google.firebase.example.dataconnect.gradle.util.runCommand +import java.io.File +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations + +@CacheableTask +public abstract class SetupFirebaseToolsTask : DefaultTask() { + + @get:Input + public abstract val firebaseCliVersion: Property + + @get:OutputDirectory + public abstract val outputDirectory: DirectoryProperty + + @get:Internal + public abstract val npmExecutable: RegularFileProperty + + @get:Internal + public abstract val nodeExecutable: RegularFileProperty + + @get:Internal + public val firebaseExecutable: RegularFile get() = outputDirectory.get().file("node_modules/.bin/firebase") + + @get:Inject + internal abstract val execOperations: ExecOperations + + private val pathEnvironmentVariable: Provider get() = project.providers.environmentVariable("PATH") + + @TaskAction + public fun run() { + val firebaseCliVersion: String = firebaseCliVersion.get() + val npmExecutable: File = npmExecutable.get().asFile + val nodeExecutable: File = nodeExecutable.get().asFile + val outputDirectory: File = outputDirectory.get().asFile + + logger.info("firebaseCliVersion: {}", firebaseCliVersion) + logger.info("npmExecutable: {}", npmExecutable.absolutePath) + logger.info("nodeExecutable: {}", nodeExecutable.absolutePath) + logger.info("outputDirectory: {}", outputDirectory.absolutePath) + + project.delete(outputDirectory) + project.mkdir(outputDirectory) + + val oldPath = pathEnvironmentVariable.getOrElse("") + val newPath = nodeExecutable.absoluteFile.parent + File.pathSeparator + oldPath + + val packageJsonFile = File(outputDirectory, "package.json") + packageJsonFile.writeText("{}", Charsets.UTF_8) + + val installLogFile = File(outputDirectory, "install.log.txt") + execOperations.runCommand(installLogFile, logger) { + environment("PATH", newPath) + commandLine(npmExecutable.absolutePath, "install", "firebase-tools@$firebaseCliVersion") + workingDir(outputDirectory) + } + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/ArchiveExtraction.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/ArchiveExtraction.kt new file mode 100644 index 0000000000..dc7a37ee14 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/ArchiveExtraction.kt @@ -0,0 +1,248 @@ +/* + * 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.example.dataconnect.gradle.util + +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.attribute.FileTime +import java.nio.file.attribute.PosixFilePermission +import org.apache.commons.compress.archivers.ArchiveEntry +import org.apache.commons.compress.archivers.ArchiveInputStream +import org.apache.commons.compress.archivers.tar.TarArchiveEntry +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream +import org.apache.commons.compress.compressors.xz.XZCompressorInputStream +import org.apache.commons.compress.compressors.xz.XZUtils + +internal fun File.extractArchive(destDir: File, configure: ExtractArchiveConfigBuilder.() -> Unit = {}) { + if (XZUtils.isCompressedFileName(name)) { + extractTarXzArchive(destDir, configure) + } else if (name.endsWith(".7z")) { + extract7zArchive(destDir, configure) + } else { + throw UnsupportedArchiveException( + "don't know how to extract $name; " + + "supported archive formats are .7z and .tar.xz " + + "(error code vm9w6kmaby)" + ) + } +} + +@JvmName("extractTarXzArchiveFileExt") +private fun File.extractTarXzArchive(destDir: File, configure: ExtractArchiveConfigBuilder.() -> Unit) { + val config = ExtractArchiveConfigBuilder().apply(configure).build(destDir) + config.callbacks.onExtractArchiveStarting(ArchiveType.TarXz) + extractTarXzArchive(this, config) +} + +private fun extractTarXzArchive( + inputStream: InputStream, + config: ExtractArchiveConfig +) { + XZCompressorInputStream(inputStream).use { xzInputStream -> + extractTarArchive(xzInputStream, config) + } +} + +private fun extractTarArchive(inputStream: InputStream, config: ExtractArchiveConfig) { + // Use `ArchiveInputStream` as the static type of `tarInputStream`, rather than the more + // natural `TarArchiveInputStream` because this enables compatibility with older versions of + // the Apache Commons Compress library. This is because at one point `ArchiveInputStream` was + // changed to be a _generic_ class which caused the signature of + // `TarArchiveInputStream.getNextEntry()` to change, a breaking ABI change which could cause + // NoSuchMethodErrors at runtime. By using `ArchiveInputStream` directly it uses the + // `getNextEntry()` method from `ArchiveInputStream`, whose ABI did _not_ change, and, + // therefore, links correctly at runtime with both the old and the new Commons Compress library. + val archiveInputStream: ArchiveInputStream<*> = TarArchiveInputStream(inputStream) + + archiveInputStream.use { tarInputStream -> + while (true) { + val tarEntry: ArchiveEntry = tarInputStream.nextEntry ?: break + if (tarEntry !is TarArchiveEntry) { + continue + } + + val outputFile = config.destDir + .childWithPathPrefixComponentsStripped(tarEntry.name, config.prefixStripCount) + .absoluteFile + + if (tarEntry.isSymbolicLink) { + config.callbacks.onExtractSymlink(tarEntry.linkName, outputFile) + Files.createSymbolicLink(outputFile.toPath(), Paths.get(tarEntry.linkName)) + } else if (tarEntry.isFile) { + config.callbacks.onExtractFileStarting(tarEntry.name, outputFile) + outputFile.parentFile.mkdirs() + val extractedByteCount = outputFile.outputStream().use { fileOutputStream -> + tarInputStream.copyTo(fileOutputStream) + } + config.callbacks.onExtractFileDone(tarEntry.name, outputFile, extractedByteCount) + + val lastModifiedTime = FileTime.from(tarEntry.lastModifiedDate.toInstant()) + try { + Files.setLastModifiedTime(outputFile.toPath(), lastModifiedTime) + } catch (e: IOException) { + config.callbacks.onSetFileMetadataFailed(outputFile, ArchiveSetFileMetadataType.LastModifiedTime, e) + } + + val newPermissions = buildSet { + add(PosixFilePermission.OWNER_READ) + add(PosixFilePermission.OWNER_WRITE) + + add(PosixFilePermission.GROUP_READ) + add(PosixFilePermission.OTHERS_READ) + + val mode = tarEntry.mode + if ((mode and 0x100) == 0x100) { + add(PosixFilePermission.OWNER_EXECUTE) + add(PosixFilePermission.GROUP_EXECUTE) + add(PosixFilePermission.OTHERS_EXECUTE) + } + } + try { + Files.setPosixFilePermissions(outputFile.toPath(), newPermissions) + } catch (e: UnsupportedOperationException) { + config.callbacks.onSetFileMetadataFailed( + outputFile, + ArchiveSetFileMetadataType.PosixFilePermissions, + e + ) + } + } else { + continue + } + } + } +} + +private fun extractTarXzArchive(file: File, config: ExtractArchiveConfig) { + file.inputStream().use { fileInputStream -> + extractTarXzArchive(fileInputStream, config) + } +} + +@JvmName("extract7zArchiveFileExt") +private fun File.extract7zArchive(destDir: File, configure: ExtractArchiveConfigBuilder.() -> Unit) { + val config = ExtractArchiveConfigBuilder().apply(configure).build(destDir) + extract7zArchive(this, config) +} + +private fun extract7zArchive(file: File, config: ExtractArchiveConfig) { + TODO("extract7zArchive() not yet implemented") +} + +private class UnsupportedArchiveException(message: String) : Exception(message) + +internal class ExtractArchiveConfigBuilder { + var callbacks: ExtractArchiveCallbacks? = null + var prefixStripCount: Int? = null +} + +private class ExtractArchiveConfig( + val destDir: File, + val callbacks: ExtractArchiveCallbacks, + val prefixStripCount: Int +) + +private fun ExtractArchiveConfigBuilder.build(destDir: File) = ExtractArchiveConfig( + destDir = destDir, + callbacks = callbacks ?: ExtractArchiveCallbacksStubImpl, + prefixStripCount = prefixStripCount ?: 0 +) + +internal interface ExtractArchiveCallbacks { + + fun onExtractArchiveStarting(archiveType: ArchiveType) + + fun onExtractFileStarting(srcPath: String, destFile: File) + + fun onExtractFileDone(srcPath: String, destFile: File, extractedByteCount: Long) + + fun onExtractSymlink(linkPath: String, destFile: File) + + fun onSetFileMetadataFailed(file: File, metadataType: ArchiveSetFileMetadataType, exception: Exception) +} + +private object ExtractArchiveCallbacksStubImpl : ExtractArchiveCallbacks { + override fun onExtractArchiveStarting(archiveType: ArchiveType) { + } + + override fun onExtractFileStarting(srcPath: String, destFile: File) { + } + + override fun onExtractFileDone(srcPath: String, destFile: File, extractedByteCount: Long) { + } + + override fun onExtractSymlink(linkPath: String, destFile: File) { + } + + override fun onSetFileMetadataFailed(file: File, metadataType: ArchiveSetFileMetadataType, exception: Exception) { + } +} + +internal enum class ArchiveType { + TarXz, + SevenZip +} + +internal enum class ArchiveSetFileMetadataType { + LastModifiedTime, + PosixFilePermissions +} + +@JvmName("childWithPathPrefixComponentsStrippedFileExt") +private fun File.childWithPathPrefixComponentsStripped(childPath: String, pathPrefixStripCount: Int): File = + childWithPathPrefixComponentsStripped(this, childPath, pathPrefixStripCount) + +private fun childWithPathPrefixComponentsStripped(file: File, childPath: String, pathPrefixStripCount: Int): File { + require(pathPrefixStripCount >= 0) { "invalid pathPrefixStripCount: $pathPrefixStripCount" } + val pathSeparators = charArrayOf('\\', '/') + + if (pathSeparators.any { childPath.startsWith(it) }) { + throw InvalidArchivePathException( + "unable to extract file: $childPath " + + "(must not be an absolute path)" + + "(error code t827vf36ac)" + ) + } + + val newChildPath = buildString { + append(childPath) + repeat(pathPrefixStripCount) { prefixComponentIndex -> + val index = indexOfAny(pathSeparators) + if (index < 0) { + throw MissingPathPrefixException( + "the given childPath was expected to have at least " + + "$pathPrefixStripCount leading path components, " + + "but there were only $prefixComponentIndex " + + "(error code radpfa8nc9)" + ) + } + deleteRange(0, index + 1) + while (isNotEmpty() && first() in pathSeparators) { + deleteAt(0) + } + } + } + + return File(file, newChildPath) +} + +private class InvalidArchivePathException(message: String) : Exception(message) + +private class MissingPathPrefixException(message: String) : Exception(message) diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/DataConnectGradleLogger.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/DataConnectGradleLogger.kt new file mode 100644 index 0000000000..a0e0262157 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/DataConnectGradleLogger.kt @@ -0,0 +1,71 @@ +/* + * 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.example.dataconnect.gradle.util + +import kotlin.random.Random +import org.gradle.api.logging.LogLevel +import org.gradle.api.logging.Logger + +/** + * A wrapper around a [Logger] that provides a bit of functionality used commonly by the + * Firebase Data Connect Gradle plugin and its classes. + * + * Each logged message will be prefixed with a randomly-generated alphanumeric prefix, + * making it easy to track the output of a logger throughout the sea of log messages in + * Gradle's output. + * + * @param loggerIdPrefix A string with which to prefix the randomly-generated logger ID + * included in each message logged by this object. This is typically a very short (between + * 2 and 4 characters) string whose prefix will give some indication of where the logged + * messages came from. A common strategy is to use the uppercase characters of a class name; + * for example a class named "DataFileLoader" could use the `loggerIdPrefix` of "dfl". + * @param logger The logger that will be used to do the actual logging. + */ +public class DataConnectGradleLogger(loggerIdPrefix: String, private val logger: Logger) { + + private val loggerId: String = "$loggerIdPrefix${Random.nextAlphanumericString(8)}" + + public val isDebugEnabled: Boolean get() = logger.isDebugEnabled + + public val isInfoEnabled: Boolean get() = logger.isInfoEnabled + + public val isWarnEnabled: Boolean get() = logger.isWarnEnabled + + public inline fun debug(block: () -> String) { + if (isDebugEnabled) { + log(LogLevel.DEBUG, block()) + } + } + + public inline fun info(block: () -> String) { + if (isInfoEnabled) { + log(LogLevel.INFO, block()) + } + } + + public inline fun warn(block: () -> String) { + if (isWarnEnabled) { + log(LogLevel.WARN, block()) + } + } + + public fun warn(message: String): Unit = log(LogLevel.WARN, message, prefix = "WARNING: ") + + public fun log(level: LogLevel, message: String, prefix: String = "") { + logger.log(level, "[{}] {}{}", loggerId, prefix, message) + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/FileDownloader.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/FileDownloader.kt new file mode 100644 index 0000000000..026d00a18b --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/FileDownloader.kt @@ -0,0 +1,128 @@ +/* + * 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.example.dataconnect.gradle.util + +import com.google.firebase.example.dataconnect.gradle.DataConnectGradleException +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.call.body +import io.ktor.client.engine.cio.CIO +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.request.prepareGet +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.jvm.javaio.copyTo +import java.io.File +import java.text.NumberFormat +import java.util.concurrent.atomic.AtomicReference + +internal class FileDownloader(private val logger: DataConnectGradleLogger) : AutoCloseable { + + private val state: AtomicReference = AtomicReference(State.Uninitialized) + private val httpClient: HttpClient get() = getOrCreateHttpClient() + + suspend fun download(url: String, destFile: File, maxNumDownloadBytes: Long) { + logger.info { "Downloading $url to ${destFile.absolutePath}" } + + val actualNumBytesDownloaded = httpClient.prepareGet(url).execute { httpResponse -> + val downloadChannel: ByteReadChannel = httpResponse.body() + destFile.outputStream().use { destFileOutputStream -> + downloadChannel.copyTo(destFileOutputStream, limit = maxNumDownloadBytes) + } + } + + val numberFormat = NumberFormat.getNumberInstance() + val actualNumBytesDownloadedStr = numberFormat.format(actualNumBytesDownloaded) + if (actualNumBytesDownloaded >= maxNumDownloadBytes) { + val maxNumDownloadBytesStr = numberFormat.format(maxNumDownloadBytes) + throw DataConnectGradleException( + "Downloading $url failed: " + + "maximum file size $maxNumDownloadBytesStr bytes exceeded; " + + "cancelled after downloading $actualNumBytesDownloadedStr bytes " + + "(error code hvmhysn5vy)" + ) + } + + logger.info { + "Successfully downloaded $actualNumBytesDownloadedStr bytes from $url " + + "to ${destFile.absolutePath}" + } + } + + override fun close() { + while (true) { + val oldState = state.get() + val httpClient = when (oldState) { + is State.Uninitialized -> null + is State.Open -> oldState.httpClient + is State.Closed -> break + } + if (state.compareAndSet(oldState, State.Closed)) { + httpClient?.close() + } + } + } + + private fun newHttpClient(): HttpClient = HttpClient(CIO) { + expectSuccess = true + installDataConnectLogger(logger) + } + + private fun getOrCreateHttpClient(): HttpClient { + while (true) { + when (val oldState = state.get()) { + is State.Open -> return oldState.httpClient + is State.Closed -> throw DataConnectGradleException( + "FileDownloader has been closed (error code thp7vtw9rm)" + ) + is State.Uninitialized -> { + val httpClient = newHttpClient() + if (!state.compareAndSet(oldState, State.Open(httpClient))) { + httpClient.close() + } + } + } + } + } + + private sealed interface State { + object Uninitialized : State + class Open(val httpClient: HttpClient) : State + object Closed : State + } +} + +private fun HttpClientConfig<*>.installDataConnectLogger(dataConnectGradleLogger: DataConnectGradleLogger) { + install(Logging) { + level = if (dataConnectGradleLogger.isDebugEnabled) { + LogLevel.HEADERS + } else if (dataConnectGradleLogger.isInfoEnabled) { + LogLevel.INFO + } else { + LogLevel.NONE + } + + logger = object : Logger { + override fun log(message: String) { + message.lines().forEach { line -> + dataConnectGradleLogger.info { "ktor: ${line.trimEnd()}" } + } + } + } + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/FileKtx.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/FileKtx.kt new file mode 100644 index 0000000000..964193d7e1 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/FileKtx.kt @@ -0,0 +1,54 @@ +/* + * 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.example.dataconnect.gradle.util + +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException + +internal fun File.readBytes(byteLimit: Int): ByteArray = inputStream().use { inputStream -> + require(byteLimit >= 0) { + "invalid byte limit: $byteLimit " + + "(must be greater than or equal to zero) (error code vrr6daevyw)" + } + + val buffer = ByteArray(8192) + val byteArrayOutputStream = ByteArrayOutputStream() + var totalByteReadCount = 0 + while (true) { + val numBytesJustRead = inputStream.read(buffer) + if (numBytesJustRead < 0) { + break + } + + totalByteReadCount += numBytesJustRead + if (totalByteReadCount > byteLimit) { + val byteLimitStr = String.format("%,d", byteLimit) + val totalByteReadCountStr = String.format("%,d", totalByteReadCount) + throw TooManyBytesReadException( + "too many bytes read: $totalByteReadCountStr " + + "(expected at most $byteLimitStr) (error code fnaxenqmxs)" + ) + } + + byteArrayOutputStream.write(buffer, 0, numBytesJustRead) + } + + return byteArrayOutputStream.toByteArray() +} + +public class TooManyBytesReadException(message: String) : IOException(message) diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/GradleKtx.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/GradleKtx.kt new file mode 100644 index 0000000000..07c33baef2 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/GradleKtx.kt @@ -0,0 +1,43 @@ +/* + * 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.example.dataconnect.gradle.util + +import java.io.File +import org.gradle.api.logging.Logger +import org.gradle.process.ExecOperations +import org.gradle.process.ExecSpec + +internal fun ExecOperations.runCommand(logFile: File, logger: Logger, configure: ExecSpec.() -> Unit) { + val effectiveLogFile = if (logger.isInfoEnabled) null else logFile + val result = + effectiveLogFile?.outputStream().use { logStream -> + runCatching { + exec { + isIgnoreExitValue = false + if (logStream !== null) { + standardOutput = logStream + errorOutput = logStream + } + configure(this) + } + } + } + result.onFailure { exception -> + effectiveLogFile?.let { logger.warn("{}", it.readText()) } + throw exception + } +} diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/LongKtx.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/LongKtx.kt new file mode 100644 index 0000000000..0b2c6cf015 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/LongKtx.kt @@ -0,0 +1,21 @@ +/* + * 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.example.dataconnect.gradle.util + +import java.text.NumberFormat + +internal fun Long.toFormattedString(): String = NumberFormat.getNumberInstance().format(this) diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/NodeJsPaths.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/NodeJsPaths.kt new file mode 100644 index 0000000000..0da15e1db8 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/NodeJsPaths.kt @@ -0,0 +1,189 @@ +/* + * 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.example.dataconnect.gradle.util + +import com.google.firebase.example.dataconnect.gradle.DataConnectGradleException +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory + +public class NodeJsPaths( + public val archiveUrl: String, + public val archiveBaseFileName: String, + public val archiveFileName: String, + public val shasumsUrl: String, + public val shasumsFileName: String, + public val nodeExecutableRelativePath: String, + public val npmExecutableRelativePath: String +) { + public companion object +} + +internal fun ProviderFactory.nodeJsPaths( + nodeJsVersion: Provider, + operatingSystem: Provider +): Provider = provider { + @Suppress("NAME_SHADOWING") + val nodeJsVersion: String = nodeJsVersion.get() + + @Suppress("NAME_SHADOWING") + val operatingSystem: OperatingSystem = operatingSystem.get() + NodeJsPaths.from(nodeJsVersion, operatingSystem.type, operatingSystem.architecture) +} + +public fun NodeJsPaths.Companion.from( + nodeJsVersion: String, + operatingSystemType: OperatingSystem.Type, + operatingSystemArchitecture: OperatingSystem.Architecture +): NodeJsPaths { + val calculator = NodeJsPathsCalculator( + nodeJsVersion, + operatingSystemType, + operatingSystemArchitecture + ) + return NodeJsPaths( + archiveUrl = calculator.archiveUrl(), + archiveBaseFileName = calculator.archiveBaseFileName(), + archiveFileName = calculator.archiveFileName(), + shasumsUrl = calculator.shasumsUrl(), + shasumsFileName = calculator.shasumsFileName(), + nodeExecutableRelativePath = calculator.nodeExecutableRelativePath(), + npmExecutableRelativePath = calculator.npmExecutableRelativePath() + ) +} + +private class NodeJsPathsCalculator( + val nodeJsVersion: String, + val operatingSystemType: OperatingSystem.Type, + val operatingSystemArchitecture: OperatingSystem.Architecture +) + +private fun NodeJsPathsCalculator.urlForFileWithName(fileName: String): String = + "https://nodejs.org/dist/v$nodeJsVersion/$fileName" + +/** + * The URL to download the Node.js binary distribution. + * + * Here are some examples: + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-darwin-arm64.tar.xz + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-darwin-x64.tar.xz + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-linux-arm64.tar.xz + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-linux-armv7l.tar.xz + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-linux-x64.tar.xz + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-win-arm64.7z + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-win-x64.7z + * * https://nodejs.org/dist/v20.9.0/node-v20.9.0-win-x86.7z + */ +private fun NodeJsPathsCalculator.archiveUrl(): String = urlForFileWithName(archiveFileName()) + +@Suppress("UnusedReceiverParameter") +private fun NodeJsPathsCalculator.shasumsFileName(): String = "SHASUMS256.txt.asc" + +private fun NodeJsPathsCalculator.shasumsUrl(): String = urlForFileWithName(shasumsFileName()) + +/** + * The file name of the download for the Node.js binary distribution. + * + * Here are some examples: + * * node-v20.9.0-darwin-arm64.tar.xz + * * node-v20.9.0-darwin-x64.tar.xz + * * node-v20.9.0-linux-arm64.tar.xz + * * node-v20.9.0-linux-armv7l.tar.xz + * * node-v20.9.0-linux-x64.tar.xz + * * node-v20.9.0-win-arm64.7z + * * node-v20.9.0-win-x64.7z + * * node-v20.9.0-win-x86.7z + */ +private fun NodeJsPathsCalculator.archiveFileName(): String = + "${archiveBaseFileName()}${archiveFileNameSuffix()}" + +/** + * The suffix of the file name download for the Node.js binary distribution. + * + * Here are some examples: + * * .tar.xz + * * .7z + */ +private fun NodeJsPathsCalculator.archiveFileNameSuffix(): String = when (operatingSystemType) { + OperatingSystem.Type.Windows -> ".7z" + OperatingSystem.Type.MacOS, + OperatingSystem.Type.Linux -> ".tar.xz" + else -> throw DataConnectGradleException( + "unable to determine Node.js download file name suffix " + + "for operating system type: $operatingSystemType " + + "(error code ead53smf45)" + ) +} + +/** + * The base file name of the download for the Node.js binary distribution; + * that is, the file name without the ".7z" or ".tar.xz" extension. + * + * Here are some examples: + * * node-v20.9.0-darwin-arm64 + * * node-v20.9.0-darwin-x64 + * * node-v20.9.0-linux-arm64 + * * node-v20.9.0-linux-armv7l + * * node-v20.9.0-linux-x64 + * * node-v20.9.0-win-arm64 + * * node-v20.9.0-win-x64 + * * node-v20.9.0-win-x86 + */ +private fun NodeJsPathsCalculator.archiveBaseFileName(): String { + val osType: String = when (operatingSystemType) { + OperatingSystem.Type.Windows -> "win" + OperatingSystem.Type.MacOS -> "darwin" + OperatingSystem.Type.Linux -> "linux" + else -> throw DataConnectGradleException( + "unable to determine Node.js download base file name " + + "for operating system type: $operatingSystemType " + + "(error code m2grw3h7xz)" + ) + } + + val osArch: String = when (operatingSystemArchitecture) { + OperatingSystem.Architecture.Arm64 -> "arm64" + OperatingSystem.Architecture.ArmV7 -> "armv7l" + OperatingSystem.Architecture.X86 -> "x86" + OperatingSystem.Architecture.X86_64 -> "x64" + } + + return "node-v$nodeJsVersion-$osType-$osArch" +} + +private fun NodeJsPathsCalculator.nodeExecutableRelativePath(): String = + when (operatingSystemType) { + OperatingSystem.Type.Windows -> "node.exe" + OperatingSystem.Type.MacOS, + OperatingSystem.Type.Linux -> "bin/node" + else -> throw DataConnectGradleException( + "unable to determine nodeexecutable path " + + "for operating system type: $operatingSystemType " + + "(error code bjs8et7w6a)" + ) + } + +private fun NodeJsPathsCalculator.npmExecutableRelativePath(): String = + when (operatingSystemType) { + OperatingSystem.Type.Windows -> "npm.cmd" + OperatingSystem.Type.MacOS, + OperatingSystem.Type.Linux -> "bin/node" + else -> throw DataConnectGradleException( + "unable to determine npm executable path " + + "for operating system type: $operatingSystemType " + + "(error code zrrsk9g5n4)" + ) + } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/OperatingSystem.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/OperatingSystem.kt new file mode 100644 index 0000000000..64838062c4 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/OperatingSystem.kt @@ -0,0 +1,126 @@ +/* + * 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.example.dataconnect.gradle.util + +import kotlinx.serialization.Serializable +import org.gradle.api.GradleException +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.Input + +@Serializable +public data class OperatingSystem( + @get:Input val type: Type, + @get:Input val architecture: Architecture +) { + + public enum class Type { + Windows, + Linux, + MacOS, + FreeBSD, + Solaris; + + public companion object + } + + public enum class Architecture { + Arm64, + ArmV7, + X86, + X86_64; + + public companion object + } + + public companion object +} + +internal fun ProviderFactory.operatingSystem(): Provider { + val osNameSystemPropertyName = "os.name" + val osArchitectureSystemPropertyName = "os.arch" + val osNameProvider = systemProperty(osNameSystemPropertyName) + val osArchitectureProvider = systemProperty(osArchitectureSystemPropertyName) + + return provider { + val osName = osNameProvider.orNull + val osArchitecture = osArchitectureProvider.orNull + + val type = osName?.let { OperatingSystem.Type.forName(it) } + val architecture = osArchitecture?.let { OperatingSystem.Architecture.forName(it) } + + if (type === null || architecture === null) { + throw GradleException( + "unable to determine operating system from Java system properties: " + + "$osNameSystemPropertyName=$osName, " + + "$osArchitectureSystemPropertyName=$osArchitecture " + + "(computed values: type=$type, architecture=$architecture) " + + "(error code qecxcvcf8n)" + ) + } + + OperatingSystem(type = type, architecture = architecture) + } +} + +/** + * Returns the [OperatingSystem.Type] for the given operating system name. + * + * @param osName the name of the operating system whose value to return; this value should be one that was + * returned from [System.getProperty] called with `"os.name"`. + */ +public fun OperatingSystem.Type.Companion.forName(osName: String): OperatingSystem.Type? = + forLowerCaseName( + osName.lowercase() + ) + +// NOTE: This logic was adapted from +// https://github.com/gradle/gradle/blob/99d83f56d6/platforms/core-runtime/base-services/src/main/java/org/gradle/internal/os/OperatingSystem.java#L63-L79 +private fun OperatingSystem.Type.Companion.forLowerCaseName(osName: String): OperatingSystem.Type? = + if (osName.contains("windows")) { + OperatingSystem.Type.Windows + } else if (osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx")) { + OperatingSystem.Type.MacOS + } else if (osName.contains("sunos") || osName.contains("solaris")) { + OperatingSystem.Type.Solaris + } else if (osName.contains("linux")) { + OperatingSystem.Type.Linux + } else if (osName.contains("freebsd")) { + OperatingSystem.Type.FreeBSD + } else { + null + } + +/** + * Returns the [OperatingSystem.Type] for the given operating system name. + * + * @param osArch the name of the operating system whose value to return; this value should be one + * that was returned from [System.getProperty] called with `"os.name"`. + */ +public fun OperatingSystem.Architecture.Companion.forName(osArch: String): OperatingSystem.Architecture? = + forLowerCaseName(osArch.lowercase()) + +// NOTE: This logic was adapted from +// https://github.com/gradle/gradle/blob/e745e6d369/platforms/native/platform-native/src/main/java/org/gradle/nativeplatform/platform/internal/Architectures.java#L26-L42 +private fun OperatingSystem.Architecture.Companion.forLowerCaseName(osArch: String): OperatingSystem.Architecture? = + when (osArch) { + "x86", "i386", "ia-32", "i686" -> OperatingSystem.Architecture.X86 + "x86-64", "x86_64", "amd64", "x64" -> OperatingSystem.Architecture.X86_64 + "arm-v7", "armv7", "arm", "arm32" -> OperatingSystem.Architecture.ArmV7 + "aarch64", "arm-v8", "arm64" -> OperatingSystem.Architecture.Arm64 + else -> null + } diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/RandomKtx.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/RandomKtx.kt new file mode 100644 index 0000000000..8c9ad678bb --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/RandomKtx.kt @@ -0,0 +1,40 @@ +/* + * 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.example.dataconnect.gradle.util + +import kotlin.random.Random + +/** + * Generates and returns a string containing random alphanumeric characters. + * + * The characters returned are taken from the set of characters comprising of the 10 numeric digits + * and the 26 lowercase English characters. + * + * @param length the number of random characters to generate and include in the returned string; + * must be greater than or equal to zero. + * @return a string containing the given number of random alphanumeric characters. + */ +internal fun Random.nextAlphanumericString(length: Int): String { + require(length >= 0) { "invalid length: $length" } + return (0 until length).map { ALPHANUMERIC_ALPHABET.random(this) }.joinToString(separator = "") +} + +// The set of characters comprised of the 10 numeric digits and the 26 lowercase letters of the +// English alphabet with some characters removed that can look similar in different fonts, like +// '1', 'l', and 'i'. +@Suppress("SpellCheckingInspection") +private const val ALPHANUMERIC_ALPHABET = "23456789abcdefghjkmnpqrstvwxyz" diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/SHASUMS256.txt.asc.example b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/SHASUMS256.txt.asc.example new file mode 100644 index 0000000000..0cc270fdf5 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/SHASUMS256.txt.asc.example @@ -0,0 +1,60 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +dc148e207d1a6456af05bf55d2c5af0185a2c79139fa64c8278ca257dc4894d5 node-v20.18.1-aix-ppc64.tar.gz +1f05a889ee095bebae179d794d409d2c4a6b42f63be891a9b1e16740712891e6 node-v20.18.1-arm64.msi +9e92ce1032455a9cc419fe71e908b27ae477799371b45a0844eedb02279922a4 node-v20.18.1-darwin-arm64.tar.gz +68dbaeb8e37be25699c47788d00f2ec748ae6599e5f1696a695b1e3b4312db97 node-v20.18.1-darwin-arm64.tar.xz +c5497dd17c8875b53712edaf99052f961013cedc203964583fc0cfc0aaf93581 node-v20.18.1-darwin-x64.tar.gz +2f40325262474304101da93df9f1a0ced16fd11eabcc7aeec96c085a2e34e10f node-v20.18.1-darwin-x64.tar.xz +4a228c99ff3c1ea9c052336f3353bf157f7bf0a339da9acabba8ec4ec728d871 node-v20.18.1-headers.tar.gz +c29a72345fbbfa06a78bbfd129fcb2db097f138be17408a520420ae38406243d node-v20.18.1-headers.tar.xz +73cd297378572e0bc9dfc187c5ec8cca8d43aee6a596c10ebea1ed5f9ec682b6 node-v20.18.1-linux-arm64.tar.gz +44d1ffc5905c005ace4515ca6f8c090c4c7cfce3a9a67df0dba35c727590b8f6 node-v20.18.1-linux-arm64.tar.xz +7b7c3315818e9fe57512737c2380fada14d8717ce88945fb6f7b8baadd3cfb92 node-v20.18.1-linux-armv7l.tar.gz +b5a02f354f67d8f4bda8c563954f4805eccb4039f66a5dcdf755168aaac4e16c node-v20.18.1-linux-armv7l.tar.xz +703fc140e330002020bf54cffcfbbf8a957413b23fe6940a9f5a147e5103e960 node-v20.18.1-linux-ppc64le.tar.gz +ad01ea5905e8fb587f306d6cf44cce62a334aaa5e689f1f086ae4d03c4eaf2f2 node-v20.18.1-linux-ppc64le.tar.xz +15724744d75fa10f5856b17d5293d141175a2832b7c17bf12df669d4b22b12bc node-v20.18.1-linux-s390x.tar.gz +1df248c207d8557ec1c554242758b5852bad5e0da49108d2dd94abcb9e9eae52 node-v20.18.1-linux-s390x.tar.xz +259e5a8bf2e15ecece65bd2a47153262eda71c0b2c9700d5e703ce4951572784 node-v20.18.1-linux-x64.tar.gz +c6fa75c841cbffac851678a472f2a5bd612fff8308ef39236190e1f8dbb0e567 node-v20.18.1-linux-x64.tar.xz +ed547fa08a8855b6ce1b88e9b8a5ec839a02a7358868a00cd772878e78b560e3 node-v20.18.1.pkg +5bad8ced873eef3b32e7daee703156bce9224920ac6044f4232f5393df0628b8 node-v20.18.1.tar.gz +91df43f8ab6c3f7be81522d73313dbdd5634bbca228ef0e6d9369fe0ab8cccd0 node-v20.18.1.tar.xz +b4cc958546b0743123f958f5c607f21cd8029bde8f7f0989cc99b9b38414e81a node-v20.18.1-win-arm64.7z +7c03744df29e81c34043a956969b3afc34171d3ab85e25fc737eb1860222444f node-v20.18.1-win-arm64.zip +08ce2180a82fb5ba3cd4dc8f0744e3f5029f1745359dd3833dfcf0e10148e7d0 node-v20.18.1-win-x64.7z +56e5aacdeee7168871721b75819ccacf2367de8761b78eaceacdecd41e04ca03 node-v20.18.1-win-x64.zip +18c03af826919dc791613ee3ff7903cc78e68627e8da44ecaf55a40eb6cd994a node-v20.18.1-win-x86.7z +08987ceb478044b652ad57e15b96597e1eaf7f06502336b5a02c545f9e403ed6 node-v20.18.1-win-x86.zip +658930e6136d01bf244146a6436d8ea146cd50557c1b2a2617a60bcd9dce0da1 node-v20.18.1-x64.msi +6f89d8ee1ff0daf73ef408b9187a8f787327ddc6c393d5b8cdcbf9c6ff04b7c6 node-v20.18.1-x86.msi +9a52905b5d22b08b5b34e86b8d5358cd22c03fbd0fcb9dbb37c9bd82a8f18c17 win-arm64/node.exe +58795bcd44e8023ff443dedabf7f9af928732a51befc5324082aafe56e0f5eb0 win-arm64/node.lib +37d60e51b1fa23027187cc63bd79ef485de5777a601bbe74d5985720dc9dfabd win-arm64/node_pdb.7z +5e716c36e4a9e46406955d4cf4598cdca28c7e61c218192d8f8de7cead3bf15e win-arm64/node_pdb.zip +06c1dec1b428927d6ff01c8f5882f119ec13b61ac77483760aa7fba215c72cf5 win-x64/node.exe +a2df424ebe89449195d830e80cb2ade2e7ea5fe9b27ca9510de173b9a06733ae win-x64/node.lib +e33ea0808185d7607dc7e8c996aa9e21499801c6a6c5ecd4ab71751ee7539474 win-x64/node_pdb.7z +2250b545b0c6b4f3bd1cf2a31dfb0f5c91b90c3de41f8656df0359f74da47900 win-x64/node_pdb.zip +ff782544e1514f58bc334955d4fdc4bb4df06b277c693c74762eb9740ac2f42b win-x86/node.exe +a2ccacb78f399dceb60f5ad275c9380c5cf498202ef1cb7f7bd789d844ab0daa win-x86/node.lib +47aca7d7e52747b0bd08c8f1a3a19eda9e512ee194c801402897ee3abc86ddc1 win-x86/node_pdb.7z +64457eb79562c7e8d97544ec9b06ad92425778010bda3771001a28f5503ff55d win-x86/node_pdb.zip +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCAAdFiEEzGj1oxBv9EgyLkjtJ/XjjVsKIV8FAmc981QACgkQJ/XjjVsK +IV+LkRAAmyE2ddbS29NI9kIYI9kETn5kGPlJr7Hkk8KV1XhHpUJ8Abb3L1e8hBmv +QVtDhccNZfbrs/tE3ChKxv9BfYQB+sqWg2ckPDlIs2NnC8S6LYfrpiDATY+sLGax +F0zT3yO9SnoqbZ/FsNbvLjr7bNqrUxc1dtPfl4/dtHSeSUGeKGGbbe/mvbUW4l40 +tnZpDIxIwWXxkgnnG0Nt6nI7R5MXeCDUaf/CuG46sumvsmC1HnMH471vQVq+nSZD +a2U+R1SsTbIM6gCjWQP4laTbtQdXIy9WRQNoXl9NRkinzAQvUY3/i0RLT4IBuonH +tUe0MFsayaIn5k3IUSOT5XHfynwIxXthx6hJBGq3GDrHLo2SYpNeSmgtKVI0yXmm +UmQWjknz2M5dHVEUyG2rWdvz58Rp9ocITmX1WmTSoig+hBRDNaoe9hipA1AxJ5x4 +A/uA/uRt0eysBkKpcqt3Jj/9uORRNV9j2+uzj2D5wkrOfTeuDLm8X1oBuXEdtpU+ +IM4UoXdVlSJ6AWL1W3P//XCj8gEYsaRbgmOg1z/U6wgqtxYDCefebDNx80ImTeap +ffKiBva4OmtGydvXtzt6pqslZFdLvjSw5UYy8diHDBnn3ZV/NCsbiVYJ7eQKHkKM +/AwHFB4JQbicXS8a2lYnnsJs0LDHO7VhdXU1Fj680SbH0FwIeFU= +=No17 +-----END PGP SIGNATURE----- diff --git a/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/Sha256SignatureVerifier.kt b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/Sha256SignatureVerifier.kt new file mode 100644 index 0000000000..13d9d7d358 --- /dev/null +++ b/dataconnect/buildSrc/src/main/kotlin/com/google/firebase/example/dataconnect/gradle/util/Sha256SignatureVerifier.kt @@ -0,0 +1,218 @@ +package com.google.firebase.example.dataconnect.gradle.util + +import com.google.firebase.example.dataconnect.gradle.DataConnectGradleException +import java.io.File +import java.io.InputStream +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock +import org.bouncycastle.util.encoders.Hex +import org.pgpainless.sop.SOPImpl + +/** + * Verifies the SHA256 hash of files. + * + * The immediate use case for this class in the Data Connect Gradle plugin is to verify the + * hash of the downloaded Node.js binary distribution archive, ensuring that it is a legitimate + * binary distribution that has not been tampered. + * + * This class is thread-safe; that is, instances of this class can safely have their methods invoked + * concurrently from multiple threads. + */ +internal class Sha256SignatureVerifier { + + private val lock = ReentrantLock() + private val certificates = mutableListOf() + private val hashByFileName = mutableMapOf() + + /** + * Verifies that the SHA256 hash of the bytes matches the expected hash. + * + * @param inputStream the stream of bytes whose hash to calculate and verify. + * @param fileName the name of the file whose expected hash to use; the hash for this file name + * must have been previously added by a call to [addHashForFileName]. + * @throws UnknownHashException if the hash for the given file name was _not_ added by a + * previous invocation of [addHashForFileName]. + * @throws IncorrectHashException if the hash of the bytes of the given input stream does _not_ + * match the expected hash of the given file name, as added by a previous invocation of + * [addHashForFileName]. + */ + fun verifyHash(inputStream: InputStream, fileName: String) { + val expectedSha256Hash = lock.withLock { + hashByFileName[fileName] + } + if (expectedSha256Hash === null) { + throw UnknownHashException( + "expected SHA256 hash for file name not known: $fileName " + + "(did you call addHashForFileName() with the given file name?) " + + "(error code ws53hqrmd6)" + ) + } + + val actualSha256Hash = run { + val messageDigest = MessageDigest.getInstance("SHA-256") + val buffer = ByteArray(8192) + while (true) { + val readCount = inputStream.read(buffer) + if (readCount < 0) { + break + } + messageDigest.update(buffer, 0, readCount) + } + val digest = messageDigest.digest() + Hex.toHexString(digest) + } + + if (expectedSha256Hash != actualSha256Hash) { + throw IncorrectHashException( + "SHA256 hash of $fileName did not match the expected hash: " + + "$actualSha256Hash (expected $expectedSha256Hash)" + ) + } + } + + /** + * Adds the expected SHA256 hash for a file with the given name. + * + * @param fileName The name of the file whose hash to add. + * @param hash The hex encoding of the SHA256 hash for a file with the given file name + * (e.g. "dc148e207d1a6456af05bf55d2c5af0185a2c79139fa64c8278ca257dc4894d5"). + */ + fun addHashForFileName(fileName: String, hash: String) { + lock.withLock { + hashByFileName.put(fileName, hash) + } + } + + /** + * Adds a signing certificate to this object's internal collection of certificates. + * + * The collection of certificates that this method manipulates is the one used by [verifySignature]. + * + * The given certificate must be a OpenPGP certificate, which typically comes from a file + * with the extension ".asc" or ".gpg". + * + * @param certificate the bytes of the certificate to install. + */ + fun addCertificate(certificate: ByteArray) { + lock.withLock { + certificates.add(certificate) + } + } + + /** + * Verifies the signature of the given bytes, which are expected to be an ASCII armor encoded + * file. The certificate to use to verify the signature must have been added by a previous + * invocation of [addCertificate]. + * + * @param bytes The bytes whose signature to verify, such as the contents of a text file with + * the ".asc" extension. + * @return the given bytes with the signature information stripped; that is, the actual bytes + * whose signature was verified. + * @throws Exception if anything goes wrong with the signature verification, such as malformed + * signature or the certificate for the signature has not been added. + */ + fun verifySignature(bytes: ByteArray): ByteArray { + val sop = SOPImpl() + + val verificationResult = sop.inlineVerify().let { verifier -> + lock.withLock { + certificates.forEach { + verifier.cert(it) + } + } + verifier.data(bytes).toByteArrayAndResult() + } + + return verificationResult.bytes + } + + class UnknownHashException(message: String) : Exception(message) + class IncorrectHashException(message: String) : Exception(message) +} + +/** + * Shorthand for [Sha256SignatureVerifier.addCertificate], calling it with the bytes of the given + * resource, as loaded by [ClassLoader.getResourceAsStream]. + */ +internal fun Sha256SignatureVerifier.addCertificateFromResource(resourcePath: String) { + val certificateBytes = loadResource(resourcePath) + addCertificate(certificateBytes) +} + +/** + * Shorthand for [addCertificateFromResource], calling it with the bytes of each + * resource specified in the given "key list" resource, as loaded by + * [ClassLoader.getResourceAsStream]. + * + * @return the number of certificates that were added by this method invocation. + */ +internal fun Sha256SignatureVerifier.addCertificatesFromKeyListResource(keyListResourcePath: String): Int { + val keyNames = loadKeyNameList(keyListResourcePath) + + val lastSlashIndex = keyListResourcePath.lastIndexOf('/') + val resourceKeyPathPrefix = if (lastSlashIndex <= 0) { + "" + } else { + keyListResourcePath.substring(0..lastSlashIndex) + } + + keyNames.forEach { + addCertificateFromResource("$resourceKeyPathPrefix$it.asc") + } + + return keyNames.size +} + +private fun Sha256SignatureVerifier.loadResource(path: String): ByteArray { + val classLoader = this::class.java.classLoader + return classLoader.getResourceAsStream(path).let { inputStream -> + inputStream.use { + if (inputStream === null) { + throw DataConnectGradleException("resource not found: $path (error code 6ygyz2dj2n)") + } + inputStream.readAllBytes() + } + } +} + +private fun Sha256SignatureVerifier.loadKeyNameList(resourcePath: String): List { + val resourceBytes = loadResource(resourcePath) + val resourceText = String(resourceBytes, StandardCharsets.UTF_8) + return resourceText.lines().map { it.trim() }.filter { it.isNotBlank() } +} + +/** + * Adds SHA256 hashes and their corresponding file names loaded from the given file by calling + * [Sha256SignatureVerifier.addHashForFileName] for each hash/filename pair loaded from the given + * file. + * + * The given file must be an "ASCII armor" text file that has a valid signature. The signature + * must have been signed by one (or more) of the certificates that installed by a previous + * invocation of [Sha256SignatureVerifier.addCertificate]. + * + * An example of such a file is https://nodejs.org/dist/v20.18.1/SHASUMS256.txt.asc, which, for + * convenience, has been downloaded into the same directory as this file with the name + * `SHASUMS256.txt.asc.example`. + * + * @return the names of the files whose hashes were added. + */ +internal fun Sha256SignatureVerifier.addHashesFromShasumsFile(shasumsFile: File): Set { + val shasumsFileSignedBytes = shasumsFile.readBytes(byteLimit = 1_000_000) + val shasumsFileBytes = this.verifySignature(shasumsFileSignedBytes) + val shasumsFileLines = String(shasumsFileBytes, StandardCharsets.UTF_8).lines() + + val regex = Regex("""\s*(.*?)\s+(.*?)\s*""") + val hashByFileName = shasumsFileLines.mapNotNull { regex.matchEntire(it) }.associate { + val fileName = it.groupValues[2] + val hash = it.groupValues[1] + fileName to hash + } + + hashByFileName.forEach { + addHashForFileName(fileName = it.key, hash = it.value) + } + + return hashByFileName.keys +} diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/108F52B48DB57BB0CC439B2997B01419BD92F80A.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/108F52B48DB57BB0CC439B2997B01419BD92F80A.asc new file mode 100644 index 0000000000..195596d5ff --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/108F52B48DB57BB0CC439B2997B01419BD92F80A.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +AI4= +=Sf/w +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/114F43EE0176B71C7BC219DD50A3051F888C628D.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/114F43EE0176B71C7BC219DD50A3051F888C628D.asc new file mode 100644 index 0000000000..9c8d081bec --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/114F43EE0176B71C7BC219DD50A3051F888C628D.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +USXWhEefc3s6beE= +=v6Nn +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/141F07595B7B3FFE74309A937405533BE57C7D57.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/141F07595B7B3FFE74309A937405533BE57C7D57.asc new file mode 100644 index 0000000000..b6a6dad9e0 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/141F07595B7B3FFE74309A937405533BE57C7D57.asc @@ -0,0 +1,86 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=48+4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/1C050899334244A8AF75E53792EF661D867B9DFA.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/1C050899334244A8AF75E53792EF661D867B9DFA.asc new file mode 100644 index 0000000000..16281839ca --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/1C050899334244A8AF75E53792EF661D867B9DFA.asc @@ -0,0 +1,38 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=ZsQu +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/4ED778F539E3634C779C87C6D7062848A1AB005C.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/4ED778F539E3634C779C87C6D7062848A1AB005C.asc new file mode 100644 index 0000000000..42c0d82778 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/4ED778F539E3634C779C87C6D7062848A1AB005C.asc @@ -0,0 +1,89 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFq44CwBCADNRnp3EGOqifmbqOgRb64hkObYdNAClPy/aQfxyWvrZBuVw8OF +DhtziM8M8g986wALaE/nCMufVLrWLVFr4hDHrKr9weaX8vdrPVgvbk/wLfokumnT +ied2EXUYv4i1+PFPLnBEfb/FhG/x11mSStIra74JIw7C3uLbBdZfU5SBI9SRjFEg +IMHnnTVrXsoZCf+MBUU5nN+tEuOk5s2bnb8rZOsDfdkMLblLbk7j9OvU4OfJ9cLa +tNk0wsvrXmOkxAkr0NNwaotb6xqQwXML1obiBkLl3cZ8c9PnvxWnEau8jItvO/+n +VOgSIvCQd/obAZzAYWPKrYwLp5iEwejB66XLABEBAAG0IEJldGggR3JpZ2dzIDxi +Z3JpZ2dzQHJlZGhhdC5jb20+iQFXBBMBCABBAhsDBQsJCAcCBhUKCQgLAgQWAgMB +Ah4BAheAAhkBFiEETtd49TnjY0x3nIfG1wYoSKGrAFwFAmBePXEFCQln0lUACgkQ +1wYoSKGrAFyFMwf/dwvX7pOhVOhaXUwc0jhzslRnoVCNPcdHJvTXRWl/sdMEcgZN +I074o7hWBhEy1q9hveBA0d+xNUW7akcHSgzj3hU3PqKqtX6X8x3Z/vymlBCX3Bmn +EwydEvMK1GVLct3StgLQDw4iq9vueIHUV43H3U5Qpr1RuO4qrQiFZAD12RL+aAT/ +Gno6pDqOOb1GrxZJQ859hsWXh1cnoI1r6AS/ztnkPAofXISe4XxABNh2dLZXEBSo +yXMvc7LSLsJoT8sGdRtjMirDRuUN6D1lnOqe1kcE0efv22igBRpVlOF9rgAi90Go +b5qSfnTysC/306lmAWlj0Rgl2OBEEl/1fzRV1okBVwQTAQgAQQIbAwUJBaPvfgUL +CQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBE7XePU542NMd5yHxtcGKEihqwBcBQJf +dgz6AhkBAAoJENcGKEihqwBcl28H/RS5pGfdPEsR0UeB9F2t7W08m6e8f3nBdmmx +tEC6qowZkv+LpSxApBpb9E2iig7uv+5yenkwqdCZRoFhtmYdhHGAoez0bLMLsUSA +lLdmaj7U41QntdB27fRhB3VXdbvXmMugi7nKw9gOLjepdetWGA52S9uHvc3PlZmI +sY0FUR7JKIDCbkMHQUMnKj0+zj8giuaYGapzBUI0ZWUbxyem/l5xZ+U6ufbpU7Sk +XWjFd097OXoQzw7OiEdVRBZEZj1QIW4vXQlWBZDwKXAB+Oobz7/muJIVPDN6w1ik +Zf62zHWzTpLz+VDMDKtDNtnGqNV5/089+oonG/x6dfzhdIIs9uGJAVQEEwEIAD4W +IQRO13j1OeNjTHech8bXBihIoasAXAUCX3YM4AIbAwUJBaPvfgULCQgHAgYVCgkI +CwIEFgIDAQIeAQIXgAAKCRDXBihIoasAXIZJB/9QBWKhLUV8El+bE7XplkwgnLWv +4eTvR5dGDLNo5sYXQxS5y6NG0/mSC8biez6tUFfnqY1lp8uKz1E8BZlcbRUBe1bx +CTDCr7psIig9IqKOYbMNpAcrnVG2wv+TZz9Sb9PJxgT2Pqwek9KiMPTJk6V3fWau +GKRsl11ur5tpx0SiJwk21iv1YVHL6Vz6/E4/3XMEq5QpdjLroCBqFBKbKgDHQUYa +6G/h8YU+WkdncVi331WX2ukX88Z47b5nagXN5FHEHMA44qWyqKJbts5LHsI+hbUK +4zT+yNxSd0jmEuGi8kYvzz7IXGBcED7CCY2ZG5wlCVa1O3L9FqHK0EJFkOjItCdC +ZXRoIEdyaWdncyA8QmV0aGFueS5HcmlnZ3NAdWsuaWJtLmNvbT6JAVQEEwEIAD4C +GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQRO13j1OeNjTHech8bXBihIoasA +XAUCYF49cQUJCWfSVQAKCRDXBihIoasAXGIyB/9TCYA8fJYElS0S5OvYjA38l+3k +2D22ckRrRTO1UvWMSltBA4PWNtKZ9j2/uQVDFWzgxio9n8tJs0b+9U7mKKKovubo +lYD4tMzsBaEKESd+tOgwraAPqkwT6jQt49R/NspoQaZ1ubIYvYK00mPerGHX4wIQ +w/9u9douLqTw6Q38J34sxJA2pJ3Rz4OAvPef3MsBd+5PE1OHXgSi9/+MvPzDBGw5 +o+N6qm/AyW06v6c0RrhKX4i8POcycLkMdcLgZXLhQacj40a+PP5AeVogjjwMFzkc +bGJUcMuqJHkf5d8G0SdrVwEOcEfDQmAGpvWtS5JqwZfU9x+J/UdBB70rQ+WeiQFU +BBMBCAA+FiEETtd49TnjY0x3nIfG1wYoSKGrAFwFAlq44CwCGwMFCQPCZwAFCwkI +BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ1wYoSKGrAFz3hgf/RfSAI0EfcPPo7t+3 +LvJ6EENvOY/+UJAF5kTLExKdmwT/nx9got9vi8QJ+rHX6RxzGa0tLlzTUOegDZft +VGQ/aanpOpStIc6TxSPkKqNZrt/ICceDhTl0101dafaIAChY5TIz8KDUsEOmWOO7 +bO6Nk+/IlttM7X6BDC0vHOH09bsn7APQ19fUL0PLIOvjiTkI+knecSnXagVn7Qyg +e5FLgpSbHgz/MLlt9hSX0Zz9nClI4S313bLsKufvyh1QzK9VnQZFnAp70r3Rw00t +5LsHdh8Me8h/VqpDQYWyOGlTDREustxvOlN+cRnlsZsCOX8m1gwTTVAPtgYz7GZd +1p65uokBVAQTAQgAPgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAUJBaPvfhYh +BE7XePU542NMd5yHxtcGKEihqwBcBQJfdgz6AAoJENcGKEihqwBcRxIIAKTG7199 +XR/SHE4zY+FNu9AZZnModHa+1sVdpq1u11cQgjV33xeU4kK0hqQyUv+CcPfPGpFk +ODmIWTsz90PhO6x568Va3SXYtJrXJW6DXIADpxobgYEvxMraxReViuUuMnl+Dl4L +p6qdXQBGLAvyBuZ8Ebq79Os8pMMXccF/KQh1tpNlJzeP6xqWkvLGwFQR05nTtIlp +OdAc13wlfgojWrDI18lIRYObZ8NBdnfCFf2JcaPXmkEzskNbGG86VJr0YNXo7P0H +1+8CqxOhucOE/Fkc2pbbUqXcympkaC2OqB/vvrvOqSKOwQOFaCv7cZZ5YV5z20uq +JavJh6hEwAgZCVCJAVcEEwEIAEECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AF +CQWj734WIQRO13j1OeNjTHech8bXBihIoasAXAUCX3YM4AIZAQAKCRDXBihIoasA +XCr6CADMzDvi2isS7AiwvbDz/y/wtiy0HbOeT/BH1g/ZpArmx2ML1lgxBlD5+uro +NAYLgWhub3ofV5uOadma65y5uHDzKpdsold1K7zJNlewCjVyEYg6v7wikSY7E16O +yQRlDN+pSdoNU7N2aAmCOTLul1jRBhMflyjZWjT8NiWCN9ie9THfFtRNT6sPinx8 +b6k9z8arby9rdOHJysMyGBcjCsL6/TTUqwC/ZSBz9yM3nFt7LvWHNS14bNCwoPx1 +pOYa3cCRfmnd4LtbXvQ60y81f6MgNepIHi5nJ9YeJy8cY9oANtZggK3Nw4Lv1WU/ +/lixbTsDLcB8qe9AWPlH4Mhka9/ZiQFUBBMBCAA+AhsDBQsJCAcCBhUKCQgLAgQW +AgMBAh4BAheAFiEETtd49TnjY0x3nIfG1wYoSKGrAFwFAl57nCoFCQWj734ACgkQ +1wYoSKGrAFxoUQf7BS5G4leywWU0tTeh8sT1dcfjXpF6LT4Xp5Z8dKPlC4ZZ2VWo +2YjRwggwmhXT+01dfV97k5/cge+a1QuI/ItajjhaeeI+Lgw/PfeS1UwaHk/r+5z/ +R1jvGB+/yXtSgI5aQmwxw2T67o/PlLx/qPkyw3iqMdhuT4zrRHGm9c3nmZyGm573 +c2myvOSHmgceTyscIxRB5187Xtm5A+ur+NEBCwF8QwYqsznz4B6mTc0zkBxxfzJa +IsrJNgCT5eD62+JjJCKlg3uGOJ18JvrWEJsigIjlgF8JMvVO/qcYsMoDNPdLxeP7 +bK1NTfzbIeVyPYIoDdhWHXzNT96Q7YWqQssbEokBVAQTAQgAPgIbAwULCQgHAgYV +CgkICwIEFgIDAQIeAQIXgBYhBE7XePU542NMd5yHxtcGKEihqwBcBQJee35/BQkF +o9HTAAoJENcGKEihqwBcYzsH/j1Bodb0le0jwRly3K81UASvCuPzQP4o+7jbMC0s +yfAZIgqTdaKq3H0o6TFg16ByyBQCywXTQ28q5rMUMpzS5rBThdDD/88lmMAwQb0G +O+cpDWUrtOrfzsPX9ifVp/IAlOlOvyRNx4KXFca+LUegkuSU+HFSbihjebTfHbEb +dSPma7CIyDY/zVnwprOmysOcIItx5MW6xc/lY62CrcHk2ivM9ZRt492zY0M6Alqj +kprLes3oH8HswhdLRdDT2IfFO+0zwZflBIn0bepKkXzSzNBeZ0JjY7tDfV4w84vM +VsJ53MG1b71WEALhiTMWj0zzQeSzqLMJVOt79olDtHEWote5AQ0EWrjgLAEIAML0 +gaAcTl01H3mhCZkbL+yRpknAiUNXFe56bSkpxZFP43X3N8i63NF9iLYzcjgicTHv +rF5hZQXrjqMPPotqR1jye34qfp3pZ/3pqrTqcGgRTZ0z3aMr+G+eNSIhd5ZlGgXN +5U4PAddehK7mst9tJbc3+xvC/sb0jc0nut7D40jaKMkoQjb1MGlmZ1NmunuyJ4yM +sI6jbq2Qm72duML1GC12i0FU+GfUdk+8NU+GR6j4lr/QPi+X4O0RPfdpzXm8cQ6w +kCzrKP6a4LQ06qSG26iwue3V+H4WjWCluapsZ+RADUdB5+DCHboPu5jS2iecHVxA +2byPQLuOyW4p4igSkXcAEQEAAYkBPAQYAQgAJgIbDBYhBE7XePU542NMd5yHxtcG +KEihqwBcBQJgXj1xBQkJZ9JVAAoJENcGKEihqwBc/9UIAJRkCDOgNLX+mbpo+29p +joU+KZj5IE1R9XggKjZJeAMOZtSCMt4QNQLYDBP6fnuSVEt0L6t9CCwbQgrtgknv +vq1urynIp8MQvMMRaN++uC+7v/oTO/Sxlq8w08HCX0+SmA+upSECMv+pehaZD8pT +P45AVubs4hVZf1O68/4RMzOI7IDF6sJTl8GSW8fWbOeOa0XR3l46JF9MJzWKozMF +DDimlWvZOxxUwM2eWPVrP0dqN26r/i2EGgyY18AGL/SUBIpzp9sMnh2qDX+2Jat7 +CGGhgXlIlvNNx29Ru5eTE0k4BVuq1ZM+rgQTTQis3x5tTICxog+joxMGVWfC5s2r +MH8= +=H626 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/56730D5401028683275BD23C23EFEFE93C4CFFFE.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/56730D5401028683275BD23C23EFEFE93C4CFFFE.asc new file mode 100644 index 0000000000..a4349eb842 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/56730D5401028683275BD23C23EFEFE93C4CFFFE.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +OweTnxa0BqWTj1O87/SvSKdOd9ZI5GIp +=K6ke +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/61FC681DFB92A079F1685E77973F295594EC4689.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/61FC681DFB92A079F1685E77973F295594EC4689.asc new file mode 100644 index 0000000000..3fb6b4aac6 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/61FC681DFB92A079F1685E77973F295594EC4689.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 ++SMSBsOPDf0= +=i++9 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/71DCFD284A79C3B38668286BC97EC7A07EDE3FC1.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/71DCFD284A79C3B38668286BC97EC7A07EDE3FC1.asc new file mode 100644 index 0000000000..9c9037c07a --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/71DCFD284A79C3B38668286BC97EC7A07EDE3FC1.asc @@ -0,0 +1,145 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +ntI6COSRy17o3GzXm9d4V70= +=h9up +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/74F12602B6F1C4E913FAA37AD3A89613643B6201.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/74F12602B6F1C4E913FAA37AD3A89613643B6201.asc new file mode 100644 index 0000000000..3579b90f3d --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/74F12602B6F1C4E913FAA37AD3A89613643B6201.asc @@ -0,0 +1,129 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +ni5lUGYmUmXIuRub +=EVa1 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/77984A986EBC2AA786BC0F66B01FBB92821C587A.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/77984A986EBC2AA786BC0F66B01FBB92821C587A.asc new file mode 100644 index 0000000000..b899fae67b --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/77984A986EBC2AA786BC0F66B01FBB92821C587A.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +KoZmFSI= +=Fwcs +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/7937DFD2AB06298B2293C3187D33FF9D0246406D.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/7937DFD2AB06298B2293C3187D33FF9D0246406D.asc new file mode 100644 index 0000000000..c269e5a9a4 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/7937DFD2AB06298B2293C3187D33FF9D0246406D.asc @@ -0,0 +1,74 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGiBEPOjg4RBAC2iPU+EHukOrmApMnhYym03gV/VbdPDydVVj+fc7TyjULlKWP7 +iEMFZb58EBjjK4Db3O5JiC9yZ7NRgYvqCFGP7hTVBTykJucXIyT8wCmaAImrtAlg +hA7LKTKlMdkPMz+iTiaVivt5F2W24Lfy/f664J5POcUH3Y5e1dhImNY6fwCg9TN8 +T2QKrFuudsbvqdw+w9I9ZD8EALBfpb39xdZenQllhq6MnODIzeHf5+wQV0slAzaO +RZCAVWodGtNx3NYomvmOuhY3iwFrTiGW7r+3CDqt/4us1vhn/y/A8JijReltOeF0 +nbTM9HzD+3MrlTZMSfR9lzpu941IDkvw67NeFdSHPxLEEcAxI+eoS5dYsdjM3isb +Ovv1A/9jJFYcPg+CRDPYVsY7ok+JW6olXfDzK0pg9zKS2gNbYq2AhyvKKjM/L1Ca +RkmpliBQZEq3QarXrirdwtR9283JKQ7K5UgigkCDqxE2C7jF4XZ6BA41Wh63GPFF +hV1J7R+LI/FZiG0gE0y+bckeSB68+wdh8jnb2JCkNkegbBU70LQvVGltb3RoeSBK +IEZvbnRhaW5lIChPRlRDKSA8dGpmb250YWluZUBvZnRjLm5ldD6IaAQTEQIAKAIb +AwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AFAlMeUUgFCREw9rcACgkQfTP/nQJG +QG1lcgCfdK0g8n4Rwe7ng5CiGsK2aeFu3tMAn2GJN5GDVyVpxIvDMAITPIcbpJFi +iGgEExECACgCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheABQJVFZjCBQkTKD4x +AAoJEH0z/50CRkBt9DUAn3JLT9PifudI9mbufjOvNhTRjNzBAKDnPR1ceqWO3F78 +O02juhMZp82YfYhoBBMRAgAoBQJRPNokAhsDBQkPT38mBgsJCAcDAgYVCAIJCgsE +FgIDAQIeAQIXgAAKCRB9M/+dAkZAbSbgAJ4q2SMC4N9bzJwiU1fdz8evS2z5xgCg +hyS/CRrGGzlBbaQfLsY2h6CVftO0MlRpbW90aHkgSiBGb250YWluZSAoV29yaykg +PHRqLmZvbnRhaW5lQGpveWVudC5jb20+iGgEExECACgCGwMGCwkIBwMCBhUIAgkK +CwQWAgMBAh4BAheABQJTHlFIBQkRMPa3AAoJEH0z/50CRkBtgOgAoKCVOmY7DUec +ppH25/ZNtzhUZUBQAKCv6nNAXhafNJ1KEi9nPLsweUv6CohoBBMRAgAoAhsDBgsJ +CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAUCVRWYwgUJEyg+MQAKCRB9M/+dAkZAbSXB +AJ9T4QxHcQiglAj6unDkpNCfQd8gAwCfd3zAVN6xtlDI119a/Gvb5H2JskeIaAQT +EQIAKAUCUTzaRgIbAwUJD09/JgYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ +fTP/nQJGQG0vcQCfXBAfor5DHZEd0vrUAtHoZXIiRNEAoKs9sryP9dD65Uf8P0eL +W7NpB/Y5tDRUaW1vdGh5IEogRm9udGFpbmUgKFBlcnNvbmFsKSA8dGpmb250YWlu +ZUBnbWFpbC5jb20+iGsEExECACsCGwMFCQ9PfyYGCwkIBwMCBhUIAgkKCwQWAgMB +Ah4BAheABQJRPNqyAhkBAAoJEH0z/50CRkBtqIwAn2HZ88QHvZFReoqNo/saO4ax +zqxyAJ94cWr+bwMO8wbx2JHTtGxRKx+s+ohrBBMRAgArAhsDBgsJCAcDAgYVCAIJ +CgsEFgIDAQIeAQIXgAIZAQUCUx5RRQUJETD2twAKCRB9M/+dAkZAbe1gAKC/YnU4 +K3YvGN3zDZZqA45F6dGUywCcCzQs5rgW4ivr4kVTWb0JJXh6BNCIawQTEQIAKwIb +AwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4ACGQEFAlUVmL8FCRMoPjEACgkQfTP/ +nQJGQG1yCgCgrxLj45QV/4cZ8rarKJjetNdBj8gAoMxW6LSPod4qwTE2jw2Qf4wP +lXC7tEBUaW1vdGh5IEogRm9udGFpbmUgKFBlcnNvbmFsIEtleSkgPHRqZm9udGFp +bmVAYXR4Y29uc3VsdGluZy5jb20+iGQEExECACQCGwMGCwkIBwMCAxUCAwMWAgEC +HgECF4AFAk1yezwFCQ1mVBUACgkQfTP/nQJGQG2ITACg3Zd9owEpcatNeuPrWpmL +M3yORKIAn2kmpLHReRpKQ+buF/I2SUq5uLXbiGQEExECACQCGwMGCwkIBwMCAxUC +AwMWAgECHgECF4AFAlE82bQFCQ9PfyYACgkQfTP/nQJGQG0qEQCfbXfhJFcYc/7k +lu56ErfoVejcFdEAoOdUqZR1xrvo2KlJ5VjLLfYQSrjAiGQEExECACQCGwMGCwkI +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 +Uhw2iju+XrCVJ/AXUtwH +=AG5V +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4.asc new file mode 100644 index 0000000000..acbebcb64a --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4.asc @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +H9y08K6zB4JFN7saJiCj2Q7W2nXuEUhRVs2K +=L7AX +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600.asc new file mode 100644 index 0000000000..434e770e80 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +HQH6ZS1ibGU9KRlmgqU= +=bwC0 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/93C7E9E91B49E432C2F75674B0A78B0A6C481CF6.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/93C7E9E91B49E432C2F75674B0A78B0A6C481CF6.asc new file mode 100644 index 0000000000..089b9d6202 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/93C7E9E91B49E432C2F75674B0A78B0A6C481CF6.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBEx8nbMBCACjmblSAGggunFHAWRGZLWLKltA2PG6rIM0bOokJFWtGwRqBCAa +dKuNwBGy7eBwFQAxuHnNwDKqyGgCKUBe4RUuz0gKr1WgkPAzHZppo/n5BhE4iIxr +lWJBKVKk/gjXVUBPhRPWNTa23JbzJHntxzHqeMQWIwIdBXoyOf0tOuXcfH2oVBdq +p8nFaeJQ906F6olUD8U12LgIhkg6/NTDLymwkau4YcOe8c2repNITVFaP6w5FmSj +aWTnyFXpdnVF4fWpbrLXsPBG72UMsAyWW4XuMtO07t4MvzgKBQALmGqlGMVtVkCI +jBgYD250NqBGKPmBrfJQVYbEesOZqNAF7bdNABEBAAG0J2lzYWFjcyAoaHR0cDov +L2Jsb2cuaXpzLm1lLykgPGlAaXpzLm1lPokBOAQTAQIAIgUCTHydswIbAwYLCQgH +AwIGFQgCCQoLBBYCAwECHgECF4AACgkQsKeLCmxIHPYyyAf8CUkbVGxWid46g9jq +hYJd4o2Au0l6Y5prhraugbMw72PUstn6VAqx77Voe4NS5J/8942HHWjtlMIn42gU +B5DS6gnGDFpo/64d3AZZ66meOE7OHcD2qo/kvziIPq77paOu4ZLF6XcY1xycCCkq +L+8zzH409o6uR/LN4APeUcCmnqugibA1vOmQcvU1Ks+X0hKEHOkjDjws8F3yUokG +ahUrtvYqJ7qNx5RQvKzvcNbhM7omZ2KXH57YyhyvSdUvfjzaFv7D2CABDlVk4xxT +GAZjmAKrKI6mvJLU36Ip9XJTH4J7iAqOe2phUSmOdZOfRrF6qFkvmNNNkH4MbDu/ +p9IPOLkBDQRMfJ2zAQgArSXiNKQXWZizpdNy/C5fWIqmczpW8MwnVY99PfB6+gnA +RxZ1GX4J1APd1rniOEOEEgWiB+QsihuMLHYuGHBhQo1zvrrmvUAZpNwz4NovcCAq +3AzMMk+VQYDYWqqkUFnct1dLblf6SuuZ/beWUk+Rju1mRQoi9heMMwjvNlqPBc0v +N8y3I8WhxCtQpWIMvQs7LN3v0s8AWNCV9VsQxmgtYpx5kMe3aYMJWEa+RzqhNvgP +FwfOUaBXQ07dBqLOhTjMomEVeBoCkBU8mrIrgqoOkucKZE5/Hlqb8d1y0AiO02bs +zCHPc5Wx5aCpq+EYWhskM8Zg3CpXhHQx3rE8oMnX7wARAQABiQEfBBgBAgAJBQJM +fJ2zAhsMAAoJELCniwpsSBz2bckH/0+0te0aioSQWEb39JYZCcBvXvvnpL/pHgPa +Z6tOizCf8/3tSRbwxI2CQ19p8ji95v+A0c1G4LwiTfZY3lEkIk0ZL1M1Ky82x/lc +6hhsIVQ3JdrCSuOUzMhgQQk+I+xPr8gx4+1GL+Ia9F44o0CP0WgPeLshmK03hmjy +ZqC+u0j/R8OQqS/kVe7qt41nmNZzwsROuKuJt1xFD2GMquL+Z4oCu4YLKM13WPkd ++ntZI9Gu0N7YDY4tKQCtcjs7z7AwVpfCesnuJtMtFb1+xuyt5DErMVN83Mi1VrUu +pYtVXNkSmiSUWMmh7QfVNFrWDqlP8vUi3BIEV3FtjXblPkbKbow= +=o1Jn +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/94AE36675C464D64BAFA68DD7434390BDBE9B9C5.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/94AE36675C464D64BAFA68DD7434390BDBE9B9C5.asc new file mode 100644 index 0000000000..806041d9f2 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/94AE36675C464D64BAFA68DD7434390BDBE9B9C5.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +mMJZL+1dR71q +=sm/t +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/9554F04D7259F04124DE6B476D5A82AC7E37093B.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/9554F04D7259F04124DE6B476D5A82AC7E37093B.asc new file mode 100644 index 0000000000..928a08f7ca --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/9554F04D7259F04124DE6B476D5A82AC7E37093B.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFTROWUBCACslqx8p2znj3CwEqWK+bEgyfxykVC1iFEABB6UCQ5UrlAiYTjI +0vwFaTlbWkD3dWBxiFN2+n24Lro549ATmXGO+i0Sacr7DTgawZdkJuM17nNmlAW1 +c1lo6D/KfQ1K7QrakloQw10LzLLW3uxi3OXVoefY1JKTeQn43jolrCHho9iNvZ0N +xyihQYzSiRzqMQGltKw5UDdbhwuzr9jROgHuUH+msOzax9IkLb8mm2VqJ3HaDJWk +oAaU92leGoicEBhR8qC3zGzua7pRFK8qhjaSSL/N/NpCu4ud6axHaBomfn5HJQNT +dTHB6R6wsVgMED9vfZHqJeyltDvA8FDw4uQnABEBAAG0OUNocmlzdG9waGVyIERp +Y2tpbnNvbiA8Y2hyaXN0b3BoZXIucy5kaWNraW5zb25AZ21haWwuY29tPokBOAQT +AQIAIgUCVNE5ZQIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQbVqCrH43 +CTvBaAf9HEU1MhfIF8qlGno0WQojtEW8hLskw0Dd4OeG3cLsSmCa72zEjEVHw82X +g862h3vfEybf7nd+mxdoSDT3iLwD8NOW6pnzW4/RjRGwvKKUhYggu8oi3xBSNj+h +GRg2dkRWpCtqJo1q0XUdh34iqJUpk2isYvsQuHJrbcglPLO+tuFFNZ0JTf1f895s +OnP1f+N1ygOnhehRoxO8gjncU3M8TXSsX8LQuCYfrBXayT5kZ49j5/+HOS8dHxYE +R9e4LpPIWpfWQtXgXZKxcpyu0x25ZO6jkQLhleXtRpCfDO3DJw7l80suGf7Vjt0P +XiYk5K2Z6x7h9rhWkEN92kTmRSbErrkBDQRU0TllAQgA1g9MDLyGI3/W+oXwrl2r +2DKvJFRALeUHyedU+oaGo47XBKvNk4UdIDeHuUvf5DzYJW6t8GH1G55ZdYGik9/M +16/6b1eYA2T7P6kjOmCGtv3KMkCLbymlFxb5SWEWwVR16L+5eXfVKbEBT7n2MGwT +c42DCJzg7APZasaJuRQDoPgQFVQ1WX+0eXimAgdYlbtVjZmrkaIZbzGIcET1lJdB +xOj+bR0pXIXbOvo5FlCVMmGAefrDePhqfuFMAQup8d4Gd7V19AcV8FyNUrVkji/a +iupi4FcBKLuF5yybJLY5d/Ij0pZ7MGUcbUhe9CbOZv9fvZBnL1Ukl9bxfchzrM2h +VwARAQABiQEfBBgBAgAJBQJU0TllAhsMAAoJEG1agqx+Nwk7vzAH/iTv3qDimLGA +9YdgSJ0h1aWZBgU2okJLLEeZQQuNHzM+ekQDhBA6Bdb938VtNUAdCSABBKUlFeq7 ++cqpcH1YgHyBv6K6avTI0F2Y1+nYIqZoe4n1QlcWg858fg4zzjBPU+s2eVIl9rOZ +kMioU0UgM53DOhz/KjCHIWeLCpZAdfXsK2+lzWLINoYJ767wTNkXWPcxj2mGtht3 +hCOcTHK5toVhlytM7vGVacn0xwOhNSNMBwFq6rucIi/fgLheNDFgLPykE48XmLau +fb3Fyii+q1BCWWZsyZjkLW7kcpf62i/YlrJHjWRheWLblLtxqy4ZiKqqLCtyfh9a +xr5u2jsokh8= +=2Ugv +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/A363A499291CBBC940DD62E41F10027AF002F8B0.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/A363A499291CBBC940DD62E41F10027AF002F8B0.asc new file mode 100644 index 0000000000..f082b653d4 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/A363A499291CBBC940DD62E41F10027AF002F8B0.asc @@ -0,0 +1,89 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +wUp1P1BBxrBTTg== +=D9cV +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/A48C2BEE680E841632CD4E44F07496B3EB3C1762.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/A48C2BEE680E841632CD4E44F07496B3EB3C1762.asc new file mode 100644 index 0000000000..2c711558fc --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/A48C2BEE680E841632CD4E44F07496B3EB3C1762.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +fyCIa9sx6lcYB+gDJgESL0Nt +=hSfG +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/B9AE9905FFD7803F25714661B63B535A4C206CA9.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/B9AE9905FFD7803F25714661B63B535A4C206CA9.asc new file mode 100644 index 0000000000..c2b6d06343 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/B9AE9905FFD7803F25714661B63B535A4C206CA9.asc @@ -0,0 +1,75 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=CMP0 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/B9E2F5981AA6E0CD28160D9FF13993A75599653C.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/B9E2F5981AA6E0CD28160D9FF13993A75599653C.asc new file mode 100644 index 0000000000..8ece24d18f --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/B9E2F5981AA6E0CD28160D9FF13993A75599653C.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +1j7Q6fERAXlFlv7ciEuKBz5A3SK7Xt7Eb7vQ +=kARQ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C0D6248439F1D5604AAFFB4021D900FFDB233756.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C0D6248439F1D5604AAFFB4021D900FFDB233756.asc new file mode 100644 index 0000000000..67211b7b86 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C0D6248439F1D5604AAFFB4021D900FFDB233756.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +Th80Z8v5rFX5xNw+zn0Ee+yr +=SZFY +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8.asc new file mode 100644 index 0000000000..2baa8ea5ef --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8.asc @@ -0,0 +1,228 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +ftyH6K7HWwkSmrXf0//7kPEF7+P0LvSX4Ri/X2zfa+GgkfmIK66n8Vl/+YXSZxr8 +3Yf06k16Nl0loYZqYYkhQvdxKIyrXKx4zLiIU3QOKWFUy5EvAFwg++i5s8YvSvxb +72p5tlhZ7C2I5/zzZG6+VPFEa4kCOQQTAQgAIwUCWJ4DrwIbAwcLCQgHAwIBBhUI +AgkKCwQWAgMBAh4BAheAAAoJEOc7xkHMEfTI3RUQAKagnz10ZI0WUXYbQlZ09eUG +zDzxA8gpsMAoEb31+vo3bwFJ8hlErSkWe/WsHoaF7y0BiqWlUYibdpKrdmNnivbs +j5TTb5MEndP/kKYsfKQZtbNZwP5ITU3lKVEPHCBZbDYQWKPdQBSq/6qvQ1D1/lSr +6VEhML0p/XbFy9st22jMqTWiSEu6xHyOf7t6+niLcLuBo/edR/+jOEF8qizGYZaU +R7eFtRkWD5I8y8sPWtjjiRmbnV/1YcJNfZKDNu4ReVzXh9iJBt3iC8HY1nUFo2ic +2bZ31odwpGLrfebHtL63jyu7BW1GrlQK7NwrQbgFg2ePpNCv62Pzz+LUkXl4UH2Q +KFRBhJ+MMmq9Tx4IIAUrcOx/wc292S8+VOoH404Scp8y13dsW4v1YmvbO7zobomF +eTA1TZ371q7OqumxtCl2B8gnAFxzBPH7W6O3YHSs2zEpwtd3r20VS/WZ2owl8+HL +1hGg1AY+LCVBsX4BiBF/4e4qr/6BghvScximLbIThS9jLJCJs0jphhPKudS7dMAQ +veGSlWJUJ1stLqoWkmQuo0ndArLtnHQF+pFRtNcaIqmn2ZOQM+GD91PxIlMmR/hw +dKzVf6b5ncwvfS2di0ySl8v50SgBvZTbLm/C2Qh5VcB9NolzVHqICtoVO/fD4I3P +gS52Asdx6Deeq188yoXUiQJQBBMBCAA6AhsDBwsJCAcDAgEGFQgCCQoLBBYCAwEC +HgECF4AWIQTE8N//TowagjZAnQjnO8ZBzBH0yAUCW/Q5HwAKCRDnO8ZBzBH0yPtu +EACUVtMYUeB8NTZC5e4ceP8D0fciwxvcXKngRSm/BItY/Hi4gWn01Us5cG/7hh6y +nW16f2DLWMTQL8EIcUZ7kiKvBCJc/v6X/4XCPGJthGrsiFzPiaFUv2qzhSooQkGB +72Qi1WhdvLn1ocSkUDWbFeEJCnGxH5bVBSntDu0nVU8DwVTB/NQ8V2HZDl4e/mWo +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 +4G5Uhcw= +=YIVB +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C.asc new file mode 100644 index 0000000000..5a6eb622b2 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C.asc @@ -0,0 +1,76 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=pqm4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/CC68F5A3106FF448322E48ED27F5E38D5B0A215F.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/CC68F5A3106FF448322E48ED27F5E38D5B0A215F.asc new file mode 100644 index 0000000000..0ea9c21cf2 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/CC68F5A3106FF448322E48ED27F5E38D5B0A215F.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +ZWf0a3VP6Gco5bmDPhvGoLEs9Vw5 +=57kS +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7.asc new file mode 100644 index 0000000000..1e3f56f74b --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7.asc @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZAFttBYJKwYBBAHaRw8BAQdA9UUQNclFp0rIrgtQnNw6BgjDINkFPoVbuS4H +sQNEf+e0LEp1YW4gSm9zw6kgQXJib2xlZGEgPHNveWp1YW5hcmJvbEBnbWFpbC5j +b20+iJMEExYKADsWIQTdeS9Zc8beUsQyy9rHer+gDdvytwUCZAFttAIbAwULCQgH +AgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRDHer+gDdvyt3PYAP9A8XbvZYT+N7k9 +2xnRMfrAUep6TLfWbx9uf7k/hm/+KAEAmqUYV1afcuwU2xXE5I8m25VMnCEKFFEF +52tW2baWkQW4OARkAW20EgorBgEEAZdVAQUBAQdAtwscgbxby3vew2hn9F+KlVPL +vFBPjnvODcnsqlO2mXQDAQgHiHgEGBYKACAWIQTdeS9Zc8beUsQyy9rHer+gDdvy +twUCZAFttAIbDAAKCRDHer+gDdvytzxaAQDvYX4o1Y6R30bYwIXemGgbO8GlCkgk +5it7MjeSk8vUygEApE5zIi5OtP8TlPiMgu2MoJbIltqqDCKWTtHPIQRNIwk= +=IisY +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/DD8F2338BAE7501E3DD5AC78C273792F7D83545D.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/DD8F2338BAE7501E3DD5AC78C273792F7D83545D.asc new file mode 100644 index 0000000000..50e3ec9657 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/DD8F2338BAE7501E3DD5AC78C273792F7D83545D.asc @@ -0,0 +1,36 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +8FLoLzom0A1FNxCvgzOraMITOobs +=dTMc +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/FD3A5288F042B6850C66B31F09FE44734EB7990E.asc b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/FD3A5288F042B6850C66B31F09FE44734EB7990E.asc new file mode 100644 index 0000000000..c8d0bd579a --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/FD3A5288F042B6850C66B31F09FE44734EB7990E.asc @@ -0,0 +1,111 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=NfOh +-----END PGP PUBLIC KEY BLOCK----- diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/README.md b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/README.md new file mode 100644 index 0000000000..0b106ce3a6 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/README.md @@ -0,0 +1,8 @@ +This directory contains the Node.js release signing certificates. + +At the time of writing, these were downloaded from +https://github.com/nodejs/release-keys/tree/main/keys +on Wed Nov 20, 2024 at commit 604322f38d5cc74bc7552af75f1d3b4d6e8825ac. + +These keys are used to verify the authenticity of the Node.js binary +distributions downloaded by the plugin. diff --git a/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/keys.list b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/keys.list new file mode 100644 index 0000000000..acaed558a5 --- /dev/null +++ b/dataconnect/buildSrc/src/main/resources/com/google/firebase/example/dataconnect/gradle/nodejs_release_signing_keys/keys.list @@ -0,0 +1,27 @@ +4ED778F539E3634C779C87C6D7062848A1AB005C +94AE36675C464D64BAFA68DD7434390BDBE9B9C5 +1C050899334244A8AF75E53792EF661D867B9DFA +B9AE9905FFD7803F25714661B63B535A4C206CA9 +77984A986EBC2AA786BC0F66B01FBB92821C587A +71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 +61FC681DFB92A079F1685E77973F295594EC4689 +FD3A5288F042B6850C66B31F09FE44734EB7990E +8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 +C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 +890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 +C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C +DD8F2338BAE7501E3DD5AC78C273792F7D83545D +A48C2BEE680E841632CD4E44F07496B3EB3C1762 +B9E2F5981AA6E0CD28160D9FF13993A75599653C +108F52B48DB57BB0CC439B2997B01419BD92F80A +9554F04D7259F04124DE6B476D5A82AC7E37093B +93C7E9E91B49E432C2F75674B0A78B0A6C481CF6 +56730D5401028683275BD23C23EFEFE93C4CFFFE +114F43EE0176B71C7BC219DD50A3051F888C628D +7937DFD2AB06298B2293C3187D33FF9D0246406D +74F12602B6F1C4E913FAA37AD3A89613643B6201 +141F07595B7B3FFE74309A937405533BE57C7D57 +DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 +A363A499291CBBC940DD62E41F10027AF002F8B0 +CC68F5A3106FF448322E48ED27F5E38D5B0A215F +C0D6248439F1D5604AAFFB4021D900FFDB233756 diff --git a/dataconnect/dataconnect/.gitignore b/dataconnect/dataconnect/.gitignore new file mode 100644 index 0000000000..ecb842d705 --- /dev/null +++ b/dataconnect/dataconnect/.gitignore @@ -0,0 +1 @@ +.generated/ diff --git a/dataconnect/dataconnect/movie-connector/connector.yaml b/dataconnect/dataconnect/movie-connector/connector.yaml index 2ba51749dc..61163cf957 100644 --- a/dataconnect/dataconnect/movie-connector/connector.yaml +++ b/dataconnect/dataconnect/movie-connector/connector.yaml @@ -8,6 +8,8 @@ generate: kotlinSdk: # Create a custom package name for your generated SDK package: com.google.firebase.dataconnect.movies - # Specify where to store the generated SDK - # We're using the build/ directory so that generated code doesn't get checked into git - outputDir: ../../app/build/generated/sources/com/google/firebase/dataconnect/movies + # Use an arbitrary directory for generating the code, as the + # "com.google.firebase.example.dataconnect.gradle" custom Gradle plugin applied by + # app/build.gradle.kts will look after generating the code on-demand into the directory + # prescribed by Gradle (e.g. app/build/generated/java/generateDebugDataConnectSources). + outputDir: ../.generated diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5523009483..4ca43cc15f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,7 @@ googleServices = "4.4.2" composeNavigation = "2.8.4" [libraries] +android-gradlePlugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "agp" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-lifecycle-runtime-compose-android = { module = "androidx.lifecycle:lifecycle-runtime-compose-android", version.ref = "lifecycle" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" } @@ -37,6 +38,7 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit androidx-material3 = { group = "androidx.compose.material3", name = "material3" } compose-navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "composeNavigation"} kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerializationCore" } +snakeyaml = { module = "org.yaml:snakeyaml", version = "2.3" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/scripts/ci_remove_fdc.py b/scripts/ci_remove_fdc.py deleted file mode 100644 index c425af615b..0000000000 --- a/scripts/ci_remove_fdc.py +++ /dev/null @@ -1,8 +0,0 @@ -# TODO(thatfiredev): remove this once github.com/firebase/quickstart-android/issues/1672 is fixed -with open('settings.gradle.kts', 'r') as file: - filedata = file.read() - -filedata = filedata.replace('":dataconnect:app",', '') - -with open('settings.gradle.kts', 'w') as file: - file.write(filedata) diff --git a/settings.gradle.kts b/settings.gradle.kts index af004974bc..6962a01794 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,7 @@ pluginManagement { + includeBuild("dataconnect/buildSrc") { + name = "dataConnectBuildSrc" + } repositories { google() mavenCentral()