Apply external Plugin to a subset of subprojects - gradle

I am using the Gradle SSH Plugin to deploy some, but not all subprojects to two different remote machines. I want to configure these remotes in the main build script to avoid duplication, but cannot apply the plugin as per this:
configure([
project(':Subproject1'), project(':Subproject5'), project(':Subproject7')
])
{
buildscript {
dependencies {
classpath 'org.hidetake:gradle-ssh-plugin:2.4.2'
}
}
apply plugin: "org.hidetake.ssh"
ssh.settings {
user = getProperty('ssh.username')
identity = file(getProperty('ssh.identity'))
knownHosts = allowAnyHosts
}
remotes {
tomcat {
host = getProperty('ssh.hosts.tomcat')
}
jboss {
host = getProperty('ssh.hosts.jboss')
}
}
}
Gradle fails with > Plugin with id 'org.hidetake.ssh' not found.
Everything is fine when the contents of the configuration closure are applied per project. How can I elegantly solve this issue?

Guessing here as I haven't reproduced your issue, but the buildscript block is special as it is pre-parsed before normal groovy parsing of the build files and I suspect that having it inside a project block like you have will not work.
Have you tried moving the buildscript block to the root level in the script above?
edit 1: old gradle forums post discussion this can be found here
edit 2: adding a snippet of code in response to a comment.
To remove duplication and only define say the repository references in one place, you could use the following pattern:
buildscript {
ext.RepositoryConfigurator = {
maven {
credentials.username artifactoryReader
credentials.password artifactoryReaderPwd
url artifactoryReaderUrl
}
jcenter()
mavenCentral()
}
ext.DependencyConfigurator = {
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:1.2"
}
ext.ResolutionConfigurator = {
resolutionStrategy.cacheChangingModulesFor 60, 'minutes'
}
repositories RepositoryConfigurator
dependencies DependencyConfigurator
configurations.classpath ResolutionConfigurator
}
apply plugin: "some.plugin.requiring.above.classpath"
gradle.rootProject {
buildscript {
repositories RepositoryConfigurator
dependencies DependencyConfigurator
configurations.classpath ResolutionConfigurator
}
}
gradle.allprojects {
buildscript {
repositories RepositoryConfigurator
dependencies DependencyConfigurator
configurations.classpath ResolutionConfigurator
}
}
In other words: since the buildscript block is special and pre-parsed, you can not use things defined elsewhere in the buildscript block. Going in the other direction is ok though, i.e. you can define things in the buildscript block which are then visible elsewhere. We can use this to define the repository references once inside the buildscript block and then use that reference in other places in the build file.
The above is from a settings.gradle file so might or might not be a perfect fit, but should demonstrate the idea.

Related

Gradle single-project pluginManagement block not working (Kotlin DSL)

I need to change a multi-project build to a single-project build, as there is and only ever will be one project in this repo. Currently, in settings.gradle, I have a custom plugin repo that currently uses a pluginManagement block with resolutionStrategy and my list of repo's:
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.meanwhileinhell.plugin') {
useModule('com.meanwhileinhell:gradle-plugin:1.0.0-SNAPSHOT')
}
}
}
repositories {
mavenLocal()
maven { url "https://repo.spring.io/milestone" }
maven { url "https://plugins.gradle.org/m2/" }
// Meanwhileinhell repo
maven {
url "s3://mvn.meanwhileinhell.com/releases"
credentials(AwsCredentials) {
accessKey s3_access_key
secretKey s3_access_secret
}
}
}
plugins {
...
...
}
}
However, deleting settings.gradle and moving this block into my build.gradle.kts (Kotlin DSL) seems to do nothing. I've tried wrapping in a
configurations {
all {
resolutionStrategy {
eachPlugin {
...
}
}
}
}
and also
settings {
pluginManagement {
resolutionStrategy {
eachPlugin {
...
}
}
}
}
I found a SO answer that used settingsEvaluated in order to get the settings object, but again this was a no go.
Currently my build.gradle.kts looks like this, without pulling any plugin in from my repo:
val springBootVersion: String by project
group = "com.meanwhileinhell.myapp"
version = "$version"
repositories {
mavenCentral()
mavenLocal()
maven ("https://repo.spring.io/snapshot")
maven ("https://repo.spring.io/milestone")
maven ("https://plugins.gradle.org/m2/")
maven {
url = uri("s3://mvn.meanwhileinhell.com/releases")
credentials(AwsCredentials::class) {
accessKey = (project.property("s3_access_key") as String)
secretKey = (project.property("s3_access_secret") as String)
}
}
}
plugins {
base
eclipse
idea
java
id("io.spring.dependency-management") version "1.0.9.RELEASE"
// Load but don't apply to root project
id("org.springframework.boot") version "1.5.14.RELEASE" apply false
}
dependencies {
...
}
Whenever I try to add a plugin id like id("com.meanwhileinhell.plugin.hell2java") version "1.0.0-SNAPSHOT" I get an error that looks like it isn't even looking in my S3 location:
* What went wrong:
Plugin [id: 'com.meanwhileinhell.plugin.hell2java', version: '1.0.0-SNAPSHOT'] was not found in any of the following sources:
- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (could not resolve plugin artifact 'com.meanwhileinhell.plugin.hell2java:com.meanwhileinhell.plugin.hell2java.gradle.plugin:1.0.0-SNAPSHOT')
Searched in the following repositories:
Gradle Central Plugin Repository
Any help on this would be appreciated!
EDIT !!!! -----------------------
I've just found this in the Gradle docs:
https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_management
The pluginManagement {} block may only appear in either the settings.gradle file....
Looks like I'm going down the wrong way entirely, so will look into the initialisation script route.
I think you may have missed something about the file structure.
In the Groovy DSL, you have the following files:
build.gradle
settings.gradle
init.gradle
In the Kotlin DSL, you have the same files but with the .kts extension:
build.gradle.kts
settings.gradle.kts
init.gradle.kts
The Kotlin DSL doesn't differ to the Groovy DSL in where to put things. pluginManagement need to go in to the settings file, so for Kotlin that would be settings.gradle.kts. If you are in doubt, look at the documentation. For almost all code examples, you can switch between Groovy and Kotlin DSL to see how to do it (and which files they are supposed go to into).

How are gradle extra properties set in the Kotlin DSL?

I'm trying to organize my build files as I would in groovy, by having values in a separate file to reuse. But I cannot understand the syntax to do the same thing in the kotlin DSL.
Here's what I'm using in root build.gradle.kts:
applyFrom("config.gradle.kts")
buildscript {
repositories {
google()
jcenter()
}
dependencies {
val test = project.extra["minSdkVer"]
classpath("com.android.tools.build:gradle:3.0.0-alpha4")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-5")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
and here's whats in the config.gradle.kts that is being referenced:
mapOf(
Pair("minSdkVer", 22),
Pair("targetSdkVer", 25),
Pair("compiledSdkVer", 25),
Pair("buildToolsVer", "26-rc4")
).entries.forEach {
project.extra.set(it.key, it.value)
}
But there's an error:
Cannot get property 'minSdkVer' on extra properties extension as it
does not exist
A correct fix: Gradle collects and applies the buildscript { ... } blocks from your script strictly before executing anything else from it. So, to make your properties from config.gradle.kts available inside the buildscript, you should move applyFrom("config.gradle.kts") to your buildscript { ... } block:
buildscript {
applyFrom("config.gradle.kts")
/* ... */
}
Another possible mistake is using an extra property as extra["minSdkVer"] in a scope of another ExtensionAware, like a task in this example:
val myTask = task("printMinSdkVer") {
doLast {
println("Extra property value: ${extra["minSdkVer"]}")
}
}
In this case, extra.get(...) uses not the project.extra but the extra of the task.
To fix that, specify that you work with the project. Direct usage:
println(project.extra["minSdkVer"])
And for delegation.
val minSdkVer by project.extra

Why does this string replacement/concatenation not work in Gradle?

In this snippet of a build.gradle file, the first reference to ${appengineVersion} (line 11) causes an error. But the second reference (line 27) works fine. Why is that?
To get it working, I've had to explicitly include the version number on line 11...meaning that I'm going to forget to update it next time. How to fix that?
apply plugin: 'war'
apply plugin: 'appengine'
def appengineVersion = "1.9.48"
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "com.google.appengine:gradle-appengine-plugin:${appengineVersion}"
}
}
war {
from 'src/main/webUI/app'
exclude('src/main/webUI/app/node_modules')
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
appengineSdk "com.google.appengine:appengine-java-sdk:${appengineVersion}"
compile "javax.servlet:servlet-api:2.5"
...
Standard gradle practice is to declare user-defined properties in the ext namespace. See here
You should do:
ext.appengineVersion = "1.9.48"
Declaring vars on the top level of a project build file doesn’t make them visible to all Gradle blocks. buildscript {} is special, it gets evaluated before any other part of the script is. You can move declaration into the buildscript though, it should make it visible to other blocks too:
buildscript {
def appengineVersion = "1.9.48"
...
but you would need to use like
dependencies {
appengineSdk "com.google.appengine:appengine-java-sdk:" + appengineVersion
The solution was a combination of the answers from Oleg and RaGe:
Move the declaration of appengineVersion into the buildscript block AND define it in the ext namespace.
buildscript {
ext.appengineVersion = "1.9.48"
repositories {
mavenCentral()
}
dependencies {
classpath "com.google.appengine:gradle-appengine-plugin:${appengineVersion}"
}
}
This allowed it variable to be resolved both by the buildscript dependency and the project dependency:
dependencies {
appengineSdk "com.google.appengine:appengine-java-sdk:${appengineVersion}"
compile "javax.servlet:servlet-api:2.5"
compile "com.google.appengine:appengine-api-1.0-sdk:${appengineVersion}"
Both answers were a key part of the solution. But unfortunately, I can't accept 2 answers, so I'm answering it myself (and up-voting both answers).

Can I put my dependencies and repositories in another file?

I have a requirement that some of my dependencies are in a separate file. How can I accomplish this? One of the samples on docs.gradle.org is:
List groovy = ["org.codehaus.groovy:groovy-all:2.4.7#jar",
"commons-cli:commons-cli:1.0#jar",
"org.apache.ant:ant:1.9.6#jar"]
List hibernate = ['org.hibernate:hibernate:3.0.5#jar',
'somegroup:someorg:1.0#jar']
dependencies {
runtime groovy, hibernate
}
Something like this would work, but I'd also like to specify my repositories in the same file.
Edit:
The solution I came up with
stuff.gradle
repositories {
maven {
credentials {
username 'stuff'
password 'stuff'
}
url 'stuff'
}
}
dependencies {
compile 'things'
}
And in build.gradle
apply from: 'path/to/stuff.gradle'
After dependencies, though I'm not sure if it makes a difference. It was surprisingly simple. There were no changes to build.gradle apart from the apply statement, it still has the normal repositories and dependencies closures. Thanks to Opal for putting me on the right track.
Here you go:
lol.gradle:
ext.repos = { mavenCentral(); mavenLocal() }
ext.groovy = [
"org.codehaus.groovy:groovy-all:2.4.7#jar",
"commons-cli:commons-cli:1.0#jar",
"org.apache.ant:ant:1.9.6#jar",
]
ext.hibernate = [
'org.hibernate:hibernate:3.0.5#jar',
]
build.gradle
apply from: 'lol.gradle'
apply plugin: 'java'
repositories repos
dependencies {
runtime groovy, hibernate
}
Since repositories can be kept as a list, the simplest way of keeping repositories with no workarounds is to use a closure. If you use a list to keep repositories as well it will fail with with method resolution error for both mavenLocal and mavenCentral.
If you prefer to keep repositories as a list however then the following piece of code can be used:
lol.gradle
ext.repos = ['mavenCentral', 'mavenLocal',]
ext.groovy = [
"org.codehaus.groovy:groovy-all:2.4.7#jar",
"commons-cli:commons-cli:1.0#jar",
"org.apache.ant:ant:1.9.6#jar",
]
ext.hibernate = [
'org.hibernate:hibernate:3.0.5#jar',
]
build.gradle
apply from: 'lol.gradle'
apply plugin: 'java'
repositories { r ->
repos.each { n -> r."$n"() }
}
dependencies {
runtime groovy, hibernate
}

Gradle Custom buildScriptRepository methods

I would like to make a custom buildScript repository method so I can easily reference our internal maven repo. Right now I'm required to declare a maven block everywhere we use our plugin.
Here is the current setup
buildscript {
repositories {
jcenter()
maven { url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
What I would like to do is something like this
buildscript {
repositories {
jcenter()
myReleaseRepo()
}
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
How can I make a method available to create a repository anywhere we may use the plugin in the future?
Another solution is to add custom methods on RepositoryHandler using some Groovy goodness. Just chuck this in ~/.gradle/init.gradle
RepositoryHandler.metaClass.myReleaseRepo = {
delegate.maven {
name 'myReleaseRepo'
url 'http://myNexus:8081/nexus/content/repositories/My-Release'
}
}
After that, you can use it just as you described:
buildscript {
repositories {
myReleaseRepo()
}
}
Metaclasses in Groovy are just great. The delegate in this case is pretty much like the javascript this. This code is essentially using the RepositoryHandler instance (delegate keyword) and just calling repositoryHandlerInstance.maven(name, url).
It is possible to add a repo to an init script which would then apply to all gradle invocations that use the init script - without having to individually declare your maven repo in each build.gradle.
Solution 1:
Partial solution, does not do exactly what you're asking for. In init.gradle:
allprojects{
buildscript{
repositories{
maven{ url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
}
}
Then your build.gradle can skip buildscript repo declaration entirely:
buildscript {
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
Solution 2:
In fact, you can even move your buildscript classpath declaration to init and have the plugin apply to all projects that use the init script:
beefier init.gradle
allprojects{
buildscript{
repositories{
maven{ url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
}
gives you a lighter build.gradle
apply plugin: 'my-plugin'
I tried to, but apparently you cannot move the apply line to init.gradle as well. see this defect.
Solution 3:
I retract what I said in the comment above, I figured out how to do exactly what you're asking for. Apparently you can create extensions for the buildscript block using the initscript. However I still prefer solution2, because it gives you a cleaner build.gradle.
To create a buildscript extension, in your init.gradle:
class customRepos {
def buildscript
customRepos(buildscript) {
this.buildscript = buildscript
}
void addMyRepo() {
buildscript.repositories {
maven{ url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
}
}
allprojects {
extensions.create('myRepo', customRepos, buildscript)
}
which then allows you to do this in your build.gradle
buildscript{
myRepo.addMyRepo()
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
apply plugin: 'my-plugin'

Resources