1. Introduction

Enforce build and project settings. A port of the popular maven-enforcer-plugin.

2. Usage

2.1. Applying the plugin

There are two modes for applying the plugin: build and project

Build

Add the following to your settings.gradle file

buildscript {
    repositories {
        jcenter()
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    // configure rules
}
Project

Add the following to a project build file (build.gradle)

Option #1

buildscript {
    repositories {
        jcenter()
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.project-enforcer'

enforce {
    // configure rules
}

Option #2

plugins {
    id 'org.kordamp.gradle.project-enforcer' version '0.3.0'
}

enforce {
    // configure rules
}

2.2. Requirements

Java 8 and Gradle 5 are the minimum requirements to use this plugin.

2.3. Rule DSL

There are two variations of the Rule DSL depending on which mode (build or project) you are running the plugin

Build
enforce {
    configure()
    rule()
    allprojects {
        rule()
    }
    project() {
        rule()
    }
    projects() {
        rule()
    }
}
Project
enforce {
    configure()
    rule()
}
Methods

void configure(Class<? extends Action<? extends BuildEnforcerExtension>> configurerClass)
Configures build rules based on a classpath resource. Useful for sharing rule definitions across different builds.

void configure(Class<? extends Action<? extends ProjectEnforcerExtension>> configurerClass)
Configures project rules based on a classpath resource. Useful for sharing rule definitions across different builds.

<R extends EnforcerRule> void rule(Class<R> ruleType)
<R extends EnforcerRule> void rule(Class<R> ruleType, Action<R> configurer)
Defines a rule for the build. Use the configurer variant if you need to configure the rule. Note that this configuration will be applied lazily, that is, when the rule is about to be invoked.
Rules will be executed in the following phases: BEFORE_BUILD, BEFORE_PROJECT, AFTER_PROJECT, PROJECTS_EVALUATED, AFTER_BUILD.

<R extends EnforcerRule> void rule(Class<R> ruleType)
<R extends EnforcerRule> void rule(Class<R> ruleType, Action<R> configurer)
Defines a rule for the current project. Use the configurer variant if you need to configure the rule. Note that this configuration will be applied lazily, that is, when the rule is about to be invoked.
Rules will be executed in the following phases: BEFORE_PROJECT, AFTER_PROJECT.

void allprojects(Action<? extends EnforcerRuleConfiguration> configurer)
Configures rules for all projects. Rules will be executed in the following phases: BEFORE_PROJECT, AFTER_PROJECT.

void project(String projectPath, Action<? extends EnforcerRuleConfiguration> configurer)
Configures rules for a single project. Rules will be executed in the following phases: BEFORE_PROJECT, AFTER_PROJECT.

void projects(List<String> projectPaths, Action<? extends EnforcerRuleConfiguration> configurer)
Configures rules for a group of projects. Rules will be executed in the following phases: BEFORE_PROJECT, AFTER_PROJECT.

Properties

The DSL exposes the following properties regardless of the mode choice

Name Type Default Required Description

enabled

Property<Boolean>

true

Enables or disables all rules.

failFast

Property<Boolean>

true

Fails and reports the first rule violation if set to true otherwise reports all rule violations within the same phase.

mergeStrategy

MergeStrategy

MergeStrategy.OVERRIDE

Controls how duplicate rule definitions should be handled.

2.4. MergeStrategy

The mergeStrategy property takes effect when duplicate rule definitions are encountered during the configuration phase. Duplicate rule definitions may occur when configuring the enforcer using any of the two configure() variants. Once a value for this property is set it cannot be changed, thus it’s a good idea to set it in your build before invoking configure() if you need your build to have a choice in how duplicates should be handled.

Values
OVERRIDE

The last configuration action wins. All previous configuration(s) (if any) will be discarded.

APPEND

Executes all configurations on a single rule instance, in FIFO order.

PREPEND

Executes all configurations on a single rule instance, in LIFO order.

DUPLICATE

Creates a duplicate rule with no shared configuration.

DENY

Does not allow configuration to be changed. First (if any) wins.

2.5. Enforcer Phase

Rules are invoked during phases. A single rule may trigger for more than one phase during a build. The following list shows all possible values and the order of invocation.

Values
BEFORE_BUILD

After Settings have been evaluated and before any projects are loaded.

BEFORE_PROJECTS

When projects have been loaded and before any is evaluated.

BEFORE_PROJECT

When a project is about to be evaluated.

AFTER_PROJECT

When a project has been evaluated.

AFTER_PROJECTS

When all projects have been evaluated.

AFTER_BUILD

When the build finishes.

If rules are configured using the org.kordamp.gradle.project-enforcer plugin then they will only trigger during the BEFORE_PROJECT (if project is not Root), AFTER_PROJECT, AFTER_PROJECTS, and AFTER_BUILD phases.

2.6. Version Ranges

Some rules require a version range. The following table describes the supported formats and their meanings

Range Meaning

1.0

x >= 1.0

(,1.0]

x ⇐ 1.0

(,1.0)

x < 1.0

[1.0]

x == 1.0

[1.0,)

x >= 1.0

(1.0,)

x > 1.0

(1.0,2.0)

1.0 < x < 2.0

[1.0,2.0]

1.0 ⇐ x ⇐ 2.0

(,1.0],[1.2,)

x ⇐ 1.0 or x >= 1.2. Multiple sets are comma-separated

(,1.1),(1.1,)

x != 1.1

2.7. System Properties

The behavior of the enforcer and configured rules can be changed with the following System properties

enforcer.enabled

Enables of disables all enforcer rules.

enforcer.fail.fast

Fails and reports the first rule violation if set to true otherwise reports all rule violations within the same phase.

enforcer.phase.<phase-name>.enabled

Enables or disables all rules in the given phase. The value of phase-name must be any of the enforcer phases, in lower case; a . may be used instead of _.

<rule-class-name>.enabled

Enables or disables a specific rule. The value of rule-class-name is the fully qualified classname of the rule.

2.8. Comparison to Maven

The following table shows rules available to both plugins

Rule Maven Gradle

AlwaysFail

AlwaysPass

BanCircularDependencies

BanDistributionManagement

BanDuplicateClasses

BanDuplicatePomDependencyVersions

BannedDependencies

BannedPlugins

BannedRepositories

BanTransitiveDependencies

DependencyConvergence

EnforceBytecodeVersion

EvaluateBeanshell

ExcludeDependencies

ForceDependencies

ReactorModuleConvergence

RequireActiveProfile

RequireContributorRoles

RequireDeveloperRoles

RequireEncoding

RequireEnvironmentVariable

RequireFileChecksum

RequireFilesDontExist

RequireFilesExist

RequireFilesSize

RequireGradleProperty

RequireGradleVersion

RequireJavaVersion

RequireMavenVersion

RequireNoRepositories

RequireOS

RequirePluginVersions

RequirePrerequisite

RequireProfileIdsExist

RequireProperty

RequirePropertyDiverges

RequireReleaseDeps

RequireReleaseVersion

RequireSnapshotVersion

RequireSameVersions

RequireSystemProperty

RequireUpperBoundDeps

3. Rules

Properties

All rules share the following properties

Name Type Default Required Description

enabled

Property<Boolean>

true

Enables or disables the rule. Can be overridden using a System property whose key follows the pattern <fullyQualifiedClassName>.enabled.

The following icons indicate the required state of a property:
required
optional
conditionally required

3.1. AlwaysFail

This rule will always fail. It is useful for testing proper plugin configuration.

3.1.1. Trigger Phases

  • BEFORE_BUILD

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

  • AFTER_PROJECT

  • AFTER_PROJECTS

  • AFTER_BUILD

3.1.2. Configuration

enforce {
    rule(enforcer.rules.AlwaysFail) { r ->
        r.enabled
    }
}

3.1.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

3.2. AlwaysPass

This rule will always succeed. It is useful for testing proper plugin configuration.

3.2.1. Trigger Phases

  • BEFORE_BUILD

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

  • AFTER_PROJECT

  • AFTER_PROJECTS

  • AFTER_BUILD

3.2.2. Configuration

enforce {
    rule(enforcer.rules.AlwaysPass) { r ->
        r.enabled
    }
}

3.2.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

3.3. BanDuplicateClasses

This rule checks the dependencies and fails if any class is present in more than one dependency.

The following exclusions are enabled by default: module-info, META-INF/versions/*/module-info
This rule will resolve configurations eagerly, once the target project or projects have been evaluated.

3.3.1. Trigger Phases

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.3.2. Configuration

enforce {
    rule(enforcer.rules.BanDuplicateClasses) { r ->
        r.enabled
        r.message
        r.findAllDuplicates
        r.ignoreWhenIdentical
        r.configurations
        r.ignoreClasses
        r.ignore()
        r.dependencies
        r.dependency() { d ->
            d.ignore()
        }
    }
}
Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

findAllDuplicates

Property<Boolean>

false

Indicate whether the rule should find all duplicates or fail fast at the first duplicate.

ignoreWhenIdentical

Property<Boolean>

false

When true indicates duplicate classes don’t fail the build when their bytecode exactly matches each other.

configurations

ListProperty<String>

[]

Only verify dependencies within these configurations. Unresolvable configurations will be ignored. This rule applies
to all resolvable configurations if this property is left empty.

ignoreClasses

ListProperty<String>

[]

A list of classes to ignore duplicates of. Wildcards can be specified using the * character.

dependencies

ListProperty<Dependency>

[]

A list of dependencies for which you want to ignore specific classes.

3.3.3. Methods

ignore(String str)
Adds a class to ignore duplicates of. Wildcards can be specified using the * character.

dependency(String str, Action<? extends Dependency> configurer)
dependency(Map<String, String> map, Action<? extends Dependency> configurer)
Adds an explicit dependency from which classes will be ignored of. Wildcards can be specified using the * character.
Accepted format for str is groupId:artifactId:version[:classifier].
Accepted keys for map are groupId, artifactId, version, classifier.

3.3.4. Example

enforce {
    rule(enforcer.rules.BanDuplicateClasses) { r ->
        // search only on compile and runtime classpaths
        r.configurations.addAll(['compileClasspath', 'runtimeClasspath'])
        // ignore all classes under the following package
        r.ignore('org.log4j.impl.*')
        // ignore classes from commons-codec
        r.dependency('commons-codec:commons-codec:1.14') { d ->
            // ignore all classes under a specific package
            d.ignore('org.apache.commons.codec.cli.*)
        }
    }
}

3.4. BannedDependencies

This rule checks the dependencies and fails if any of the matching excludes are found.

This rule will resolve configurations eagerly, once the target project or projects have been evaluated.

3.4.1. Trigger Phases

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.4.2. Configuration

enforce {
    rule(enforcer.rules.BannedDependencies) { r ->
        r.enabled
        r.configurations
        r.excludes
        r.includes
        r.exclude()
        r.include()
    }
}

3.4.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to the user if the rule fails.

configurations

ListProperty<String>

[]

Only verify dependencies within these configurations. Unresolvable configurations will be ignored. This rule applies
to all resolvable configurations if this property is left empty.

excludes

ListProperty<String>

[]

A list of artifacts to ban. The format is groupId[:artifactId][:version][:classifier] where artifactId, version,
and classifier are optional. Wildcards may be used to replace an entire or just parts of a section.

includes

ListProperty<String>

[]

A list of artifacts to include. These are exceptions to the excludes. It is meant to allow wide exclusion rules with
wildcards and fine tune using includes. If nothing has been excluded, then the includes have no effect. In other words,
includes only subtract from artifacts that matched an exclude rule.
For example, to ban all xerces except xerces-api you would exclude "xerces" (groupId) and include "xerces:xerces-api"

Artifact pattern examples:

  • org.apache.maven

  • org.apache.maven:badArtifact

  • org.apache.maven:artifact:badVersion

  • org.apache.maven:*:1.2 (exclude version 1.2 and above, equivalent to [1.2,) )

  • org.apache.maven:*:[1.2] (explicit exclude of version 1.2)

  • org.apache.maven:*:*:test

  • org.apache.*:maven-*:*

3.4.4. Methods

exclude(String str)
Add a dependency pattern to be excluded.

include(String str)
Add a dependency pattern to be included.

3.4.5. Example

enforce {
    rule(enforcer.rules.BannedDependencies) { r ->
        // search only on compile and runtime classpaths
        r.configurations.addAll(['compileClasspath', 'runtimeClasspath'])
        // ignore all classes under the following package
        r.exclude('org.apache.maven')
        r.exclude('org.apache.maven:badArtifact')
        r.exclude('*:badArtifact')
        // only 1.0 of badArtifact is allowed
        r.include('org.apache.maven:badArtifact:1.0')
    }
}

3.5. BannedRepositories

This rule checks whether the build or project include a specified banned repository.

3.5.1. Trigger Phases

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.5.2. Configuration

enforce {
    rule(enforcer.rules.BannedRepositories) { r ->
        r.enabled
        r.mavenLocalAllowed
        r.bannedRepositories
        r.allowedRepositories
    }
}
Name Type Default Required Description

mavenLocalAllowed

Property<Boolean>

true

Whether usage of the local Maven repository is allowed or not.

bannedRepositories

ListProperty<String>

[]

Specify banned non-plugin repositories. This is a black list of http/https url patterns.

allowedRepositories

ListProperty<String>

[]

Specify explicitly allowed non-plugin repositories. This is a white list of http/https url patterns.

3.5.3. Example

enforce {
    rule(enforcer.rules.BannedRepositories) { r ->
        // ban the following repository
        r.bannedRepositories.add('http://repo1/*')
        // for some cases, white list is more effective
        r.allowedRepositories.add('http://repo2/*')
    }
}

3.6. DependencyConvergence

This rule requires that dependency version numbers converge. If a project has two dependencies, A and B, both depending on the same artifact, C, this rule will fail the build if A depends on a different version of C than the version of C depended on by B.

This rule also configures additional settings applicable to the resolutionStrategy property of all Configuration instances that can be resolved.

This rule will resolve configurations eagerly, once the target project or projects have been evaluated.

3.6.1. Trigger Phases

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.6.2. Configuration

enforce {
    rule(enforcer.rules.DependencyConvergence) { r ->
        r.enabled
        r.failOnDynamicVersions
        r.failOnChangingVersions
        r.failOnNonReproducibleResolution
        r.activateDependencyLocking
        r.deactivateDependencyLocking
    }
}

3.6.3. Properties

Name Type Default Required Description

failOnDynamicVersions

Property<Boolean>

false

Gradle will make sure that no dynamic version was used in the resulting dependency graph. In practice, it means that if
the resolved dependency graph contains a module and that the versions participating in the selection of that module
contain at least one dynamic version, then resolution will fail if the resolution result can change because of this
version selector. This can be used in cases you want to make sure your build is reproducible, without relying on
dependency locking.
Requires Gradle 6.1+

failOnChangingVersions

Property<Boolean>

false

Gradle will make sure that no changing version participates in resolution. This can be used in cases you want to make
sure your build is reproducible, without relying on dependency locking.
Requires Gradle 6.1+

failOnNonReproducibleResolution

Property<Boolean>

false

Configures Gradle to fail the build is the resolution result is expected to be unstable, that is to say that it includes
dynamic versions or changing versions and therefore the result may change depending on when the build is executed.
Setting this property is equivalent to settings both failOnDynamicVersions and failOnChangingVersions.
Requires Gradle 6.1+

activateDependencyLocking

Property<Boolean>

false

Activates dependency locking support in Gradle. Once turned on on a configuration, resolution result can be saved and
then reused for subsequent builds. This enables reproducible builds when using dynamic versions.

deactivateDependencyLocking

Property<Boolean>

false

Deactivates dependency locking support in Gradle.
Requires Gradle 6.0+

3.6.4. Example

Given the following configuration found in settings.gradle

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.DependencyConvergence)
}

And a simple project

build.gradle
plugins {
    id 'java-library'
}

repositories {
    jcenter()
}

dependencies {
    api 'org.slf4j:slf4j-jdk14:1.6.1'
    api 'org.slf4j:slf4j-nop:1.6.0'
}

The build will fail with the following message

FAILURE: Build failed with an exception.

* What went wrong:
Could not resolve all dependencies for configuration ':compileClasspath'.
> Conflict(s) found for the following module(s):
    - org.slf4j:slf4j-api between versions 1.6.1 and 1.6.0
  Run with:
      --scan or
      :dependencyInsight --configuration compileClasspath --dependency org.slf4j:slf4j-api
  to get more insight on how to solve the conflict.

You’ll have to temporarily disable this rule in order to invoke the suggested command, like so

$ gradle -Denforcer.rules.DependencyConvergence.enabled=false \
         :dependencyInsight --configuration compileClasspath \
          --dependency org.slf4j:slf4j-api

> Task :dependencyInsight
org.slf4j:slf4j-api:1.6.1
   variant "compile" [
      org.gradle.status              = release (not requested)
      org.gradle.usage               = java-api
      org.gradle.libraryelements     = jar (compatible with: classes)
      org.gradle.category            = library (not requested)

      Requested attributes not found in the selected variant:
         org.gradle.dependency.bundling = external
         org.gradle.jvm.version         = 8
   ]
   Selection reasons:
      - By conflict resolution : between versions 1.6.1 and 1.6.0

org.slf4j:slf4j-api:1.6.1
\--- org.slf4j:slf4j-jdk14:1.6.1
     \--- compileClasspath

org.slf4j:slf4j-api:1.6.0 -> 1.6.1
\--- org.slf4j:slf4j-nop:1.6.0
     \--- compileClasspath

You may exclude the offending dependency, either by manually adding an exclusion (doing so in this example it’s trivial) or enabling the ExcludeDependencies rule (affecting all configurations). You may also force the version of slf4j-api with the ForceDependencies rule (affecting all configurations).

3.7. EnforceBytecodeVersion

This rule checks the dependencies transitively and fails if any class of any dependency is having its bytecode version higher than the one specified.

The following exclusions are enabled by default: module-info, META-INF/versions/(\d+)/.*
This rule will resolve configurations eagerly, once the target project or projects have been evaluated.

3.7.1. Trigger Phases

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.7.2. Configuration

enforce {
    rule(enforcer.rules.EnforceBytecodeVersion) { r ->
        r.enabled
        r.maxJdkVersion
        r.maxJavaMajorVersionNumber
        r.maxJavaMinorVersionNumber
        r.ignoreClasses
        r.includes
        r.excludes
        r.configurations
        r.showErrors
        r.ignore()
        r.include()
        r.exclude()
    }
}

3.7.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

maxJdkVersion

Property<String>

The maximum target jdk version in the 1.x form (e.g. 1.6, 1.7, 1.8, 1.9 or 6, 7, 8, 9, 10, 11…​).

maxJavaMajorVersionNumber

Property<Integer>

An integer indicating the maximum bytecode major version number (cannot be specified if maxJdkVersion is present).

maxJavaMinorVersionNumber

Property<Integer>

0

An integer indicating the maximum bytecode minor version number (cannot be specified if maxJdkVersion is present)

excludes

ListProperty<String>

[]

An optional list of artifacts to exclude. The format is groupId[:artifactId][:version][:classifier] where artifactId,
version, and classifier are optional. Wildcards may be used to replace an entire or just parts of a section.

includes

ListProperty<String>

[]

An optional list of artifacts to include. These are exceptions to the excludes. It is meant to allow wide exclusion rules with
wildcards and fine tune using includes. If nothing has been excluded, then the includes have no effect. In other words,
includes only subtract from artifacts that matched an exclude rule.
For example, to ban all xerces except xerces-api you would exclude "xerces" (groupId) and include "xerces:xerces-api"

configurations

ListProperty<String>

[]

Only verify dependencies within these configurations. Unresolvable configurations will be ignored. This rule applies
to all resolvable configurations if this property is left empty.

ignoreClasses

ListProperty<String>

[]

A list of classes to ignore. Wildcards can be specified using the * character.

showErrors

Property<Boolean>

false

The rule stops on the first violation. Set this property to true if you want to see all violations.

Artifact pattern examples:

  • org.apache.maven

  • org.apache.maven:badArtifact

  • org.apache.maven:artifact:badVersion

  • org.apache.maven:*:1.2 (exclude version 1.2 and above, equivalent to [1.2,) )

  • org.apache.maven:*:[1.2] (explicit exclude of version 1.2)

  • org.apache.maven:*:*:test

  • org.apache.*:maven-*:*

3.7.4. Methods

ignore(String str)
Adds a class to ignore for verification. Wildcards can be specified using the * character.

exclude(String str)
exclude(Map<String, String> map)
Adds a dependency pattern from which classes will be excluded for verification. Wildcards can be specified using the * character.
Accepted format for str is [groupId]:[artifactId]:[version]:[classifier].
Accepted keys for map are groupId, artifactId, version, classifier.

include(String str)
include(Map<String, String> map)
Adds a dependency pattern from which classes will be included for verification. Wildcards can be specified using the * character.
Accepted format for str is [groupId]:[artifactId]:[version]:[classifier].
Accepted keys for map are groupId, artifactId, version, classifier.

3.7.5. Example

Given the following configuration found in settings.gradle

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.EnforceBytecodeVersion) { r ->
        r.maxJdkVersion = '1.8'
    }
}

And a simple project

build.gradle
plugins {
    id 'java-library'
}

repositories {
    jcenter()
}

dependencies {
    api 'org.kordamp.ikonli:ikonli-javafx:11.4.0'
}

Running the build will fail with the following message

FAILURE: Build failed with an exception.

* What went wrong:
[AFTER_PROJECTS] A Enforcer rule has failed
>
  Enforcer rule 'enforcer.rules.EnforceBytecodeVersion' was triggered.
  Found Banned Dependency: org.kordamp.ikonli:ikonli-javafx:11.4.0
  Found Banned Dependency: org.kordamp.ikonli:ikonli-core:11.4.0
  Disable this rule temporarily with -Denforcer.rules.EnforceBytecodeVersion.enabled=false and
  invoke 'dependencyInsight' or 'dependencies' to locate the source of the banned dependencies.

If the enforcer configuration is changed to show all errors

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.EnforceBytecodeVersion) { r ->
        r.maxJdkVersion = '1.8'
        r.showErrors = true
    }
}

We get a better picture on why these dependencies cause the build to fail when running the build once more

[build-enforcer] Restricted to JDK 1.8 yet ikonli-javafx-11.4.0.jar (org.kordamp.ikonli:ikonli-javafx:11.4.0) contains org/kordamp/ikonli/javafx/FontIcon$1.class targeted to JDK 11
[build-enforcer] Restricted to JDK 1.8 yet ikonli-core-11.4.0.jar (org.kordamp.ikonli:ikonli-core:11.4.0) contains org/kordamp/ikonli/Ikon.class targeted to JDK 11

FAILURE: Build failed with an exception.

* What went wrong:
[AFTER_PROJECTS] A Enforcer rule has failed
>
  Enforcer rule 'enforcer.rules.EnforceBytecodeVersion' was triggered.
  Found Banned Dependency: org.kordamp.ikonli:ikonli-javafx:11.4.0
  Found Banned Dependency: org.kordamp.ikonli:ikonli-core:11.4.0
  Disable this rule temporarily with -Denforcer.rules.EnforceBytecodeVersion.enabled=false and
  invoke 'dependencyInsight' or 'dependencies' to locate the source of the banned dependencies.

3.8. ExcludeDependencies

This rule excludes dependencies from all resolvable configurations.

3.8.1. Trigger Phases

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

3.8.2. Configuration

enforce {
    rule(enforcer.rules.ExcludeDependencies) { r ->
        r.enabled
        r.exclude()
    }
}

3.8.3. Methods

exclude(String str)
exclude(Map<String, String> map)
Adds an dependency pattern for exclusion. Wildcards can be specified using the * character.
Accepted format for str is [groupId]:[artifactId]:[version]:[classifier].
Accepted keys for map are groupId, artifactId, version, classifier.

3.8.4. Example

Given the following configuration found in settings.gradle

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.DependencyConvergence)
}

And a simple project

build.gradle
plugins {
    id 'java-library'
}

repositories {
    jcenter()
}

dependencies {
    api 'org.slf4j:slf4j-jdk14:1.6.1'
    api 'org.slf4j:slf4j-nop:1.6.0'
}

The build will fail with the following message

FAILURE: Build failed with an exception.

* What went wrong:
Could not resolve all dependencies for configuration ':compileClasspath'.
> Conflict(s) found for the following module(s):
    - org.slf4j:slf4j-api between versions 1.6.1 and 1.6.0
  Run with:
      --scan or
      :dependencyInsight --configuration compileClasspath --dependency org.slf4j:slf4j-api
  to get more insight on how to solve the conflict.

We can force an exclusion for org.slf4j:slf4j-api:1.6.0

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.DependencyConvergence)
    rule(enforcer.rules.ExcludeDependencies) { r ->
        r.exclude('org.slf4j:slf4j-api:1.6.0')
    }
}

3.9. ForceDependencies

This rule forces dependency versions for all resolvable configurations.

3.9.1. Trigger Phases

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

3.9.2. Configuration

enforce {
    rule(enforcer.rules.ForceDependencies) { r ->
        r.enabled
        r.dependencies
    }
}

3.9.3. Properties

Name Type Default Required Description

dependencies

ListProperty<Object>

[]

A list of dependencies to be forced. Accepted formats are the same when declaring dependencies in a configuration.

3.9.4. Example

Given the following configuration found in settings.gradle

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.DependencyConvergence)
}

And a simple project

build.gradle
plugins {
    id 'java-library'
}

repositories {
    jcenter()
}

dependencies {
    api 'org.slf4j:slf4j-jdk14:1.6.1'
    api 'org.slf4j:slf4j-nop:1.6.0'
}

The build will fail with the following message

FAILURE: Build failed with an exception.

* What went wrong:
Could not resolve all dependencies for configuration ':compileClasspath'.
> Conflict(s) found for the following module(s):
    - org.slf4j:slf4j-api between versions 1.6.1 and 1.6.0
  Run with:
      --scan or
      :dependencyInsight --configuration compileClasspath --dependency org.slf4j:slf4j-api
  to get more insight on how to solve the conflict.

We can force the version of org.slf4j:slf4j-api to be 1.6.1

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.DependencyConvergence)
    rule(enforcer.rules.ForceDependencies) { r ->
        r.dependencies.add('org.slf4j:slf4j-api:1.6.1')
    }
}

Then running the build to verify that the version has been forced

$ gradle dependencies --configuration=compileClasspath

> Task :dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

compileClasspath - Compile classpath for source set 'main'.
+--- org.slf4j:slf4j-jdk14:1.6.1
|    \--- org.slf4j:slf4j-api:1.6.1
\--- org.slf4j:slf4j-nop:1.6.0
     \--- org.slf4j:slf4j-api:1.6.0 -> 1.6.1

(*) - dependencies omitted (listed previously)

3.10. RequireEnvironmentVariable

This rule checks that a specified environment variable is set.

3.10.1. Trigger Phases

  • BEFORE_BUILD

  • AFTER_PROJECT

3.10.2. Configuration

enforce {
    rule(enforcer.rules.RequireEnvironmentVariable) { r ->
        r.enabled
        r.message
        r.variableName
        r.regex
        r.regexMessage
        r.displayValue
    }
}

3.10.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

variableName

Property<String>

The name of the environment variable to be checked for.

regex

Property<String>

A regular expression used to check the value of the variable.
The regex is applied to the entire value of the variable (i.e. using the regex "match" method), and not just a substring
of the variable’s value.

regexMessage

Property<String>

An optional message to the user if the regex check fails.

displayValue

Property<Boolean>

true

Displays the value to the user if the regex check fails.

after

Property<Boolean>

false

Whether to check during the BEFORE_* or AFTER_* phases.

3.10.4. Example

enforce {
    rule(enforcer.rules.RequireEnvironmentVariable) { r ->
        r.variableName = 'SECRET_TOKEN'
        r.regex = '[A-Za-z0-9]{16}'
        r.displayValue = false
    }
}

3.11. RequireFileChecksum

This rule checks that the specified file has an given checksum.

3.11.1. Configuration

enforce {
    rule(enforcer.rules.RequireFileChecksum) { r ->
        r.enabled
        r.message
        r.file
        r.checksum
        r.type
        r.phases
    }
}

3.11.2. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

file

Property<File>

The file to be checked.

checksum

Property<String>

The expected checksum

type

Property<String>

Type of hashing algorithm used to calculate the checksum. May be one of ['md5', 'sha1', 'sha256', 'sha384', 'sha512'].

phases

ListProperty<EnforcerPhase>

[BEFORE_BUILD]

The list of phases where the check should be run.

3.12. RequireFilesDontExist

This rule checks that the specified list of files do not exist.

3.12.1. Configuration

enforce {
    rule(enforcer.rules.RequireFilesDontExist) { r ->
        r.enabled
        r.message
        r.files
        r.allowNulls
        r.phases
        r.file()
    }
}

3.12.2. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

allowNulls

Property<Boolean>

false

If null files should be allowed. If allowed, they will be treated as if they do not exist.

files

Property<String>

A list of files to be checked.

phases

ListProperty<EnforcerPhase>

[BEFORE_BUILD]

The list of phases where the check should be run.

3.12.3. Methods

file(String str)
file(File file)
Adds a file to the list of files to be checked. Path may be relative or absolute.

3.12.4. Example

Given the following project structure

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── secrets.txt
└── settings.gradle

And the following enforcer configuration

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.RequireFilesDontExist) { r ->
        r.file('secrets.txt')
    }
}

Running the build results in an error

* What went wrong:
[BEFORE_BUILD] A Enforcer rule has failed
>
  Enforcer rule 'enforcer.rules.RequireFilesDontExist' was triggered.
  Some files should not exist:

  /Users/joe/projects/sample/secrets.txt

3.13. RequireFilesExist

This rule checks that the specified list of files exist.

3.13.1. Configuration

enforce {
    rule(enforcer.rules.RequireFilesExist) { r ->
        r.enabled
        r.message
        r.files
        r.allowNulls
        r.phases
    }
}

3.13.2. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

allowNulls

Property<Boolean>

false

If null files should be allowed. If allowed, they will be treated as if they do exist.

files

Property<String>

A list of files to be checked.

phases

ListProperty<EnforcerPhase>

[BEFORE_BUILD]

The list of phases where the check should be run.

3.13.3. Methods

file(String str)
file(File file)
Adds a file to the list of files to be checked. Path may be relative or absolute.

3.13.4. Example

Given the following project structure

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

And the following enforcer configuration

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

enforce {
    rule(enforcer.rules.RequireFilesExist) { r ->
        r.file('gradle.properties')
    }
}

Running the build results in an error

* What went wrong:
[BEFORE_BUILD] A Enforcer rule has failed
>
  Enforcer rule 'enforcer.rules.RequireFilesExist' was triggered.
  Some required files are missing:

  /Users/joe/projects/sample/gradle.properties

3.14. RequireFilesSize

This rule checks that the specified list of files exist and are within the specified size range.

3.14.1. Configuration

enforce {
    rule(enforcer.rules.RequireFilesSize) { r ->
        r.enabled
        r.message
        r.files
        r.maxsize
        r.minsize
        r.allowNulls
        r.phases
    }
}

3.14.2. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

allowNulls

Property<Boolean>

false

If null files should be allowed. If allowed, they will be treated as if they do exist.

files

Property<String>

A list of files to be checked.

maxsize

Property<Long>

10_000L

Maximum size in bytes for this file.

minsize

Property<Long>

0L

Minimum size in bytes for this file.

phases

ListProperty<EnforcerPhase>

[BEFORE_BUILD]

The list of phases where the check should be run.

3.14.3. Methods

file(String str)
file(File file)
Adds a file to the list of files to be checked. Path may be relative or absolute.

3.15. RequireGradleProperty

This rule checks that a specified Gradle property is set.

3.15.1. Trigger Phases

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.15.2. Configuration

enforce {
    rule(enforcer.rules.RequireGradleProperty) { r ->
        r.enabled
        r.message
        r.property
        r.regex
        r.regexMessage
        r.displayValue
        r.after
    }
}

3.15.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

property

Property<String>

The name of the Gradle property to be checked for.

regex

Property<String>

A regular expression used to check the value of the property.
The regex is applied to the entire value of the property (i.e. using the regex "match" method), and not just a substring
of the property’s value.

regexMessage

Property<String>

An optional message to the user if the regex check fails.

displayValue

Property<Boolean>

true

Displays the value to the user if the regex check fails.

after

Property<Boolean>

false

Whether to check during the BEFORE_* or AFTER_* phases.

3.15.4. Example

enforce {
    rule(enforcer.rules.RequireGradleProperty) { r ->
        r.property = 'somePropertyName'
        r.regex = '[A-Za-z0-9]{16}'
        r.displayValue = false
    }
}

3.16. RequireGradleVersion

This rule enforces certain Gradle versions. The rule uses the version range syntax to define allowed versions.

3.16.1. Trigger Phases

  • BEFORE_BUILD

3.16.2. Configuration

enforce {
    rule(enforcer.rules.RequireGradleVersion) { r ->
        r.enabled
        r.message
        r.version
    }
}

3.16.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

version

Property<String>

Range of allowed Gradle versions.

3.17. RequireJavaVersion

This rule enforces certain Java versions. The rule uses the version range syntax to define allowed versions.

The JDK version is retrieved and the following processing occurs before being checked:

  1. Drop all non-numeric characters preceeding the first number. (build 1.5.0_07-b03 becomes 1.5.0_07-b03)

  2. Replace all '_' and '-' with '.' (1.5.0_07-b03 becomes 1.5.0.07.b03)

  3. Remove all non digit characters "[^0-9] and convert each section using Integer.parseInt() (1.5.0_07-b03 becomes 1.5.0.7.3)

  4. Split the string on '.' and take the first 3 sections, separated by '.' and add '-' followed by the fourth section (1.5.0.7.3 becomes 1.5.0-7)

3.17.1. Trigger Phases

  • BEFORE_BUILD

3.17.2. Configuration

enforce {
    rule(enforcer.rules.RequireJavaVersion) { r ->
        r.enabled
        r.message
        r.version
    }
}

3.17.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

version

Property<String>

Range of allowed Java versions.

3.18. RequireOS

This rule can enforce certain values about the Operating System and processor architecture. Detects the same values as the os-maven-plugin.

3.18.1. Trigger Phases

  • BEFORE_BUILD

3.18.2. Configuration

enforce {
    rule(enforcer.rules.RequireOS) { r ->
        r.enabled
        r.message
        r.arch
        r.name
        r.version
        r.release
        r.classifier
        r.classifierWithLikes
        r.failOnUnknownOS
    }
}

3.18.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

arch

Property<String>

The expected OS architecture.

name

Property<String>

The expected OS name.

version

Property<String>

The expected OS version.

release

Property<String>

The expected OS release (Linux).

classifier

Property<String>

The expected OS classifier (Linux).

classifierWithLikes

ListProperty<String>

[]

Additional classifiers

failOnUnknownOS

Property<Boolean>

true

Fails the build if an unsupported OS is detected.

3.18.4. Example

The following configuration only works when the build is run on a Windows OS

settings.gradle
enforce {
    rule(enforcer.rules.RequireOS) { r ->
        r.name = 'windows'
    }
}

Running the build on a Mac results in a failed build

$ gradle help

FAILURE: Build failed with an exception.

* What went wrong:
[BEFORE_BUILD] A Enforcer rule has failed
>
  Enforcer rule 'enforcer.rules.RequireOS' was triggered.
  OS Name: osx Arch: x86_64 Version: 10.14 Classifier: osx-x86_64 is not allowed by Name=windows

3.19. RequireSystemProperty

This rule checks that a specified System property is set.

3.19.1. Trigger Phases

  • BEFORE_PROJECTS

  • BEFORE_PROJECT

  • AFTER_PROJECT

  • AFTER_PROJECTS

3.19.2. Configuration

enforce {
    rule(enforcer.rules.RequireSystemProperty) { r ->
        r.enabled
        r.message
        r.property
        r.regex
        r.regexMessage
        r.displayValue
        r.after
    }
}

3.19.3. Properties

Name Type Default Required Description

message

Property<String>

An optional message to provide when the rule fails.

property

Property<String>

The name of the System property to be checked for.

regex

Property<String>

A regular expression used to check the value of the property.
The regex is applied to the entire value of the property (i.e. using the regex "match" method), and not just a substring
of the property’s value.

regexMessage

Property<String>

An optional message to the user if the regex check fails.

displayValue

Property<Boolean>

true

Displays the value to the user if the regex check fails.

after

Property<Boolean>

false

Whether to check during the BEFORE_* or AFTER_* phases.

3.19.4. Example

enforce {
    rule(enforcer.rules.RequireSystemProperty) { r ->
        r.property = 'somePropertyName'
        r.regex = '[A-Za-z0-9]{16}'
        r.displayValue = false
    }
}

4. Authoring

4.1. Create a project

First create a basic java-library project if you don’t have one already. Choose your preferred JVM language to implement your rules.

build.gradle (Groovy)
apply plugin: 'groovy'
apply plugin: 'java-library'

group   = 'com.acme'
version = '1.2.3'

repositories {
    jcenter()
}

dependencies {
    api 'org.kordamp.gradle:enforcer-api:0.3.0'
    implementation gradleApi()
}
build.gradle (Java)
apply plugin: 'java-library'

group   = 'com.acme'
version = '1.2.3'

repositories {
    jcenter()
}

dependencies {
    api 'org.kordamp.gradle:enforcer-api:0.3.0'
    implementation gradleApi()
}

4.2. Create a Rule class

Create your rule class. The rule must implement the EnforcerRule interface. You may rely on service injection as described at Developing Custom Gradle Types. It’s suggested to use the AbstractEnforcerRule as starting point.

If the rule succeeds, it should just simply return. If the rule fails, it should throw an EnforcerRuleException with a descriptive message telling the user why the rule failed.

com/acme/enforcer/rules/MyCustomRule.groovy
package com.acme.enforcer.rules

import groovy.transform.CompileStatic
import org.gradle.api.model.ObjectFactory
import org.kordamp.gradle.plugin.enforcer.api.EnforcerContext
import org.kordamp.gradle.plugin.enforcer.api.EnforcerPhase
import org.kordamp.gradle.plugin.enforcer.api.AbstractEnforcerRule
import org.kordamp.gradle.plugin.enforcer.api.EnforcerRuleException

@CompileStatic
class MyCustomRule extends AbstractEnforcerRule {
    MyCustomRule(ObjectFactory objects) {
        super(objects)
    }

    protected void doExecute(EnforcerContext context) throws EnforcerRuleException {
        if (context.enforcerPhase == EnforcerPhase.AFTER_BUILD) {
            println "Everything went OK!"
        }
    }
}
com/acme/enforcer/rules/MyCustomRule.java
package com.acme.enforcer.rules;

import org.gradle.api.model.ObjectFactory;
import org.kordamp.gradle.plugin.enforcer.api.AbstractEnforcerRule;
import org.kordamp.gradle.plugin.enforcer.api.EnforcerContext;
import org.kordamp.gradle.plugin.enforcer.api.EnforcerPhase;
import org.kordamp.gradle.plugin.enforcer.api.EnforcerRuleException;

public class MyCustomRule extends AbstractEnforcerRule {
    public MyCustomRule(ObjectFactory objects) {
        super(objects);
    }

    protected void doExecute(EnforcerContext context) throws EnforcerRuleException {
        if (context.getEnforcerPhase().equals(EnforcerPhase.AFTER_BUILD)) {
            System.out.println("Everything went OK!");
        }
    }
}

4.3. Build

Build and Install, or Deploy your custom rule to a repository.

4.4. Configure the Enforcer plugin

On the consuming project, select which mode you want: build or project

build

settings.gradle
buildscript {
    repositories {
        gradlePluginPortal()
        // add the repository where the rule artifact is located
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
        classpath 'com.acme:my-enforcer-rules:1.2.3'
    }
}
apply plugin: 'org.kordamp.gradle.enforcer'

project

build.gradle
buildscript {
    repositories {
        gradlePluginPortal()
        // add the repository where the rule artifact is located
    }
    dependencies {
        classpath 'org.kordamp.gradle:enforcer-gradle-plugin:0.3.0'
        classpath 'com.acme:my-enforcer-rules:1.2.3'
    }
}
apply plugin: 'org.kordamp.gradle.project-enforcer'

Or alternatively

build.gradle
buildscript {
    repositories {
        // add the repository where the rule artifact is located
    }
    dependencies {
        classpath 'com.acme:my-enforcer-rules:1.2.3'
    }
}

plugins {
    id 'org.kordamp.gradle.project-enforcer' version '0.3.0'
}

4.5. Configure the rule

Finally configure the rule as you need it

enforce {
    rule(com.acme.enforcer.rules.MyCustomRule)
}