/* * Copyright 2026 Signal Messenger, LLC * SPDX-License-Identifier: AGPL-3.0-only */ import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import org.gradle.process.ExecOperations import org.gradle.work.DisableCachingByDefault import java.nio.charset.StandardCharsets import javax.inject.Inject /** * Rebuilds dependency verification components from the dependencies resolved by the build. * * Gradle only adds entries when --write-verification-metadata is used, so starting with an empty * components section is what removes entries for dependencies that are no longer used. The * configuration section is retained, including its trusted-artifact rules. */ @DisableCachingByDefault(because = "This task updates Gradle's dependency verification metadata") abstract class UpdateVerificationMetadataTask @Inject constructor( private val execOperations: ExecOperations ) : DefaultTask() { @get:Internal abstract val metadataFile: RegularFileProperty @get:Internal abstract val rootDirectory: DirectoryProperty @get:Internal abstract val wrapperCommand: ListProperty @TaskAction fun update() { val file = metadataFile.get().asFile val original = file.readBytes() try { val metadata = original.toString(StandardCharsets.UTF_8) val components = Regex("(?s)().*?()") check(components.findAll(metadata).count() == 1) { "Expected exactly one section in ${file.absolutePath}" } file.writeText( components.replace(metadata) { match -> "${match.groupValues[1]}\n ${match.groupValues[2]}" }, StandardCharsets.UTF_8 ) // The qa build resolves everything a clean qa run needs, including tooling that is only pulled while a // task executes -- the KSP compiler, the aapt2 executable, and the layoutlib screenshot renderer all come // in through detached configurations. Finally the cross-platform sync fills in aapt2's other OS variants. runGradle("--write-verification-metadata", "sha256", "qa") // The build-logic precompiled-script-plugin accessors are generated by applying each plugin in throwaway // nested builds, which resolve plugin dependencies (e.g. junit BOMs) that no configuration walk or qa build // ever sees. Forcing a refresh re-downloads them through the verifier so their checksums get written. runGradle("--write-verification-metadata", "sha256", ":build-logic:plugins:generatePrecompiledScriptPluginAccessors", "--rerun-tasks", "--no-build-cache", "--refresh-dependencies") // The IDE resolves the bundled Groovy module graph (via localGroovy) during sync's model-building phase, which // no task graph reaches, so resolve it explicitly to capture those checksums. runGradle("--write-verification-metadata", "sha256", ":build-logic:plugins:syncGroovyVerification", "--rerun-tasks", "--refresh-dependencies") runGradle("--write-verification-metadata", "sha256", "syncCrossPlatformVerification", "--rerun-tasks") } catch (failure: Throwable) { file.writeBytes(original) throw failure } } private fun runGradle(vararg arguments: String) { execOperations.exec { workingDir(rootDirectory.get().asFile) commandLine(wrapperCommand.get() + arguments) } } }