Crimix / ChangedProjectsTaskPlugin

A Gradle plugin to run a user defined task on changed projects (modules) and their dependent projects (modules)
https://plugins.gradle.org/plugin/io.github.crimix.changed-projects-task
MIT License
8 stars 2 forks source link
gradle-plugin

Changed Projects Task Plugin

A Gradle plugin to run a user defined task on changed projects (modules) and their dependent projects (modules) based on git changes. This is based on either the HEAD commit, a specific commit id or even a commit range.

Installation

Recommended way is to apply the plugin to the root build.gradle in the plugins block

plugins {
    id 'io.github.crimix.changed-projects-task' version 'VERSION'
}

and then configure the plugin using the following block still in the root build.gradle

changedProjectsTask {
    taskToRun = "test" //One of the main use cases of the plugin
    alwaysRunProject = [
            ":some-other-project"
    ]
    affectsAllRegex = [
            ~'build.gradle$' //Changes to the root build.gradle affects all projects
    ]
    ignoredRegex = [
            ~'^.*([.]css|[.]html)$' //Ignore changes to front-end files
    ]
}

Configuration

As seen above, there are a few different configuration options available

Option Explanation
debugLogging Is default false and can be left out.
If true will print details during plugin configuration and execution.
taskToRun A name of a task to run on changed projects.
alwaysRunProject A set of string for project paths starting with : that will be run always when there is a not ignored changed file.
neverRunProject A set of string for project paths starting with : that will never be run, even it is changed or affectsAllRegex has been evaluated to true.
affectsAllRegex A set of regexes that if any file matches will cause the taskToRun to be executed for all projects.
ignoredRegex A set of regexes for files that are ignored when evaluating if any project has changed.
changedProjectsMode A string that denotes which mode the plugin is running in, either ONLY_DIRECTLY or INCLUDE_DEPENDENTS.

INCLUDE_DEPENDENTS is the default and causes the taskToRun to be executed for project that are changed and projects that depends on those changed.

ONLY_DIRECTLY causes the taskToRun to only be executed for projects that are changed and only those.

Usage

To use the added runTaskForChangedProjects from this plugin you need to run it with a few parameters. The minimum required is -PchangedProjectsTask.run or -PchangedProjectsTask.runCommandLine which enables the plugin to run (See the differance below).
Depending on usage, it might also be a good idea to run it with --continue such that all dependent tasks are run, instead of fail-fast behaviour. Then there are four other optional parameters -PchangedProjectsTask.taskToRun, -PchangedProjectsTask.commit, -PchangedProjectsTask.prevCommit and -PchangedProjectsTask.compareMode.

If either -PchangedProjectsTask.commit and -PchangedProjectsTask.prevCommit is not specified when running the runTaskForChangedProjects command, then that option simply defaults to HEAD if it is allowed to by the logic, otherwise an error is thrown.

The following table illustrates the allowed and available options and how the resulting diff command looks

Mode Current Previous Git diff command
commit git diff --name-only HEAD~ HEAD
commit curr git diff --name-only curr~ curr
commit curr prev git diff --name-only prev~ curr
branch curr prev git diff --name-only prev curr
branch prev git diff --name-only prev HEAD
branchTwoDotted curr prev git diff --name-only prev..curr
branchTwoDotted prev git diff --name-only prev..
branchThreeDotted curr prev git diff --name-only prev...curr
branchThreeDotted prev git diff --name-only prev...

Example for evaluating the plugin

This is a basic example you can use to evaluate the plugin on your project, apply the following to your own root build.gradle.

plugins {
    id 'io.github.crimix.changed-projects-task' version 'VERSION'
}

allprojects {
    task print {
        doLast {
            println ">> " + project.path
        }
    }
}

changedProjectsTask {
    taskToRun = "print"
}

Then run the following Gradle command line
runTaskForChangedProjects -PchangedProjectsTask.run

This example will print the path of all the projects that is affected by some change and write Task x:print SKIPPED for those not affected.
You can use this to test how the plugin works and also set up the configuration of the plugin using real-world changes in your project.
This way to can skip running time-consuming task like test when you are just configuring the plugin.

Local Development

To develop and test locally with your own projects there are a few changes needed to be made.

  1. First add the following to settings.gradle file in the root project
    pluginManagement {
    repositories {
      mavenLocal()
      gradlePluginPortal()
    }
    }
    buildscript {
    configurations.all {
      resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
    }
    }
  2. Change the version inside of build.gradle in ChangedProjectsTaskPlugin to be x.y-SNAPSHOT
  3. Change the plugin version used in your project to be x.y-SNAPSHOT
  4. Do a refresh / sync of Gradle

Local debugging

To debug the plugin on your project, first checkout the repo and set breakpoints. Then you start the project task as the following in your project ./gradlew runTaskForChangedProjects -PchangedProjectsTask.run -Dorg.gradle.debug=true --no-daemon

Then the Gradle task will wait for you to connect a debugger, this can be done using the GradleRemoteDebug run configuration from this repo.

FAQ

Dependent task execution stops on first failed

Normally Gradle uses a fail-fast approach except for the test task. This means that if a dependent task fails the build stops. Depending on the use case it can be preferable, but if this plugin is used to skip unit tests, the wanted behavior will probably to execute all test tasks.

The way to get the wanted behaviour is to run the task as the following

--continue runTaskForChangedProjects -PchangedProjectsTask.run

This caused Gradle to execute all tasks even if the fail and still report the build as failed when it is done. This way it is possible to run all dependent tasks and get all unit test results to present to the user.

Task '.run' not found in root project

If you encounter any issue running the commands with the -PchangedProjectsTask.run parameter, it might be because you are using PowerShell on Windows which needs double quotes around such parameters.

Why did I make this

I have for at least a month been looking for a plugin or way to do this in Gradle. I have found a few interesting articles and plugins/code snippets, but none that worked out-of-the-box or suited my needs.

Thus, I began to write it using plain Groovy and Gradle, but when I was nearly done I stopped and thought for a moment, because all the embedded filtering and logic could be removed and put into a configuration instead, such that others could use it. Leading to this plugin being created