Skip to content

joshfriend/spotlight

Repository files navigation

Spotlight

Maven Central Version Gradle Plugin Portal Version IDE Plugin Version License: MIT

A Gradle plugin and IntelliJ plugin that make managing settings.gradle(.kts) for large projects easier:

  • Moves your include directives out of settings.gradle(.kts) into a flat text file
  • Inspired by tools like Focus and dependency-explorer, this plugin lets you easily load subsets of your project into the IDE
  • Only loads the minimum required list of projects required to run your requested build tasks

Tip

Using Spotlight can dramatically decrease your IDE sync times, check out this blog post for more info.

How to use it

Apply the plugin in settings.gradle(.kts):

plugins {
  id 'com.fueledbycaffeine.spotlight'
}

Create gradle/all-projects.txt with your project declarations:

:app
:feature-a
:feature-b
# items may be commented out like this

Tip

If you have an existing project with include statements in settings.gradle(.kts), you can run ./gradlew :fixAllProjectsList to automatically migrate them to all-projects.txt and remove them from your settings file.

IDE Sync

To load a subset of your project in the IDE, list the projects you want to work on in gradle/ide-projects.txt:

:feature-a
...

On next sync, only :feature-a and other projects you choose will be be loaded. The transitive dependencies of these target projects are also loaded (because sync will fail otherwise), but you do not need to identify them yourself. The plugin statically parses your build.gradle(.kts) files at the start of the build to determine the dependency graph.

Task Execution

Spotlight will infer the projects necessary to include in the build from the task request list and the project directory (specified with the -p/--project-dir flags).

For example, running ./gradlew :help will only add the root project. Running something like ./gradlew :example:assemble will only add the projects required by the dependency graph of :example.

When the -p/--project-dir flag is used, Spotlight will expand the list of child projects at the specified directory and add the projects required by their dependency graphs to the build. This enables you to run something like ./gradlew -p example check in a subdirectory.

Implicit rules

It is not uncommon for a conventions plugin setup to add a default set of utilities/testing project dependencies to each project in a build. By default, Spotlight is not able to detect these implicit dependencies added to your projects by other build logic or plugins because those do not appear in your buildscripts.

A config file option is provided to configure some pattern matching rules based on project paths or buildscript contents to implicitly add other projects:

// gradle/spotlight-rules.json
{
  "implicitRules": [
    // Add :tsunami-sea as a dependency to all projects with a path matching ":rotoscope:.*"
    // The pattern strings are regexes
    {
      "type": "project-path-match-rule",
      "pattern": ":rotoscope:.*",
      "includedProjects": [
        ":tsunami-sea"
      ]
    },
    // Add :eternal-blue to any project applying the `com.example.android` convention plugin
    {
      "type": "buildscript-match-rule",
      "pattern": "id 'com.example.android'",
      "includedProjects": [
        ":eternal-blue",
        ":singles-collection"
        // multiple includes can be given for a pattern
      ]
    }
  ]
}

Implicit rules apply to all Gradle invocations (sync and task execution).

If you are using the buildscript-utils package by itself, you can read this rules list using the SpotlightRulesList class.

Type-safe Project Accessors

Spotlight automatically parses type-safe project accessors in your buildscripts and maps them to the corresponding project paths. This works with any project naming convention.

Important

Don't use type-safe project accessors with Kotlin buildscripts. Doing so causes more configuration cache misses when Spotlight is being used.

Useful Tasks

Spotlight provides several tasks for managing its config files:

  • ./gradlew :checkAllProjectsList - Check that the all-projects.txt file is correct
    • Verifies alphabetic sorting
    • Verifies that settings.gradle(.kts) does not have any includes
    • Validates that all listed projects have build files
    • Ensures all projects discovered via dependency graph are listed
  • ./gradlew :fixAllProjectsList - Auto-fix issues in the all-projects.txt file
    • Migrates any include statements from settings.gradle(.kts) to all-projects.txt
    • Removes invalid projects (those without build files)
    • Adds missing projects discovered via dependency graph
    • Sorts the file alphabetically

Differences from Focus

Unlike Focus, which configures your gradle project to select which projects get synced using the :createFocusSettings task provided by the plugin, Spotlight relies on parsing of your buildscripts with regexes to compute the dependency graph, which is much faster.

Spotlight does not include any Gradle tasks to manage your all-projects.txt or ide-projects.txt lists, and instead relies on external tooling (IDE plugin, shell command) to avoid invoking Gradle.

Differences from dependency-explorer

dependency-explorer runs completely outside Gradle, and users must rerun the build graph query whenever their dependency graph changes to avoid errors during IDE sync. Both dependency-explorer and Spotlight use similar approaches for parsing the build graph, but Spotlight just does it automatically inside a settings plugin.

Limitations

This plugin assumes you have a "nice" Gradle build that doesn't do cross-project configuration. Please read "Herding Elephants" and "Stampeding Elephants" for more thoughts on this topic.

Dependency declarations in buildscripts must be formatted to each be on a single line. The following example would not be parsed by the regexes used:

dependencies {
  implementation(
    project(
      ':feature-a'
    )
  )
}

The projectDir for projects listed in all-projects.txt cannot be relocated because the list of all your projects is now a flat text file and not a dynamic script.

You can still add includes to settings.gradle(.kts) in your build outside of this plugin. Those will be flagged by the :checkAllProjectsList lint task, but you can move them to a new script plugin that you apply in settings to "hide" that from the lint check.

About

A gradle plugin to make management of large gradle builds easier

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 5

Languages