Passing gradle project properties via command line to GradleRunner?

I’m building a gradle plugin with integration tests that use the GradleRunner (gradle v3.0). I have a problem executing the tests because I need to pass in some user credentials (the test is basically doing a “curl -u username:password http://url.example.com”).

I want to avoid having these credentials in a gradle.properties file and would like to pass them on the command line, but these properties do not get propagated to the GradleRunner.

Essentially what I would like to do is this:

./gradlew integTest -Pusername=user123 -Ppassword=userpass

Any recommendations?

I have configured below in my build.gradle. Works for me. I am passing parameters with -D instead of -P you have used.

test{
useTestNG()
systemProperty “username”, System.getProperty(“username”)
systemProperty “password”, System.getProperty(“password”)
}

Yeah, I thought that should work for me but it doesn’t for some strange reason.
Does useTestNG() do something special for you?

Basically my code looks something like this

public class integrationTests {

    public final TemporaryFolder testProjectDir = new TemporaryFolder()

    @Test
    public void listReposTest() {
        systemProperty username = System.getProperty("username")    // for debugging
        systemProperty password = System.getProperty("password")    // for debugging

        BuildResult result = GradleRunner.create()
                    .withProjectDir(testProjectDir.root)
                    .withArguments("listRepos")
                    .withPluginClasspath()
                    .build()

        result.task(":listRepos").outcome == "SUCCESS"
    }
}

If I try to run the above with either of these:

./gradlew integTest -DsystemProp.username=foo -DsystemProp.password=foo
./gradlew integTest -Dusername=foo -Dpassword=foo

It fails with:

 groovy.lang.MissingPropertyException: No such property: username for class: integrationTests

If I remove those two getProperty debug lines above so that the GradleRunner executes I still get the same error. My actual task looks basically like this:

class ListReposTask extends TaskBaseClass {

void doTask() {
    systemProperty username = System.getProperty("username")
    systemProperty password = System.getProperty("password")
    String project = project.getProperty("project")

    def process = ['curl', '-su', "${username}:${password}", "${project}"].execute()
}

And the result of the test is this:

* What went wrong:
Execution failed for task ':listRepos'.
> Could not set unknown property 'username' for task ':listRepos' of type com.example.gradle.plugins.tasks.ListReposTask.

Even if that did work, I can’t help but feel that it’s a bit of a questionable approach to use the system properties for passing credentials between different JVMs though…

You have to add systemProperty part inside the test task in build.gradle of your project. UseTestNG is used as my tests are TestNG based. This is not needed for Junits.

So you’re telling me that this build.gradle should work?

16:42 wtf → cat build.gradle
buildscript {
    repositories {
        jcenter()
    }
}

apply plugin: "java"

test {
    //systemProperty username = System.getProperty("username")
    //systemProperty password = System.getProperty("password")
    systemProperty "username",System.getProperty("username")
    systemProperty "password",System.getProperty("password")
    println "${username}"
    println "${password}"
}

But it doesn’t:

16:53 wtf → gradle test -Dusername=foo -Dpassword=bar

FAILURE: Build failed with an exception.

* Where:
Build file 'C:\cygwin64\home\user\workspace\wtf\build.gradle' line: 14

* What went wrong:
A problem occurred evaluating root project 'wtf'.
> Could not get unknown property 'username' for task ':test' of type org.gradle.api.tasks.testing.Test.

Same result when running:

gradle test -DsystemProp.username=foo -DsystemProp.password=bar

If it is relevant at all I’m running Cygwin on windows 7 and Gradle 3.0

This cannot work, you are trying to access a variable that doesn’t exist.

Also, I’d generally go with project properties, because those are more Gradle-idiomatic (can be overwritten in gradle.properties, by environment variables etc.

test {
    systemProperty "username", findProperty("username")
    systemProperty "password", findProperty("password")
}

and call

./gradlew test -Pusername=foo -Ppassword=bar

Thanks for the correction @st_oehme, but we got a bit distracted from solving my actual problem.

Since the GradleRunner forks a process with a separate JVM and context, it does not have the gradle project properties that I provide on the command line when I run

./gradlew test -Pusername=foo -Ppassword=bar

I have explicitly set up some static gradle project properties in my integration tests but how do I solve this problem for credentials? I cannot hard code a username and password in plain text in the source code.

I thought the system properties should work, but for some reason it hasn’t, but that also doesn’t feel like a safe way to pass credentials. For example I suspect those system properties live outside the lifecycle of the java process and only would disappear on a reboot or when they are overwritten.

My thinking is that the best way to do this is to pass the Gradle project object to my integration test class. Or alternatively, initialise my integration test class to look up the gradle project object which has the variables that I need. Any tips on how to do this?

My suggestion above is the first part of the solution. You then have the system properties in your test class, which then needs to pass them to the GradleRunner.

runner.withArguments("-Pusername=" + System.getProperty("username"))

So in the end I got this working the way you suggested, but I’m really not satisfied from a security perspective.

If I come up with a better to handle credentials I’ll try to remember to update this thread with the solution.

What’s the security problem? You pass the properties from the very outside. Or maybe you didn’t do what I was proposing:

  1. You pass the properties to the main build with -P
  2. The main build passes them as system properties to the test VM
  3. The test VM passes them as -P properties to the build under test

This way you can define them on the command line, in your user home or as environment variables.

My familiarity with groovy and gradle is still fairly basic so forgive it I have am incorrect on any of the details.

I am doing exactly what you recommended and it works.

My main concern here is in part 2 where the credentials are passed as system properties into the GradleRunner VM. From my understanding, these credentials will be readable by any java process on the system?

I wondered about the persistence of those variables beyond the life of the GradleRunner VM but my testing showed those variables to be null after the tests finished. I was a bit surprised to see that result but happy nonetheless.

In any event, the concerns with gradle are relatively minor compared to the security issues I need to solve with jenkins.

I wasn’t assuming that your build is running in an untrusted environment.
Usually CI servers are under full control. In an untrusted environment your
concern is very valid. But if it is untrusted, then any access to
credentials is a problem.

There’s no real solution to that other than building in a trusted
environment.