Jenkins Groovy Pipeline Get (Windows)User Folder Per Node - windows

I have a distributed Jenkins build and the user under which the jenkins process runs on the slaves is not necessarily static, so I need a mechanism to get the user per node.
I am trying something like
#!/usr/bin/env groovy
class TestSettings {
public static String NuGetPackagesPath = "${env.USERPROFILE}\\.nuget\\packages"
}
node("master"){
println env.USERPROFILE // works as expected
println TestSettings.NuGetPackagesPath // throws exception
}
node("build"){
println env.USERPROFILE // works as expected
println TestSettings.NuGetPackagesPath // throws exception
}

env doesn't work in the static property, because the property is already initialized before you enter the node closure. So env just isn't available yet.
I see two ways around this:
Turn the property into a function and pass the env variable as parameter.
Make it a non-static function and pass env to the class constructor.
I would propably go with the latter as it will be easier to use when you have many test settings.
class TestSettings {
public static String getNuGetPackagesPath( def env ) { "${env.USERPROFILE}\\.nuget\\packages" }
}
class TestSettings2 {
def env = null
TestSettings2( def env ) {
this.env = env
}
public String getNuGetPackagesPath() { "${env.USERPROFILE}\\.nuget\\packages" }
}
node("master"){
println env.USERPROFILE
println TestSettings.getNuGetPackagesPath( env )
def testSettings = new TestSettings2( env )
// Note that we can use the method like a property!
println testSettings.nuGetPackagesPath
}

Related

Gradle: set env variable for static method on evaluation stage

In my build.gradle script I have a static method:
static def startDbContainer() {
var postgres = new PostgreSQLContainer(
DockerImageName.parse("registry/testcontainers/postgres:11.2-alpine")
.asCompatibleSubstituteFor("postgres"))
postgres.withInitScript("init.sql")
postgres.start()
return postgres
}
def postgres = startDbContainer()
On evaluation stage I drop to this point:
(in class org.testcontainers.DockerClientFactory)
public static String start(DockerClient client) {
String ryukImage = ImageNameSubstitutor.instance()
.apply(DockerImageName.parse("testcontainers/ryuk:0.3.3"))
.asCanonicalNameString();
DockerClientFactory.instance().checkAndPullImage(client, ryukImage);
...
There's no ryuk:0.3.3 image in my local repository, so build fails.
I can't rise up version of Testcontainers for some reasons.
I have an option to set env variable TESTCONTAINERS_RYUK_DISABLED=true to avoid behavior described, because of this branch:
boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED"));
if (useRyuk) {
log.debug("Ryuk is enabled");
try {
//noinspection deprecation
ryukContainerId = ResourceReaper.start(client);
} catch (RuntimeException e) {
cachedClientFailure = e;
throw e;
}
log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit");
} else {
log.debug("Ryuk is disabled");
ryukContainerId = null;
}
So the question is:
How I can set environment variable in Gradle script before running static method on evaluation stage?
P.S.: I've read topics here about setting env vars in test and Exec tasks: it's not my case.
You can't easily set the environment variable from within the already running Gradle process. You need to set the environment variable as part of calling Gradle or inside your environment.
And you can't access Docker Hub to pull testcontainers/ryuk:0.3.3? Did you consider bringing the image into your registry and configuring it?
See the official docs on how to do this: https://www.testcontainers.org/features/image_name_substitution/#automatically-modifying-docker-hub-image-names

Groovy DSL convention object "Could not get unknown property"

I have the following code in my build.gradle:
class GreetingPlugin implements Plugin<Project> {
def void apply(Project project) {
project.convention.plugins.greeting = new GreetingPluginConvention()
project.task('hello') {
doLast {
println project.convention.plugins.greeting.message
}
}
}
}
class GreetingPluginConvention {
String message
def greet(Closure closure) {
closure.delegate = this
closure()
}
}
apply plugin: GreetingPlugin
greet {
message = 'Hi from Gradle'
}
It executes nicely - ./gradlew hello prints "Hi from Gradle" which is expected.
However, using variable greet in the script (e.g. println greet) produces "Could not get unknown property 'greet' for project ':app' of type org.gradle.api.Project."
My question is - how the 'greet' variable is found when called against closure, but not found when used as a regular variable. What Groovy/Gradle magic is happening behind the scenes?
When it's called in a closure,
greet {
message = 'Hi from Gradle'
}
you are effectively adding more code to the original greet block/closure defined in GreetingPluginConvention, it is not a variable so attempts to treat it a such fail. Think of these block closures as a handy way to set or configure your plugins.
Gradle scripts are a bit different than your average linear script.

intellij hotswap doesn't work: adding one local variable

I am using OracleJVM with Intellij remote debugging. I am not doing any DCEVM fancy stuff. My code:
public static String test() {
String data; //new code
if (some condition){
//...
data = "abc"; //new code
//...
}
}
After making the change, recompile the class and verifying 'hotswap' finished successfully, the static method is re-ran but debugger variables window says
Cannot find local variable 'data'

Creating a closure in ext

I am implementing the texturePacker task given in LibGDX's TexturePacker with gradle.
project.ext {
// ...
texturePacker = ["assets", "../android/assets", "texture"]
}
import com.badlogic.gdx.tools.texturepacker.TexturePacker
task texturePacker << {
if (project.ext.has('texturePacker')) {
logger.info "Calling TexturePacker: "+ texturePacker
TexturePacker.process(texturePacker[0], texturePacker[1], texturePacker[2])
}
}
I got it working with the suggested modifications for the classpath and added extension variable. Now I want to modify the textPacker extension variable to be a closure (Is that the right terminology?) with descriptive member names rather than an array. I tried doing this:
project.ext {
// ...
texturePacker {
inputDir = "assets"
outputDir = "../android/assets"
packFileName = "texture"
}
}
This gives the following error:
Error:Could not find method texturePacker() for arguments [build_4dusyb6n0t7j9dfuws8cc2jlu$_run_closure1$_closure7#6305684e] on project ':desktop' of type org.gradle.api.Project.
I am very new to gradle and groovy, so I have no idea what this error means. More importantly, what is the correct way to do what I want?
I suppose, closure is not the thing you need, since it's used not to store variables, but to store some executable code. By the way, if need to store it, you have to add = as follows:
project.ext {
texturePacker = {
inputDir = "assets"
outputDir = "../android/assets"
packFileName = "texture"
}
}
Anyway, if need to store variables within texturePacker variable, you rather have to use a Map type, then a Closure. This could be done like this:
project.ext {
texturePacker = [
inputDir : "assets",
outputDir : "../android/assets",
packFileName : "texture"
]
}
And then you can access this variable just by names, as:
println texturePacker.inputDir
Or, I think you can also go for implementing your own task with those properties. You can use DefaultTask which is a standard implementation of a regular task (and I'm sure it'd be enough for you);
class TexturePacker extends DefaultTask {
String inputDir; // a property - not a field!
String outputDir; // a property - not a field!
...
#TaskAction
void doSth(){
// do sth with properties above - that will be called automatically by gradle as a task-execution
}
}
task packer (type:TexturePacker) {
inputDir '<your-input-dir>'
outputDir '<your-output-dir>'
}
Syntax might not be super correct, but I think you get the idea.

How to pass arguments from command line to Gradle

I'm trying to pass an argument from command line to a Java class. I followed this post: http://gradle.1045684.n5.nabble.com/Gradle-application-plugin-question-td5539555.html but the code does not work for me (perhaps it is not meant for JavaExec?). Here is what I tried:
task listTests(type:JavaExec){
main = "util.TestGroupScanner"
classpath = sourceSets.util.runtimeClasspath
// this works...
args 'demo'
/*
// this does not work!
if (project.hasProperty("group")){
args group
}
*/
}
The output from the above hard coded args value is:
C:\ws\svn\sqe\sandbox\selenium2forbg\testgradle>g listTests
:compileUtilJava UP-TO-DATE
:processUtilResources UP-TO-DATE
:utilClasses UP-TO-DATE
:listTests
Received argument: demo
BUILD SUCCESSFUL
Total time: 13.422 secs
However, once I change the code to use the hasProperty section and pass "demo" as an argument on the command line, I get a NullPointerException:
C:\ws\svn\sqe\sandbox\selenium2forbg\testgradle>g listTests -Pgroup=demo -s
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\ws\svn\sqe\sandbox\selenium2forbg\testgradle\build.gradle' line:25
* What went wrong:
A problem occurred evaluating root project 'testgradle'.
> java.lang.NullPointerException (no error message)
* Try:
Run with --info or --debug option to get more log output.
* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating root project
'testgradle'.
at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:54)
at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:127)
at org.gradle.configuration.BuildScriptProcessor.evaluate(BuildScriptProcessor.java:38)
There is a simple test project available at http://gradle.1045684.n5.nabble.com/file/n5709919/testgradle.zip that illustrates the problem.
This is using Gradle 1.0-rc-3. The NullPointer is from this line of code:
args group
I added the following assignment before the task definition, but it didn't change the outcome:
group = hasProperty('group') ? group : 'nosuchgroup'
Any pointers on how to pass command line arguments to Gradle appreciated.
project.group is a predefined property. With -P, you can only set project properties that are not predefined. Alternatively, you can set Java system properties (-D).
As noted in a comment, my solution is superceded by the newer built-in --args option in gradle. See this answer from #madhead or this similar question.
Building on Peter N's answer, this is an example of how to add (optional) user-specified arguments to pass to Java main for a JavaExec task (since you can't set the 'args' property manually for the reason he cites.)
Add this to the task:
task(runProgram, type: JavaExec) {
[...]
if (project.hasProperty('myargs')) {
args(myargs.split(','))
}
... and run at the command line like this
% ./gradlew runProgram '-Pmyargs=-x,7,--no-kidding,/Users/rogers/tests/file.txt'
My program with two arguments, args[0] and args[1]:
public static void main(String[] args) throws Exception {
System.out.println(args);
String host = args[0];
System.out.println(host);
int port = Integer.parseInt(args[1]);
my build.gradle
run {
if ( project.hasProperty("appArgsWhatEverIWant") ) {
args Eval.me(appArgsWhatEverIWant)
}
}
my terminal prompt:
gradle run -PappArgsWhatEverIWant="['localhost','8080']"
As of Gradle 4.9 Application plugin understands --args option, so passing the arguments is as simple as:
build.gradle
plugins {
id 'application'
}
mainClassName = "my.App"
src/main/java/my/App.java
public class App {
public static void main(String[] args) {
System.out.println(args);
}
}
bash
./gradlew run --args='This string will be passed into my.App#main arguments'
or in Windows, use double quotes:
gradlew run --args="This string will be passed into my.App#main arguments"
You can use custom command line options in Gradle:
./gradlew printPet --pet="Puppies!"
Custom command line options were an incubating feature in Gradle 5.0 but became public in Gradle 6.0.
Java solution
Follow the instructions here:
import org.gradle.api.tasks.options.Option;
public class PrintPet extends DefaultTask {
private String pet;
#Option(option = "pet", description = "Name of the cute pet you would like to print out!")
public void setPet(String pet) {
this.pet = pet;
}
#Input
public String getPet() {
return pet;
}
#TaskAction
public void print() {
getLogger().quiet("'{}' are awesome!", pet);
}
}
Then register it:
task printPet(type: PrintPet)
Now you can do:
./gradlew printPet --pet="Puppies!"
output:
Puppies! are awesome!
Kotlin solution
open class PrintPet : DefaultTask() {
#Suppress("UnstableApiUsage")
#set:Option(option = "pet", description = "The cute pet you would like to print out")
#get:Input
var pet: String = ""
#TaskAction
fun print() {
println("$pet are awesome!")
}
}
then register the task with:
tasks.register<PrintPet>("printPet")
If you need to check and set one argument, your build.gradle file would be like this:
....
def coverageThreshold = 0.15
if (project.hasProperty('threshold')) {
coverageThreshold = project.property('threshold').toString().toBigDecimal()
}
//print the value of variable
println("Coverage Threshold: $coverageThreshold")
...
And the Sample command in windows:
gradlew clean test -Pthreshold=0.25
I have written a piece of code that puts the command line arguments in the format that gradle expects.
// this method creates a command line arguments
def setCommandLineArguments(commandLineArgs) {
// remove spaces
def arguments = commandLineArgs.tokenize()
// create a string that can be used by Eval
def cla = "["
// go through the list to get each argument
arguments.each {
cla += "'" + "${it}" + "',"
}
// remove last "," add "]" and set the args
return cla.substring(0, cla.lastIndexOf(',')) + "]"
}
my task looks like this:
task runProgram(type: JavaExec) {
if ( project.hasProperty("commandLineArgs") ) {
args Eval.me( setCommandLineArguments(commandLineArgs) )
}
}
To pass the arguments from the command line you run this:
gradle runProgram -PcommandLineArgs="arg1 arg2 arg3 arg4"
There's a great example here:
https://kb.novaordis.com/index.php/Gradle_Pass_Configuration_on_Command_Line
Which details that you can pass parameters and then provide a default in an ext variable like so:
gradle -Dmy_app.color=blue
and then reference in Gradle as:
ext {
color = System.getProperty("my_app.color", "red");
}
And then anywhere in your build script you can reference it as course anywhere you can reference it as project.ext.color
More tips here: https://kb.novaordis.com/index.php/Gradle_Variables_and_Properties
Here is a solution for Kotlin DSL (build.gradle.kts).
I first try to get the variable as a property and if it was null try to get it from OS environment variables (can be useful in CIs like GitHub Actions).
tasks.create("MyCustomTask") {
val songName = properties["songName"]
?: System.getenv("SONG_NAME")
?: error("""Property "songName" or environment variable "SONG_NAME" not found""")
// OR getting the property with 'by'. Did not work for me!
// For this approach, name of the variable should be the same as the property name
// val songName: String? by properties
println("The song name: $songName")
}
We can then pass a value for the property from command line:
./gradlew MyCustomTask -PsongName="Black Forest"
Or create a file named local.properties at the root of the project and set the property:
songName=Black Forest
We can also add an env variable named SONG_NAME with our desired value and then run the task:
./gradlew MyCustomTask
pass a url from command line keep your url in app gradle file as follows
resValue "string", "url", CommonUrl
and give a parameter in gradle.properties files as follows
CommonUrl="put your url here or may be empty"
and pass a command to from command line as follows
gradle assembleRelease -Pcommanurl=put your URL here

Resources