Dependency locking and varying configurations (multiple customers)

I am trying to apply dependency locking to my Java web project. The project has varying configurations depending on the target customer/environment. Up until now, we’ve had

dependencies {
....
  if (project.customer='X') {
    runtime 'org.springframework.security.kerberos:spring-security-kerberos-web:latest.release'
  }
...
}

That didn’t work with dependency locking: generating the dependency locks for a build for customer X caused the build for customer Y to fail because of missing or extra dependencies.

We have now modified this, adding a custom configuration for each customer:

configurations {
  defaultRuntime
  customerX.extendsFrom defaultRuntime
  customerY.extendsFrom defaultRuntime
}

and our dependencies block now says

dependencies {
  compile group: 'org.springframework', name: 'spring-context', version: 'latest.release', transitive:false
  ....
  defaultRuntime group: 'org.springframework', name:'spring-aop', version:'latest.release', transitive:false
  customerX 'org.springframework.security.kerberos:spring-security-kerberos-web:latest.release'

  runtime configurations["customer${project.customer}"] // This is the *only* dependency specification for "runtime"
}

We exclude “runtime*” (and “testRuntime*”) from dependency locking, but lock everything else.

The problem: “runtime” pulls in latest.release of all the dependencies, not the locked version from the customer configuration:

./gradlew :web_client:dependencyinsight --dependency org.springframework:spring-beans --configuration customerdemo

results in

org.springframework:spring-beans:4.3.23.RELEASE
   variant "runtime" [
      org.gradle.status   = release (not requested)
      org.gradle.usage    = java-runtime (not requested)
      org.gradle.category = library (not requested)
   ]
   Selection reasons:
      - By constraint : dependency was locked to version '4.3.23.RELEASE'

org.springframework:spring-beans:{strictly 4.3.23.RELEASE} -> 4.3.23.RELEASE
\--- customerdemo

org.springframework:spring-beans:4.3.23.RELEASE
+--- org.springframework:spring-oxm:4.3.23.RELEASE
|    +--- customerdemo (requested org.springframework:spring-oxm:{strictly 4.3.23.RELEASE})
|    \--- project :mock_daos (requested org.springframework:spring-oxm:latest.release)
|         \--- customerdemo
\--- org.springframework:spring-tx:4.3.23.RELEASE
     +--- customerdemo (requested org.springframework:spring-tx:{strictly 4.3.23.RELEASE})
     \--- project :mock_daos (requested org.springframework:spring-tx:latest.release) (*)

(*) - dependencies omitted (listed previously)

but

/gradlew :web_client:dependencyinsight --dependency org.springframework:spring-beans --configuration runtime

results in

> Task :web_client:dependencyInsight
org.springframework:spring-beans:5.1.6.RELEASE
   variant "runtime" [
      org.gradle.status   = release (not requested)
      org.gradle.usage    = java-runtime (not requested)
      org.gradle.category = library (not requested)
   ]

org.springframework:spring-beans:5.1.6.RELEASE
+--- org.springframework:spring-aop:5.1.6.RELEASE
|    +--- org.springframework.security:spring-security-web:5.1.5.RELEASE
|    |    \--- runtime (requested org.springframework.security:spring-security-web:latest.release)
|    +--- org.springframework.security:spring-security-config:5.1.5.RELEASE
|    |    \--- runtime (requested org.springframework.security:spring-security-config:latest.release)
|    +--- org.springframework.security:spring-security-core:5.1.5.RELEASE
|    |    +--- org.springframework.security:spring-security-web:5.1.5.RELEASE (*)
|    |    \--- org.springframework.security:spring-security-config:5.1.5.RELEASE (*)
|    \--- org.springframework:spring-context:5.1.6.RELEASE
|         +--- runtime (requested org.springframework:spring-context:latest.release)
|         +--- org.springframework.security:spring-security-web:5.1.5.RELEASE (*)
|         +--- org.springframework.security:spring-security-config:5.1.5.RELEASE (*)
|         \--- org.springframework.security:spring-security-core:5.1.5.RELEASE (*)
+--- org.springframework:spring-context:5.1.6.RELEASE (*)
+--- org.springframework:spring-web:5.1.6.RELEASE
|    +--- runtime (requested org.springframework:spring-web:latest.release)
|    \--- org.springframework.security:spring-security-web:5.1.5.RELEASE (*)
+--- org.springframework.security:spring-security-config:5.1.5.RELEASE (*)
+--- org.springframework.security:spring-security-core:5.1.5.RELEASE (*)
\--- org.springframework.security:spring-security-web:5.1.5.RELEASE (*)

org.springframework:spring-beans:latest.release -> 5.1.6.RELEASE
\--- project :service_core
     \--- runtime

(*) - dependencies omitted (listed previously)

Additionally (just to make it clear that service_core.runtime is also a locked version):

./gradlew :service_core:dependencyinsight --dependency org.springframework:spring-beans --configuration runtime

results in

> Task :service_core:dependencyInsight
org.springframework:spring-beans:4.3.23.RELEASE
   variant "runtime" [
      org.gradle.status   = release (not requested)
      org.gradle.usage    = java-runtime (not requested)
      org.gradle.category = library (not requested)
   ]
   Selection reasons:
      - Was requested : rejected versions 5.1.6.RELEASE, 5.1.5.RELEASE, 5.1.4.RELEASE, 5.1.3.RELEASE, 5.1.2.RELEASE, 5.1.1.RELEASE, 5.1.0.RELEASE, 5.0.13.RELEASE, 5.0.12.RELEASE, 5.0.11.RELEASE, 5.0.10.RELEASE, 5.0.9.RELEASE, 5.0.8.RELEASE, 5.0.7.RELEASE, 5.0.6.RELEASE, 5.0.5.RELEASE, 5.0.4.RELEASE, 5.0.3.RELEASE, 5.0.2.RELEASE, 5.0.1.RELEASE, 5.0.0.RELEASE
      - By constraint : dependency was locked to version '4.3.23.RELEASE'

org.springframework:spring-beans:{strictly 4.3.23.RELEASE} -> 4.3.23.RELEASE
\--- runtime

org.springframework:spring-beans:latest.release -> 4.3.23.RELEASE
\--- runtime

So all of the locked configurations (web_client.compile, web_client.customerdemo, service_core:runtime etc.) correctly select the locked dependency version. The un-lockable web_client.runtime configuration ignores all that and picks latest.release.

Is that expected behaviour? If so, what is a usable strategy to lock dependencies with customer-specific artefacts?