Git Product home page Git Product logo

dependency-guard's Introduction

๐Ÿ›ก๏ธ Dependency Guard

LICENSE Latest Stable Latest Snapshot CI

A Gradle plugin that helps you guard against unintentional dependency changes.

Surface Transitive Dependency Changes

Comparison to a baseline occurs whenever you run the dependencyGuard Gradle task.

A small single version bump of androidx.activity from 1.3.1 -> 1.4.0 causes many libraries to transitively update.

Provide Custom Rules for Allowed Dependencies

For a given configuration, you may never want junit to be shipped. You can prevent this by modifying the allowedFilter rule to return !it.contains("junit") for your situation.

Why Was Dependency Guard Built?

As platform engineers, we do a lot of library upgrades, and needed insight into how dependencies were changing over time. Any small change can have a large impact. On large teams, it's not possible to track all dependency changes in a practical way, so we needed tooling to help. This is a tool that surfaces these changes to any engineer making dependency changes, and allows them to re-baseline if it's intentional. This provides us with historical reference on when dependencies (including transitive) are changed for a given configuration.

Goals:

  • Surface transitive and non-transitive dependencies changes for our production build configurations. This helps during version bumps and when new libraries are added.
  • Provide the developer with the ability to easily accept changes as needed.
  • Add deny-listing for production build configurations to explicitly block some dependencies from production.
  • Include a record of the change in Git when their code is merged to easily diagnose/debug crashes in the future.
  • Be deterministic. De-duplicate entries, and order alphabetically.

Real World Issues Which Dependency Guard Addresses

  • Accidentally shipping a testing dependency to production because implementation was used instead of testImplementation - @handstandsam
    • Dependency Guard has List and Tree baseline formats that can compared against to identify changes. If changes occur, and they are expected, you can re-baseline.
  • Dependency versions were transitively upgraded which worked fine typically, but caused a runtime crash later that was hard to figure out why it happened.
    • You would see the diff off dependencies in Git since the last working version and be able to have quickly track down the issue.
  • After adding Ktor 2.0.0, it transitively upgraded to Kotlin 1.6.20 which caused incompatibilities with Jetpack Compose 1.1.1 which requires Kotlin 1.6.10 - @joreilly
    • During large upgrades it is important to see how a single version bump will impact the rest of your application.
  • Upgrading to the Android Gradle Plugin transitively downgraded protobuf plugin which caused issues - @AutonomousApps

Setup and Configuration

Step 1: Adding The Dependency Guard Gradle Plugin and Baselining

// sample/app/build.gradle.kts
plugins {
  id("com.dropbox.dependency-guard") version "0.5.0"
}

Step 2: Run ./gradle dependencyGuard and Configure

This will show a list of available configuration(s) for this Gradle module.

You can choose the configurations you want to monitor.

We suggest monitoring your release configuration to know what is included in production builds. You can choose to monitor any classpath configuration with Dependency Guard.

// sample/app/build.gradle.kts
dependencyGuard {
    // All dependencies included in Production Release APK
    configuration("releaseRuntimeClasspath") 
}

NOTE: Checkout the "Adding to the Buildscript Classpath" section below if the plugin can't be resolved.

Step 3: Run ./gradlew dependencyGuard to Detect Changes

It's suggested to run in your CI or pre-commit hooks to detect these changes. If this task is not run, then you aren't getting the full benefit of this plugin.

Bump Version and Detect Change

Step 4: Rebaselining to Accept Changes

If any dependencies have changed, you will be provided with the ./gradlew dependencyGuardBaseline task to update the baseline and intentionally accept these new changes.

Rebaseline to Accept Change

Additional Configuration Options

Allow Rules for Dependencies (Optional)

If you have explicit test or debugging dependencies you never want to ship, you can create rules for them here.

dependencyGuard {
    configuration("releaseRuntimeClasspath") {
        allowedFilter = {
            // Disallow dependencies with a name containing "junit"
            !it.contains("junit")
        }
    }
}

Configuring Your Dependency Baseline (Optional)

By default, Dependency Guard tracks modules and artifacts in a list format is generated at dependencies/${configurationName}.txt.

org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10
org.jetbrains.kotlin:kotlin-stdlib:1.6.10
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2
org.jetbrains:annotations:13.0

You can choose to not track modules or artifacts:

dependencyGuard {
  configuration("releaseRuntimeClasspath") {
    // What is included in the list report
    artifacts = true // Defaults to true
    modules = false // Defaults to false
  }
}

Tree Format (Optional)

The builtin Dependencies task from Gradle is :dependencies. The tree format support for Dependency Guard leverages this under the hood and targets it for a specific configuration.

The dependency-tree-diff library in extremely helpful tool, but is impractical for many projects in Continuous Integration since it has a lot of custom setup to enable it. (related discussion). dependency-tree-diff's diffing logic is actually used by Dependency Guard to highlight changes in trees.

The tree format proved to be very helpful, but as we looked at it from a daily usage standpoint, we found that the tree format created more noise than signal. It's super helpful to use for development, but we wouldn't recommend storing the tree format in Git, especially in large projects as it gets noisy, and it becomes ignored. In brainstorming with @joshfein, it seemed like a simple ordered, de-duplicated list of dependencies were better for storing in CI.

dependencies/releaseRuntimeClasspath.tree.txt


------------------------------------------------------------
Project ':sample:app'
------------------------------------------------------------

releaseRuntimeClasspath - Resolved configuration for runtime for variant: release
\--- project :sample:module1
     +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10
     |    +--- org.jetbrains.kotlin:kotlin-stdlib:1.6.10
     |    |    +--- org.jetbrains:annotations:13.0
     |    |    \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10
     |    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10
     |         \--- org.jetbrains.kotlin:kotlin-stdlib:1.6.10 (*)
     \--- project :sample:module2
          +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10 (*)
          \--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2
               \--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2
                    +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30 -> 1.6.10 (*)
                    \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.5.30 -> 1.6.10

(*) - dependencies omitted (listed previously)

A web-based, searchable dependency report is available by adding the --scan option.

Enabling Tree Format

Enable the tree format for a configuration with the following option:

dependencyGuard {
  configuration("releaseRuntimeClasspath") {
    tree = true // Enable Tree Format
  }
}

Creating a Tree Format Baseline

Detecting Changes in Tree Format

Rebaselining Tree Format

Additional Information

Which Configurations Should be Guarded?

  • Production App Configurations (what you ship)
  • Production Build Configurations (what you use to build your app)

Changes to either one of these can change your resulting application.

How Does Dependency Guard Work?

Dependency Guard writes a baseline file containing all your transitive dependencies for a given configuration that should be checked into git.

Under the hood, we're leveraging the same logic that the Gradle dependencies task uses and displays in build scans, but creating a process which can guard against changes.

A baseline file is created for each build configuration you want to guard.

If the dependencies do change, you'll get easy to read warnings to help you visualize the differences, and accept them by re-baselining. This new baseline will be visible in your Git history to help understand when dependencies changed (including transitive).

Full Configuration Options

dependencyGuard {
  configuration("releaseRuntimeClasspath") {
    // What is included in the list report
    artifacts = true // Defaults to true
    modules = false // Defaults to false

    // Tree Report
    tree = false // Defaults to false

    // Filter through dependencies and return true if allowed.  Build will fail if unallowed.
    allowedFilter = {dependencyName: String ->
        return true // Defaults to true
    }
    // Modify a dependency name or remove it (by returning null) from the baseline file
    baselineMap = { dependencyName: String ->
      return dependencyName // Defaults to return itself
    }
  }
}

Suggested Workflows

The Dependency Guard plugin adds a few tasks for you to use in your Gradle Builds. Your continuous integration environment would run the dependencyGuard task to ensure things did not change, and require developers to re-baseline using the dependencyGuardBaseline tasks when changes were intentional.

Gradle Tasks added by Dependency Guard

dependencyGuard

Compare against the configured transitive dependency report baselines, or generate them if they don't exist.

This task is added to any project that you apply the plugin to. The plugin should only be applied to project you are interested in tracking transitive dependencies for.

dependencyGuardBaseline

This task overwrites the dependencies in the dependencies directory in your module.

Baseline Files

Baseline files are created in the "dependencies" folder in your module. The following reports are created for the :sample:app module by running ./gradlew :sample:app:dependencyGuardBaseline

Adding to the Buildscript Classpath

Snapshot versions are under development and change, but can be used by adding in the snapshot repository

// Root build.gradle
buildscript {
    repositories {
        mavenCentral()
        google()
        gradlePluginPortal()
        // SNAPSHOT Versions of Dependency Guard
        maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" }
    }
    dependencies {
        classpath("com.dropbox.dependency-guard:dependency-guard:0.5.0")
    }
}

License

Copyright (c) 2022 Dropbox, Inc.

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.

dependency-guard's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dependency-guard's Issues

Crash while using in root project

@ZacSweers reported: Ran into this crash while testing this out in our root project

Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class 'org.gradle.language.base.plugins.LifecycleBasePlugin'.
        at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:173)
        at org.gradle.api.internal.plugins.DefaultPluginManager.addImperativePlugin(DefaultPluginManager.java:89)
        at org.gradle.api.internal.plugins.DefaultPluginManager.addImperativePlugin(DefaultPluginManager.java:96)
        at org.gradle.api.internal.plugins.DefaultPluginContainer.apply(DefaultPluginContainer.java:77)
        at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin.attachToCheckTask(DependencyGuardPlugin.kt:50)
        at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin.apply(DependencyGuardPlugin.kt:42)
        at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin.apply(DependencyGuardPlugin.kt:16)
        at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
        at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
        at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:187)
        at org.gradle.api.internal.plugins.DefaultPluginManager.access$100(DefaultPluginManager.java:52)
        at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:282)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
        at org.gradle.api.internal.plugins.DefaultPluginManager.lambda$doApply$0(DefaultPluginManager.java:167)
        at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
        at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:166)
        at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:146)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.lambda$applyLegacyPlugin$2(DefaultPluginRequestApplicator.java:160)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:201)
        ... 179 more
Caused by: org.gradle.api.internal.tasks.DefaultTaskContainer$DuplicateTaskException: Cannot add task 'clean' as a task with that name already exists.
        at org.gradle.api.internal.tasks.DefaultTaskContainer.failOnDuplicateTask(DefaultTaskContainer.java:257)
        at org.gradle.api.internal.tasks.DefaultTaskContainer.registerTask(DefaultTaskContainer.java:398)
        at org.gradle.api.internal.tasks.DefaultTaskContainer.register(DefaultTaskContainer.java:375)
        at org.gradle.language.base.plugins.LifecycleBasePlugin.addClean(LifecycleBasePlugin.java:58)
        at org.gradle.language.base.plugins.LifecycleBasePlugin.apply(LifecycleBasePlugin.java:44)
        at org.gradle.language.base.plugins.LifecycleBasePlugin.apply(LifecycleBasePlugin.java:33)
        at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
        at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
        at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:187)
        at org.gradle.api.internal.plugins.DefaultPluginManager.access$100(DefaultPluginManager.java:52)
        at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:282)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
        at org.gradle.api.internal.plugins.DefaultPluginManager.lambda$doApply$0(DefaultPluginManager.java:167)
        at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
        at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:166)
        ... 204 more

See: #36

Feature request: Support saving and checking hash or signature of dependency in report

dependency-guard does an awesome job of protecting projects from unwanted dependency changes.

But this way we know nothing about the actual dependency content and cannot protect against malicious supply chain attacks like the ones mentioned here:

Of course, we can use Gradle's support for package signatures. But it's hard to use properly and not supported by many Gradle plugins and library providers (including many Google and JetBrains packages).

By simply saving and checking the dependency package hash along with the package, name, and version, we can verify that this dependency has not been unexpectedly replaced! Using a hashing algorithm like xxHash this would be very fast and provide an order of magnitude higher level of security for any Gradle build with a super simple setup.

Additionally, we can store and check the signature hash for packages that provide one. Using truncated hashes (like for git commits) it will not bloat the dependency reports too much.

All of this can be optional and opt-in, disabled by default.

If this idea is welcome, I would be happy to provide a prototype/mvp pull request.

Support "allowedFilter" without a baseline file

One of the possible use cases of the dependencyGuard plugin is to restrict particular dependencies between modules of a multi-module project. For example, to restrict a feature-module as a dependency of a core-module in an Android project.

Such use case can easily be done via allowedFilter parameter of the dependencyGuard configuration. dependencyGuard plugin can be applied in the core-module with allowedFilter = { !it.startsWith("feature-") }. But a baseline file will also be created in such case, though it is not needed. For core-module we don't need to guard against dependencies changes, but only against particular dependencies themselves.

From this follows that there may be some configuration of the dependencyGuard plugin, that allows to specify allowedFilter without a baseline file creation.

One possible solution may be adding special function noBaseline(), that can be assigned to baselineMap to explicitly specify, that a baseline file is not needed. (using baselineMap = { null } for that is less explicit)

dependencyGuard {
    configuration("releaseRuntimeClasspath") {
        modules = true
        allowedFilter = { !it.startsWith("feature-") }
        baselineMap = noBaseline()
    }
}

Another possible solution may be adding parameter guardDiff or baselineFile (default to true), that will control whether a baseline file will be created or not. But this solution has drawbacks:

  • such parameter can be messed with parameter baselineMap
  • it's easy to break guarding against dependencies changes by assigning baselineFile = false
dependencyGuard {
    configuration("releaseRuntimeClasspath") {
        modules = true
        allowedFilter = { !it.startsWith("feature-") }
        guardDiff = false
    }
}

IMHO, the first solution is preferred, because it is more explicit.

Better integration with renovate

So I use renovate to keep my dependencies up to date. Like here: vanniktech/TextBuilder#77 material version was updated, but then the task from dependency guard files since obviously that version hasn't been updated properly.

Slightly off-topic but: Do you happen to know if there is way in which we can feed the text files into renovate so that it'll just do a search + replace?

That would solve the problem of regenerating the file manually and pushing, while still catching regressions in case a new version has been introduced.

Bumping version results in configuration error

In our monorepo, if we bump from 0.3.2 to 0.4.3, it can no longer find the configuration:

* Where:
Build file '/Users/knelson/workspace/android/app/app.gradle' line: 5

* What went wrong:
An exception occurred applying plugin request [id: 'com.dropbox.dependency-guard', version: '0.4.3']
> Failed to apply plugin 'com.dropbox.dependency-guard'.
   > Could not create task ':app:dependencyGuardBaseline'.
      > Error: No configurations provided to Dependency Guard Plugin for project :app

Running with --no-configuration-cache and without does not make a difference.

Where to add `dependencyGuard{}` closure in project with multi modules & flavors?

For example, there are 3 modules in my project: app, utils and feature.
In app, there are also 2 flavor: dev and prod.

So, where should I add the dependencyGuard{} closure? In project-level build.gradle, or in all module-level build.gradle, or only in app > build.gradle?

And moreover, should I update configuration("releaseRuntimeClasspath") to reflect the flavor? It means configuration("prodReleaseRuntimeClasspath") ?

FR: Fail when `dependencyGuard` is executed and there is no generated file.

Current behaviour:

  1. Create a new module
  2. Push it without dependencyGuard call.
  3. dependencyGuard at CI will generated this file each time.
  4. Nothing is checked.

Option 1:
Add separate task generateDependencyGuard that create a files in modules where we don't have already generated files.

Possible behaviour:

  1. Create a new module
  2. Push it without dependencyGuard call.
  3. dependencyGuard will fail for this module.

Bottomline:
We have dependencyGuard dependencyGuardBaseline
Will have dependencyGuardGenerate dependencyGuard dependencyGuardBaseline

for inspiration https://github.com/nebula-plugins/gradle-dependency-lock-plugin/wiki/Usage#tasks-provided
gradle-dependency-lock-plugin has: generate, save, update, commit operations.

I could contribute If you like my idea.

Unused plugins jacoco and ktlint

It seems like these two plugins are not used in the project. Should we remove them?

The task ktlintCheck is available but not executed on any GitHub Actions pipeline.
Should we add it here (though it would not run on the root project and other subprojects)

run: ./gradlew -p dependency-guard clean check apiCheck --no-daemon --stacktrace

or add a dedicated step.

Update dependency-tree-diff

Hello!

dependencyTreeDiff sources are copied to this project from dependency-tree-diff from commit 2548d3e95aa709c2c2b95ed61241a49bfadddd72 (file's history).

And now there is a newer version in the original repo (commit 4e86e45bbb032535d8106aec2811399f4bce7b49). It would be great to update sources in this repo to receive bug fixes from the original repo.

Feature Request: Provide more context on baseline files

Compared to other code analysis tools, the baseline files of the dependency-guard plugin lack context and their existence in the repository is not really self-explanatory.

Especially on large teams, where a lot of engineers aren't actually active involved in some of the things like dependency management, I think it could be nice if the files that define baselines would contain a file header which either gives a short description what that file does, or links to the website of dependency-guard to explain what the file is and why it exists.

Prefixing them with the tool could also be helpful , like how lint baselines are are name line-baseline.xml and detekt baseline files are name detekt-baseline.

What do you think?

Configuration was not found on version 0.4.0

After upgrading from 0.3.2 to 0.4.0 the project configuration phase fails with the following error message:

Click to open stacktrace
 FAILURE: Build failed with an exception.

 * What went wrong:
 A problem occurred configuring project ':Project'.
 > Could not create task ':Project:dependencyGuardBaseline'.
    > Configuration with name releaseRuntimeClasspath was not found for :Project

 * Try:
 > Run with --info or --debug option to get more log output.

 * Exception is:
 org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':[Project]'.
 	at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:84)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:77)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator.access$400(LifecycleProjectEvaluator.java:55)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:255)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.lambda$run$0(LifecycleProjectEvaluator.java:114)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:360)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:378)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:359)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:100)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:72)
 	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:762)
 	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:153)
 	at org.gradle.api.internal.project.ProjectLifecycleController.lambda$ensureSelfConfigured$1(ProjectLifecycleController.java:63)
 	at org.gradle.internal.model.StateTransitionController.lambda$doTransition$12(StateTransitionController.java:236)
 	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:247)
 	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:235)
 	at org.gradle.internal.model.StateTransitionController.lambda$maybeTransitionIfNotCurrentlyTransitioning$9(StateTransitionController.java:196)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:34)
 	at org.gradle.internal.model.StateTransitionController.maybeTransitionIfNotCurrentlyTransitioning(StateTransitionController.java:192)
 	at org.gradle.api.internal.project.ProjectLifecycleController.ensureSelfConfigured(ProjectLifecycleController.java:63)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.ensureConfigured(DefaultProjectStateRegistry.java:334)
 	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:33)
 	at org.gradle.execution.DefaultTaskSelector.getSelection(DefaultTaskSelector.java:59)
 	at org.gradle.execution.selection.DefaultBuildTaskSelector.resolveTaskName(DefaultBuildTaskSelector.java:97)
 	at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:49)
 	at org.gradle.execution.TaskNameResolvingBuildTaskScheduler.scheduleRequestedTasks(TaskNameResolvingBuildTaskScheduler.java:50)
 	at org.gradle.execution.DefaultTasksBuildTaskScheduler.scheduleRequestedTasks(DefaultTasksBuildTaskScheduler.java:72)
 	at org.gradle.initialization.DefaultTaskExecutionPreparer.lambda$scheduleRequestedTasks$0(DefaultTaskExecutionPreparer.java:46)
 	at org.gradle.internal.Factories$1.create(Factories.java:31)
 	at org.gradle.internal.work.DefaultWorkerLeaseService.withReplacedLocks(DefaultWorkerLeaseService.java:345)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:205)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:198)
 	at org.gradle.initialization.DefaultTaskExecutionPreparer.scheduleRequestedTasks(DefaultTaskExecutionPreparer.java:45)
 	at org.gradle.initialization.VintageBuildModelController.lambda$scheduleRequestedTasks$0(VintageBuildModelController.java:76)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:110)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:125)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:121)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:109)
 	at org.gradle.initialization.VintageBuildModelController.scheduleRequestedTasks(VintageBuildModelController.java:76)
 	at org.gradle.internal.build.DefaultBuildLifecycleController$DefaultWorkGraphBuilder.addRequestedTasks(DefaultBuildLifecycleController.java:255)
 	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$0(DefaultBuildTreeWorkPreparer.java:38)
 	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$3(DefaultBuildLifecycleController.java:143)
 	at org.gradle.internal.build.DefaultBuildWorkPreparer.populateWorkGraph(DefaultBuildWorkPreparer.java:42)
 	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.populateTaskGraph(BuildOperationFiringBuildWorkPreparer.java:120)
 	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.run(BuildOperationFiringBuildWorkPreparer.java:91)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer.populateWorkGraph(BuildOperationFiringBuildWorkPreparer.java:68)
 	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$4(DefaultBuildLifecycleController.java:143)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:110)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:125)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:121)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:109)
 	at org.gradle.internal.build.DefaultBuildLifecycleController.populateWorkGraph(DefaultBuildLifecycleController.java:143)
 	at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.populateWorkGraph(DefaultBuildWorkGraphController.java:148)
 	at org.gradle.composite.internal.DefaultBuildController.populateWorkGraph(DefaultBuildController.java:73)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraphBuilder.withWorkGraph(DefaultIncludedBuildTaskGraph.java:150)
 	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$1(DefaultBuildTreeWorkPreparer.java:38)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1.run(DefaultIncludedBuildTaskGraph.java:197)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph.scheduleWork(DefaultIncludedBuildTaskGraph.java:192)
 	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.scheduleRequestedTasks(DefaultBuildTreeWorkPreparer.java:37)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$doScheduleAndRunTasks$3(DefaultBuildTreeLifecycleController.java:96)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph.withNewWorkGraph(DefaultIncludedBuildTaskGraph.java:109)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.doScheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:95)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$scheduleAndRunTasks$1(DefaultBuildTreeLifecycleController.java:76)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$runBuild$5(DefaultBuildTreeLifecycleController.java:113)
 	at org.gradle.internal.model.StateTransitionController.lambda$transition$5(StateTransitionController.java:166)
 	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:247)
 	at org.gradle.internal.model.StateTransitionController.lambda$transition$6(StateTransitionController.java:166)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
 	at org.gradle.internal.model.StateTransitionController.transition(StateTransitionController.java:166)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.runBuild(DefaultBuildTreeLifecycleController.java:110)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:76)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:71)
 	at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:31)
 	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
 	at org.gradle.internal.buildtree.ProblemReportingBuildActionRunner.run(ProblemReportingBuildActionRunner.java:49)
 	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:65)
 	at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:136)
 	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
 	at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.lambda$execute$0(RootBuildLifecycleBuildActionExecutor.java:40)
 	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:122)
 	at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.execute(RootBuildLifecycleBuildActionExecutor.java:40)
 	at org.gradle.internal.buildtree.DefaultBuildTreeContext.execute(DefaultBuildTreeContext.java:40)
 	at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.lambda$execute$0(BuildTreeLifecycleBuildActionExecutor.java:65)
 	at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53)
 	at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.execute(BuildTreeLifecycleBuildActionExecutor.java:65)
 	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3.call(RunAsBuildOperationBuildActionExecutor.java:61)
 	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3.call(RunAsBuildOperationBuildActionExecutor.java:57)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
 	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor.execute(RunAsBuildOperationBuildActionExecutor.java:57)
 	at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.lambda$execute$0(RunAsWorkerThreadBuildActionExecutor.java:36)
 	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:249)
 	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:109)
 	at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.execute(RunAsWorkerThreadBuildActionExecutor.java:36)
 	at org.gradle.tooling.internal.provider.continuous.ContinuousBuildActionExecutor.execute(ContinuousBuildActionExecutor.java:110)
 	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecutor.execute(SubscribableBuildActionExecutor.java:64)
 	at org.gradle.internal.session.DefaultBuildSessionContext.execute(DefaultBuildSessionContext.java:46)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:100)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:88)
 	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:69)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:62)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:41)
 	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:63)
 	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31)
 	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:52)
 	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:40)
 	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
 	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
 	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
 	at org.gradle.util.internal.Swapper.swap(Swapper.java:38)
 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
 	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
 	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
 	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
 Caused by: org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreationException: Could not create task ':Project:dependencyGuardBaseline'.
 	at org.gradle.api.internal.tasks.DefaultTaskContainer.taskCreationException(DefaultTaskContainer.java:715)
 	at org.gradle.api.internal.tasks.DefaultTaskContainer.access$600(DefaultTaskContainer.java:76)
 	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.domainObjectCreationException(DefaultTaskContainer.java:707)
 	at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.tryCreate(DefaultNamedDomainObjectCollection.java:948)
 	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.access$1401(DefaultTaskContainer.java:654)
 	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider$1.run(DefaultTaskContainer.java:680)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.tryCreate(DefaultTaskContainer.java:676)
 	at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.calculateOwnValue(DefaultNamedDomainObjectCollection.java:929)
 	at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:107)
 	at org.gradle.api.internal.provider.Collectors$ElementFromProvider.collectEntries(Collectors.java:100)
 	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectEntries(Collectors.java:334)
 	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectInto(Collectors.java:329)
 	at org.gradle.api.internal.collections.DefaultPendingSource.realize(DefaultPendingSource.java:62)
 	at org.gradle.api.internal.collections.DefaultPendingSource.realizePending(DefaultPendingSource.java:52)
 	at org.gradle.api.internal.collections.SortedSetElementSource.realizePending(SortedSetElementSource.java:112)
 	at org.gradle.api.internal.DefaultDomainObjectCollection.addEagerAction(DefaultDomainObjectCollection.java:224)
 	at org.gradle.api.internal.DefaultDomainObjectCollection.whenObjectAdded(DefaultDomainObjectCollection.java:215)
 	at org.gradle.api.internal.tasks.DefaultTaskCollection.whenTaskAdded(DefaultTaskCollection.java:87)
 	at LbcMainApplicationPlugin$fixGoogleServicesTasksOrdering$1.invoke(LbcMainApplicationPlugin.kt:150)
 	at LbcMainApplicationPlugin$fixGoogleServicesTasksOrdering$1.invoke(LbcMainApplicationPlugin.kt:23)
 	at com.android.build.api.extension.impl.AndroidComponentsExtensionImpl$beforeVariants$1.execute(AndroidComponentsExtensionImpl.kt:62)
 	at com.android.build.api.extension.impl.AndroidComponentsExtensionImpl$beforeVariants$1.execute(AndroidComponentsExtensionImpl.kt:61)
 	at com.android.build.api.extension.impl.OperationsRegistrar.executeOperations(OperationsRegistrar.kt:65)
 	at com.android.build.gradle.internal.VariantManager.createVariant(VariantManager.kt:312)
 	at com.android.build.gradle.internal.VariantManager.createVariantsFromCombination(VariantManager.kt:805)
 	at com.android.build.gradle.internal.VariantManager.computeVariants(VariantManager.kt:224)
 	at com.android.build.gradle.internal.VariantManager.createVariants(VariantManager.kt:182)
 	at com.android.build.gradle.internal.plugins.BasePlugin.createAndroidTasks(BasePlugin.kt:682)
 	at com.android.build.gradle.internal.plugins.BasePlugin$createTasks$2$1.call(BasePlugin.kt:572)
 	at com.android.build.gradle.internal.profile.NoOpAnalyticsConfiguratorService.recordBlock(NoOpAnalyticsConfiguratorService.kt:53)
 	at com.android.build.gradle.internal.plugins.BasePlugin$createTasks$2.accept(BasePlugin.kt:567)
 	at com.android.build.gradle.internal.plugins.BasePlugin$createTasks$2.accept(BasePlugin.kt:564)
 	at com.android.build.gradle.internal.crash.CrashReporting$afterEvaluate$1.execute(crash_reporting.kt:37)
 	at com.android.build.gradle.internal.crash.CrashReporting$afterEvaluate$1.execute(crash_reporting.kt:35)
 	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:123)
 	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1.run(DefaultListenerBuildOperationDecorator.java:171)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction.execute(DefaultListenerBuildOperationDecorator.java:168)
 	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:95)
 	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:83)
 	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:43)
 	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:245)
 	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:157)
 	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:83)
 	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:69)
 	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346)
 	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249)
 	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141)
 	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
 	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
 	at com.sun.proxy.$Proxy46.afterEvaluate(Unknown Source)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:247)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:244)
 	at org.gradle.api.internal.project.DefaultProject.stepEvaluationListener(DefaultProject.java:1439)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:253)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.lambda$run$0(LifecycleProjectEvaluator.java:114)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:360)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:378)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:359)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:100)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:72)
 	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:762)
 	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:153)
 	at org.gradle.api.internal.project.ProjectLifecycleController.lambda$ensureSelfConfigured$1(ProjectLifecycleController.java:63)
 	at org.gradle.internal.model.StateTransitionController.lambda$doTransition$12(StateTransitionController.java:236)
 	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:247)
 	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:235)
 	at org.gradle.internal.model.StateTransitionController.lambda$maybeTransitionIfNotCurrentlyTransitioning$9(StateTransitionController.java:196)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:34)
 	at org.gradle.internal.model.StateTransitionController.maybeTransitionIfNotCurrentlyTransitioning(StateTransitionController.java:192)
 	at org.gradle.api.internal.project.ProjectLifecycleController.ensureSelfConfigured(ProjectLifecycleController.java:63)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.ensureConfigured(DefaultProjectStateRegistry.java:334)
 	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:33)
 	at org.gradle.execution.DefaultTaskSelector.getSelection(DefaultTaskSelector.java:59)
 	at org.gradle.execution.selection.DefaultBuildTaskSelector.resolveTaskName(DefaultBuildTaskSelector.java:97)
 	at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:49)
 	at org.gradle.execution.TaskNameResolvingBuildTaskScheduler.scheduleRequestedTasks(TaskNameResolvingBuildTaskScheduler.java:50)
 	at org.gradle.execution.DefaultTasksBuildTaskScheduler.scheduleRequestedTasks(DefaultTasksBuildTaskScheduler.java:72)
 	at org.gradle.initialization.DefaultTaskExecutionPreparer.lambda$scheduleRequestedTasks$0(DefaultTaskExecutionPreparer.java:46)
 	at org.gradle.internal.Factories$1.create(Factories.java:31)
 	at org.gradle.internal.work.DefaultWorkerLeaseService.withReplacedLocks(DefaultWorkerLeaseService.java:345)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:205)
 	at org.gradle.api.internal.project.DefaultProjectStateRegistry$DefaultBuildProjectRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:198)
 	at org.gradle.initialization.DefaultTaskExecutionPreparer.scheduleRequestedTasks(DefaultTaskExecutionPreparer.java:45)
 	at org.gradle.initialization.VintageBuildModelController.lambda$scheduleRequestedTasks$0(VintageBuildModelController.java:76)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:110)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:125)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:121)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:109)
 	at org.gradle.initialization.VintageBuildModelController.scheduleRequestedTasks(VintageBuildModelController.java:76)
 	at org.gradle.internal.build.DefaultBuildLifecycleController$DefaultWorkGraphBuilder.addRequestedTasks(DefaultBuildLifecycleController.java:255)
 	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$0(DefaultBuildTreeWorkPreparer.java:38)
 	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$3(DefaultBuildLifecycleController.java:143)
 	at org.gradle.internal.build.DefaultBuildWorkPreparer.populateWorkGraph(DefaultBuildWorkPreparer.java:42)
 	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.populateTaskGraph(BuildOperationFiringBuildWorkPreparer.java:120)
 	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph.run(BuildOperationFiringBuildWorkPreparer.java:91)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer.populateWorkGraph(BuildOperationFiringBuildWorkPreparer.java:68)
 	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$populateWorkGraph$4(DefaultBuildLifecycleController.java:143)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$1(StateTransitionController.java:110)
 	at org.gradle.internal.model.StateTransitionController.lambda$inState$2(StateTransitionController.java:125)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:121)
 	at org.gradle.internal.model.StateTransitionController.inState(StateTransitionController.java:109)
 	at org.gradle.internal.build.DefaultBuildLifecycleController.populateWorkGraph(DefaultBuildLifecycleController.java:143)
 	at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.populateWorkGraph(DefaultBuildWorkGraphController.java:148)
 	at org.gradle.composite.internal.DefaultBuildController.populateWorkGraph(DefaultBuildController.java:73)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraphBuilder.withWorkGraph(DefaultIncludedBuildTaskGraph.java:150)
 	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.lambda$scheduleRequestedTasks$1(DefaultBuildTreeWorkPreparer.java:38)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1.run(DefaultIncludedBuildTaskGraph.java:197)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph.scheduleWork(DefaultIncludedBuildTaskGraph.java:192)
 	at org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer.scheduleRequestedTasks(DefaultBuildTreeWorkPreparer.java:37)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$doScheduleAndRunTasks$3(DefaultBuildTreeLifecycleController.java:96)
 	at org.gradle.composite.internal.DefaultIncludedBuildTaskGraph.withNewWorkGraph(DefaultIncludedBuildTaskGraph.java:109)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.doScheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:95)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$scheduleAndRunTasks$1(DefaultBuildTreeLifecycleController.java:76)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$runBuild$5(DefaultBuildTreeLifecycleController.java:113)
 	at org.gradle.internal.model.StateTransitionController.lambda$transition$5(StateTransitionController.java:166)
 	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:247)
 	at org.gradle.internal.model.StateTransitionController.lambda$transition$6(StateTransitionController.java:166)
 	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
 	at org.gradle.internal.model.StateTransitionController.transition(StateTransitionController.java:166)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.runBuild(DefaultBuildTreeLifecycleController.java:110)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:76)
 	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.scheduleAndRunTasks(DefaultBuildTreeLifecycleController.java:71)
 	at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:31)
 	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
 	at org.gradle.internal.buildtree.ProblemReportingBuildActionRunner.run(ProblemReportingBuildActionRunner.java:49)
 	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:65)
 	at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:136)
 	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
 	at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.lambda$execute$0(RootBuildLifecycleBuildActionExecutor.java:40)
 	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:122)
 	at org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor.execute(RootBuildLifecycleBuildActionExecutor.java:40)
 	at org.gradle.internal.buildtree.DefaultBuildTreeContext.execute(DefaultBuildTreeContext.java:40)
 	at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.lambda$execute$0(BuildTreeLifecycleBuildActionExecutor.java:65)
 	at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53)
 	at org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor.execute(BuildTreeLifecycleBuildActionExecutor.java:65)
 	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3.call(RunAsBuildOperationBuildActionExecutor.java:61)
 	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3.call(RunAsBuildOperationBuildActionExecutor.java:57)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
 	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor.execute(RunAsBuildOperationBuildActionExecutor.java:57)
 	at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.lambda$execute$0(RunAsWorkerThreadBuildActionExecutor.java:36)
 	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:249)
 	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:109)
 	at org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor.execute(RunAsWorkerThreadBuildActionExecutor.java:36)
 	at org.gradle.tooling.internal.provider.continuous.ContinuousBuildActionExecutor.execute(ContinuousBuildActionExecutor.java:110)
 	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecutor.execute(SubscribableBuildActionExecutor.java:64)
 	at org.gradle.internal.session.DefaultBuildSessionContext.execute(DefaultBuildSessionContext.java:46)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:100)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl.apply(BuildSessionLifecycleBuildActionExecuter.java:88)
 	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:69)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:62)
 	at org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter.execute(BuildSessionLifecycleBuildActionExecuter.java:41)
 	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:63)
 	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31)
 	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:52)
 	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:40)
 	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
 	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
 	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
 	at org.gradle.util.internal.Swapper.swap(Swapper.java:38)
 	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
 	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
 	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
 	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
 	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
 	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
 	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
 Caused by: org.gradle.api.GradleException: Configuration with name releaseRuntimeClasspath was not found for :Project
 	at com.dropbox.gradle.plugins.dependencyguard.internal.ConfigurationValidators.validateConfigurationsAreAvailable(ConfigurationValidators.kt:94)
 	at com.dropbox.gradle.plugins.dependencyguard.internal.list.DependencyGuardListTask.setParams$dependency_guard(DependencyGuardListTask.kt:169)
 	at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin$registerDependencyGuardBaselineTask$1.execute(DependencyGuardPlugin.kt:65)
 	at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin$registerDependencyGuardBaselineTask$1.execute(DependencyGuardPlugin.kt:14)
 	at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:44)
 	at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:44)
 	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:123)
 	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:110)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
 	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
 	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
 	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:107)
 	at org.gradle.internal.ImmutableActionSet$SetWithManyActions.execute(ImmutableActionSet.java:329)
 	at org.gradle.api.internal.DefaultDomainObjectCollection.doAdd(DefaultDomainObjectCollection.java:262)
 	at org.gradle.api.internal.DefaultNamedDomainObjectCollection.doAdd(DefaultNamedDomainObjectCollection.java:113)
 	at org.gradle.api.internal.DefaultDomainObjectCollection.add(DefaultDomainObjectCollection.java:256)
 	at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.tryCreate(DefaultNamedDomainObjectCollection.java:944)
 	... 236 more

Improve re-baseline message for multi app projects

Hello! In an Android project there may be several app-modules with common libs.versions.toml. And after updating versions it may be needed to re-baseline dependencies in all app-modules.
Currently, re-baseline message contains gradle command only about the particular module, so it might be useful to append gradle command that re-baselines dependencies in the whole project.

`baselineFilter` - Filter out certain dependencies from baseline (allowed, but ignored for baseline purposes)

In our build, we have a lot of convention plugins that we publish to an internal Artifactory instance, and which we update regularly. Their coordinates look like this:

com.squareup.register:plugins:1.40.0

Since we run ./gradlew :dependencyGuard on every build, we get a failure every time we publish a new version. (The baseline will contain ...1.39.0, but the build is running on 1.40.0.) One way to avoid this is to filter these buildscript dependencies out of the classpath.txt baseline file: basically ignore them for purposes of the baseline.

Alternatively, say that we don't care if the version changes (only care about the identifer, or com.squareup.register:plugins in this case).

I'm not actually convinced this is a great idea, but I wanted to run it by you to get your thoughts.

Why the dependency on kotlin-gradle-plugin?

When I applied this plugin to one of my projects, I ran into unrelated issues in my build, which I traced to the unintentional upgrade of the kotlin-gradle-plugin. Are the implementation dependencies on kotlin-gradle-plugin and the kotlin BoM necessary? I tried removing them - the tests still pass and the tasks seem to function appropriately in the sample app as well.

Improve/Fix Validations

Right now, validations are performed during afterEvaluate in #86 because we don't know what all configurations are until that point in time, and cannot be sure we have what we need to validate.

While this should work, it's better to just provide these values to the task and have the validations done there.

Feature request: filter out BOM artifacts

These are not actually a real dependency, rather they just dictate the version of other dependencies owned by it. This leaks across buildscript classpaths too. For example - buildscript { dependencies { classpath(platform(libs.coroutines.bom)) } } will make this show up as a configuration dependency in all subprojects using dependencyGuard as well even if they don't use coroutines. My sense here is that it should filter these.

Tree format requires Gradle 7.4 or higher

Hi team!

I wanted to play around with the tree format described in the README and came across the following error:

java.lang.NoClassDefFoundError: org/gradle/configurationcache/extensions/CharSequenceExtensionsKt
	at com.dropbox.gradle.plugins.dependencyguard.internal.DependencyTreeDiffTaskNames.createDependencyTreeTaskNameForConfiguration(DependencyTreeDiffTaskNames.kt:8)
	at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin$registerTreeDiffTasks$1.execute(DependencyGuardPlugin.kt:108)
	at com.dropbox.gradle.plugins.dependencyguard.DependencyGuardPlugin$registerTreeDiffTasks$1.execute(DependencyGuardPlugin.kt:16)

I'm using version 0.3.0 of the plugin with Gradle 7.2 in our project.

I think this is because it relies on the CharSequence.capitalized() extension defined in /src/main/kotlin/org/gradle/configurationcache/extensions/CharSequenceExtensions.kt which only landed in Gradle 7.4.

image

Perhaps a small update to the README to point this out might be useful? Alternatively there is probably a Kotlin extension that does something similar?

Cheers.

DependencyReportTask may be deprecated

This is not an issue, just a question. ๐Ÿ˜„

You know,
there is an issue that DependencyReportTask is not compatible with the configuration cache. (#4)
And I found this while browsing the issue tracker. ๐Ÿ‘€

https://issuetracker.google.com/issues/235457021
We should just deprecate this task, it's not that useful.

I know that this open source works based on DependencyReportTask.
So I wonder if this library will still work even if DependencyReportTask is deprecated.
Any other alternatives?

Task Input Params, Leverage `project.layout`

A few values are derived from project.layout and used as inputs to our tasks. @ZacSweers called out that it is possible to pass the entire project.layout value as it is config cache safe.

Example:

Other usages: https://github.com/dropbox/dependency-guard/search?q=project.layout

It was also suggested that we could avoid passing project.path and leveraging that property from the project.layout variable.

Fix "baseline created" message in DependencyGuardTreeDiffer

Hello!

When DependencyGuardTreeDiffer writes a tree baseline file, it prints the following message (sources):

"Dependency Guard Tree baseline created for : for configuration classpath."

But DependencyGuardTreeDiffer writes a tree baseline file for the given project.path and configurationName, not only for : and classpath. So it would be useful to print the correct project.path and configurationName in the output message.

Expected example of the output message:

"Dependency Guard Tree baseline created for :lib for configuration testCompileClasspath."

Issue after 0.4.0 Release

I'm monitoring the buildscript classpath, and after bumping to 0.4.0, it correctly detects there was a change. HOWEVER, if I do run the ./gradlew :dependencyGuardBaseline task it does not create a new baseline file and just continues to show the error.

* What went wrong:
Execution failed for task ':buildSrc:dependencyGuard'.
> Dependencies Changed in : for configuration classpath
  - com.dropbox.dependency-guard:com.dropbox.dependency-guard.gradle.plugin:0.3.1
  + com.dropbox.dependency-guard:com.dropbox.dependency-guard.gradle.plugin:0.4.0
  - com.dropbox.dependency-guard:dependency-guard:0.3.1
  + com.dropbox.dependency-guard:dependency-guard:0.4.0
  
  If this is intentional, re-baseline using ./gradlew :dependencyGuardBaseline
  Or use ./gradlew dependencyGuardBaseline to re-baseline dependencies in entire project.

I then deleted the rm dependencies/classpath.txt file and re-ran the baseline task, but am seeing the same issue. I additionally attempted to add --no-configuration-cache and it still doesn't work as expected. No new baseline file is created, and the same error above is shown.

A property of type 'DirectoryProperty' annotated with @Input cannot determine how to interpret the file.

Execution optimizations have been disabled for task ':buildSrc:dependencyGuard' to ensure correctness due to the following reasons:
  - In plugin 'com.dropbox.dependency-guard' type 'com.dropbox.gradle.plugins.dependencyguard.internal.list.DependencyGuardListTask' property 'buildDirectory' has @Input annotation used on property of type 'DirectoryProperty'. Reason: A property of type 'DirectoryProperty' annotated with @Input cannot determine how to interpret the file. Please refer to https://docs.gradle.org/7.6/userguide/validation_problems.html#incorrect_use_of_input_annotation for more details about this problem.

https://docs.gradle.org/7.6/userguide/validation_problems.html#incorrect_use_of_input_annotation

Add Gradle Init Script to Documentation?

The configuration phase to appropriately run dependency-guard is very expensive. An alternate option to having it "always installed" is to run it via a Gradle init script.

In this example, it will run the dependencyGuardBaseline tasks for all projects that have the plugin applied. The code iterates through all projects and only applies it to those who are com.android.library or com.android.application. This can be modified as desired.

dependency-guard.gradle

/**
 * Gradle init-script which applies the plugin to existing Gradle projects.
 *
 * Run it with the following:
 * ./gradlew --init-script dependency-guard.gradle dependencyGuardBaseline
 */
settingsEvaluated {
  rootProject {
    buildscript {
      repositories {
        mavenCentral()
        gradlePluginPortal()
        google()
        mavenLocal()
      }

      dependencies {
        classpath("com.dropbox.dependency-guard:dependency-guard:0.4.3")
      }
    }
    afterEvaluate {
      allprojects {
        project.afterEvaluate {
          // filtering for android library and app plugins in this case, but you could change this logic as needed.
          if (plugins.findPlugin("com.android.library") != null || plugins.findPlugin("com.android.application") != null) {
            plugins.apply("com.dropbox.dependency-guard")
            dependencyGuard {
              configuration("debugRuntimeClasspath") {
                modules = true
              }
            }
          }
        }
      }
    }
  }
}

I can then just run ./gradlew --init-script dependency-guard.gradle dependencyGuardBaseline

Additionally I can target a project specifically with./gradlew --init-script dependency-guard.gradle :app:dependencyGuard if desired. (to avoid configuring all projects when configuration on demand is enabled)

Dependency change only on CI though

This is a bit of a weird one. The dependencyTreeDiffReleaseRuntimeClasspath tasks fails for me. However it's only on CI. This issue started happening with these changes (adding the plugin to more modules): vanniktech/Emoji@3fe08a3

This is the CLI output:

> Task :emoji-facebook:dependencyTreeDiffReleaseRuntimeClasspath FAILED
See the report at: file:///home/runner/work/Emoji/Emoji/emoji-facebook/build/tmp/dependency-guard/releaseRuntimeClasspath.tree.txt
***** DEPENDENCY CHANGE DETECTED *****
-+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0
-|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.7.0
-|    |    \--- org.jetbrains:annotations:13.0
-|    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0
-|         \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.0 (*)
 +--- project :emoji
-|    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0 (*)
+|    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0
+|         +--- org.jetbrains.kotlin:kotlin-stdlib:1.7.0
+|         |    \--- org.jetbrains:annotations:13.0
+|         \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0
+|              \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.0 (*)
+\--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0 (*)

Running the task locally just works for me:
Screen Shot 2022-07-01 at 00 33 47

Could it be that dependencies are differently sorted on that particular machine and the tree diffing gets confused?

Create single task for tree configuration

Now the separated DependencyTreeDiffTask instance is created for each guarded configuration.

But that is not necessary, because the dependencies tree rendering is done by DependencyGraphsRenderer in AsciiDependencyReportRenderer2, that can be used with different configurations, depending on passed ResolvedComponentResult.

So rendering, comparing and baselining dependencies tree for all can be performed in one single task, like for the list configuration in DependencyGuardListTask.

Also, depending on the code complexity, handling list and task configurations may be done in the single task, since they have a lot of similar configs and logic.

Tasks not compatible with the configuration cache

First of all, this is not surprising, as dependency analysis in general is not well-supported by the configuration cache (CC). However, since I'm adding this to my project, I want an issue to refer to explain why CC has been disabled for these tasks.

I'm using the runtime API for this, available since Gradle 7.4. This looks like:

tasks.named('dependencyGuard') {
  notCompatibleWithConfigurationCache('https://github.com/dropbox/dependency-guard/issues/4')
}

I'm not sure which versions of Gradle you want to support, but you might bake this in with something like

tasks.register(...) {
  if (GradleVersion.current() > GradleVersion.version("7.4")) {
    notCompatibleWithConfigurationCache('https://github.com/dropbox/dependency-guard/issues/4')
  }
}

And of course, ideally, one day, this could be compatible out of the box.

Feature request: suppressible logging output

Currently there are some noisy logs that can't be suppressed and run even if there's no changes. If the task is successful, I'd expect it to just pass without extra logging. Could this be made configurable?

FR: harmonize `GradleException` content between List and Tree

List and Tree modes throw different exception content.

Tree

***** DEPENDENCY CHANGE DETECTED *****
-\--- androidx.activity:activity:1.4.0
-     +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     +--- androidx.core:core:1.7.0
-     |    +--- androidx.annotation:annotation:1.2.0
-     |    +--- androidx.annotation:annotation-experimental:1.1.0
-     |    +--- androidx.lifecycle:lifecycle-runtime:2.3.1
-     |    |    +--- androidx.arch.core:core-runtime:2.1.0
-     |    |    |    +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     |    |    |    \--- androidx.arch.core:core-common:2.1.0
-     |    |    |         \--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     |    |    +--- androidx.lifecycle:lifecycle-common:2.3.1
-     |    |    |    \--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     |    |    +--- androidx.arch.core:core-common:2.1.0 (*)
-     |    |    \--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     |    +--- androidx.versionedparcelable:versionedparcelable:1.1.1
-     |    |    +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     |    |    \--- androidx.collection:collection:1.0.0
-     |    |         \--- androidx.annotation:annotation:1.0.0 -> 1.2.0
-     |    +--- androidx.collection:collection:1.0.0 (*)
-     |    \--- androidx.concurrent:concurrent-futures:1.0.0
-     |         +--- com.google.guava:listenablefuture:1.0
-     |         \--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     +--- androidx.lifecycle:lifecycle-runtime:2.3.1 (*)
-     +--- androidx.lifecycle:lifecycle-viewmodel:2.3.1
-     |    \--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     +--- androidx.savedstate:savedstate:1.1.0
-     |    +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
-     |    +--- androidx.arch.core:core-common:2.0.1 -> 2.1.0 (*)
-     |    \--- androidx.lifecycle:lifecycle-common:2.0.0 -> 2.3.1 (*)
-     +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1
-     |    +--- androidx.annotation:annotation:1.0.0 -> 1.2.0
-     |    +--- androidx.savedstate:savedstate:1.1.0 (*)
-     |    +--- androidx.lifecycle:lifecycle-livedata-core:2.3.1
-     |    |    +--- androidx.arch.core:core-common:2.1.0 (*)
-     |    |    +--- androidx.arch.core:core-runtime:2.1.0 (*)
-     |    |    \--- androidx.lifecycle:lifecycle-common:2.3.1 (*)
-     |    \--- androidx.lifecycle:lifecycle-viewmodel:2.3.1 (*)
-     +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.31 -> 1.6.10 (*)
-     +--- androidx.collection:collection:1.0.0 (*)
-     \--- androidx.tracing:tracing:1.0.0
-          \--- androidx.annotation:annotation:1.1.0 -> 1.2.0
+\--- androidx.activity:activity:1.4.1 FAILED


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sample:app:dependencyTreeDiffReleaseRuntimeClasspath'.
> ***** DEPENDENCY CHANGE DETECTED *****
  Dependency Tree comparison to baseline does not match.
  
  If this is intentional, re-baseline using ./gradlew :sample:app:dependencyGuardBaseline
  Or use ./gradlew dependencyGuardBaseline to re-baseline dependencies in entire project.


* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 9s
9:07:35 pm: Execution finished ':sample:app:dependencyGuard --quiet'.

List

Dependencies Changed in :sample:app for configuration releaseRuntimeClasspath
- androidx.activity:activity:1.4.0
- androidx.annotation:annotation-experimental:1.1.0
- androidx.annotation:annotation:1.2.0
- androidx.arch.core:core-common:2.1.0
- androidx.arch.core:core-runtime:2.1.0
- androidx.collection:collection:1.0.0
- androidx.concurrent:concurrent-futures:1.0.0
- androidx.core:core:1.7.0
- androidx.lifecycle:lifecycle-common:2.3.1
- androidx.lifecycle:lifecycle-livedata-core:2.3.1
- androidx.lifecycle:lifecycle-runtime:2.3.1
- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1
- androidx.lifecycle:lifecycle-viewmodel:2.3.1
- androidx.savedstate:savedstate:1.1.0
- androidx.tracing:tracing:1.0.0
- androidx.versionedparcelable:versionedparcelable:1.1.1
- com.google.guava:listenablefuture:1.0


If this is intentional, re-baseline using ./gradlew :sample:app:dependencyGuardBaseline
Or use ./gradlew dependencyGuardBaseline to re-baseline dependencies in entire project.


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sample:app:dependencyGuard'.
> Dependencies Changed in :sample:app for configuration releaseRuntimeClasspath
  - androidx.activity:activity:1.4.0
  - androidx.annotation:annotation-experimental:1.1.0
  - androidx.annotation:annotation:1.2.0
  - androidx.arch.core:core-common:2.1.0
  - androidx.arch.core:core-runtime:2.1.0
  - androidx.collection:collection:1.0.0
  - androidx.concurrent:concurrent-futures:1.0.0
  - androidx.core:core:1.7.0
  - androidx.lifecycle:lifecycle-common:2.3.1
  - androidx.lifecycle:lifecycle-livedata-core:2.3.1
  - androidx.lifecycle:lifecycle-runtime:2.3.1
  - androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1
  - androidx.lifecycle:lifecycle-viewmodel:2.3.1
  - androidx.savedstate:savedstate:1.1.0
  - androidx.tracing:tracing:1.0.0
  - androidx.versionedparcelable:versionedparcelable:1.1.1
  - com.google.guava:listenablefuture:1.0
  
  If this is intentional, re-baseline using ./gradlew :sample:app:dependencyGuardBaseline
  Or use ./gradlew dependencyGuardBaseline to re-baseline dependencies in entire project.
  


* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 2s

As you can see, the List output contains the diff twice. Moreover, the colored diff output is generally hidden, and only the last bit appear, which is the GradleException message. (and the first line is different as well)
Do you think we could harmonize this behavior?

  • Tree (ad-hoc message)

throw GradleException(
StringBuilder().apply {
appendLine(Messaging.dependencyChangeDetected)
appendLine("Dependency Tree comparison to baseline does not match.")
appendLine()
appendLine(Messaging.rebaselineMessage(projectPath))
}.toString()

  • List (reuses the exception message, leading to duplicated output)

Add tests for various Gradle versions

Now tests in dependency-guard/gradleTest run only against GradleVersion.current().
It would be great to add parameterized tests to test against various Gradle versions. It will ensure support for older versions as well as newer.

As have been mentioned in #51.

Also check GradleVersion. isAtLeast73 can be removed if we decide to remove Gradle 7.3 support.

Can I easily protect all the dependencies of all the modules in a multi-module project?

Hi.

First of all, thanks for creating this plugin. It's truly awesome ๐Ÿ‘

However, I'm not sure how I could apply this plugin in a more generic way to cover all the possible dependencies in the whole multi-module project. Preferably, I would like to be able to guard literally all the dependencies, including the test dependencies (testImplementation / testAndroidImplementation etc.) of all the modules.

I figured out I can declare multiple configuration() { ... } blocks, for example in my app module I have:

dependencyGuard {
    listOf(
        // In most cases, "XRuntime" will contain "XCompile" dependencies but not always so we check all just in case
        "stageDebugCompileClasspath",
        "stageDebugRuntimeClasspath",
        "stageDebugUnitTestCompileClasspath",
        "stageDebugUnitTestRuntimeClasspath",
        "stageDebugAndroidTestCompileClasspath",
        "stageDebugAndroidTestRuntimeClasspath"
    ).forEach {
        configuration(it) {
            artifacts = true
            modules = false
            tree = false
        }
    }
}

However, I'm afraid that if app module has a dependency on featureX module, and featureX has testImplementation dependency on org.slf4j:slf4j-simple that is not used by app, then Dependency Guard can't protect against unwanted org.slf4j:slf4j-simple version changes, unless I apply the plugin to featureX module separately.

Therefore, I'm wondering what is the easiest way to cover that case.

  1. Is there any option to run the Dependency Guard against the whole project at once by configuring it once in a single place?
  2. Should I just apply this plugin to all my modules? If this is the suggested solution, then I have two more issues/questions:
    1. For m number of modules it's going to produce at least m text files (or m x n to cover n configurations in each module), where most of the dependencies are going to be the same (e.g. kotlin-stdlib). Therefore, it would be great to merge all these files into a single one for multi-module setup.
    2. When I tried using the plugin in allprojects in the root build.gradle.kts file, I applied the plugin and immediately stumbled upon a problem with the clean task. I'm not sure if it's a bug within this plugin or I simply did something wrong.
      org.gradle.internal.exceptions.LocationAwareException: Build file '/Users/azabost/projects/[redacted]/build.gradle.kts' line: 115
      Cannot add task 'clean' as a task with that name already exists.
      at org.gradle.kotlin.dsl.execution.InterpreterKt$locationAwareExceptionFor$2.invoke(Interpreter.kt:601)
      (...)
      Caused by: org.gradle.api.internal.tasks.DefaultTaskContainer$DuplicateTaskException: Cannot add task 'clean' as a task with that name already exists.
      at org.gradle.api.internal.tasks.DefaultTaskContainer.failOnDuplicateTask(DefaultTaskContainer.java:257)
      at org.gradle.api.internal.tasks.DefaultTaskContainer.registerTask(DefaultTaskContainer.java:398)
      at org.gradle.api.internal.tasks.DefaultTaskContainer.register(DefaultTaskContainer.java:375)
      at Build_gradle.<init>(build.gradle.kts:115)
      (...)
      

Can you advise something to cover my use case, please?

Version 0.3.0 is not resolving.

I published 0.3.0 2.5 hours ago and it's not showing up on Maven Central yet. Hopefully it will show up soon here: https://repo.maven.apache.org/maven2/com/dropbox/dependency-guard/dependency-guard/

Workarounds: Use version 0.2.0 which is stable and available. Also, if you need the latest functionality in 0.3.0, you can use 0.3.1-SNAPSHOT. Note: You need to use the snapshot maven repo: maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" }

I'll look into it more next week after the long vacation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.