How can I access ant tasks from my build.gradle.kts script? In particular, I am interested in the ant.patch task.
Can I extend it, like so?
task("patchSources", Patch::class) {
Can I invoke it from other task, like this?
task("patchSources") {
doLast {
ant.patch(...)
}
}
I know how to do it in Groovy: How do I apply a patch file in Gradle?
AntBuilder extends from Groovy's AntBuilder. You can translate the dynamic method invocations from groovy like ant.patch() to Kotlin by using invokeMethod and providing the desired task as the first argument and the properties to bind as a map in the second argument.
For example, for your Patch use case (available properties documentation) the Kotlin could look like this:
val patchSources by tasks.creating {
doLast {
ant.invokeMethod("patch", mapOf(
"patchfile" to patchFile,
"dir" to configDir,
"strip" to 1
))
}
}
This works for me:
import org.apache.tools.ant.taskdefs.Patch
val patchConfigTask = task("patchConfig") {
dependsOn(unzipTask)
doLast {
val resources = projectDir.resolve("src/main/resources")
val patchFile = resources.resolve("config.patch")
Patch().apply {
setPatchfile(patchFile)
setDir(buildDir.resolve("config/"))
setStrip(1) // gets rid of the a/ b/ prefixes
execute()
}
}
}
I am not sure if it's the one-right-way-to-do-it.
Related
I'm trying new Swift 5.5 await async features and I get that compilation error when I try this in a Playground:
let task1 = Task {
// ...
}
'Task' cannot be constructed because it has no accessible initializers
I got the example from https://www.hackingwithswift.com/articles/233/whats-new-in-swift-5-5
Any idea?
It looks like the current syntax is async { ... } instead of Task { ... } with the intention that the Task syntax is to be used in some future build.
Is something like this possible, i.e. using the JobDSL API from a class outside the main DSL script?
//main_jobdsl_script.groovy:
new JobCreator().createJob()
//JobCreator.groovy:
job("new-job") {
steps {
batchFile("Hello World")
}
}
When running it I get the error
13:03:18 ERROR: No signature of method: JobCreator.job() is applicable for argument types:
(org.codehaus.groovy.runtime.GStringImpl, StartJobCreator$_createJob_closure1)
values: ["new-job", de.dbh.jobcreation.StartJobCreator$_createStartJob_closure1#374d293]
I want to avoid that the main-script gets too big and cluttered and rather divide the code into several scripts / classes.
Yes, it is possible. The current script has access to all API methods, so you need to pass it to the custom class.
//main_jobdsl_script.groovy:
new JobCreator(this).createJob()
//JobCreator.groovy:
class JobCreator {
private final Object context
JobCreator(Object context) {
this.context = context
}
void createJob() {
context.job('new-job') {
steps {
batchFile('Hello World')
}
}
}
}
Is it possible to have a single .groovy file that has some utility functions defined and have one of those functions call another in that file?
note: for context, this is being used for Jenkins Pipeline library under vars folder. I wanted to have a function used for param validation call another function in the same groovy script file.
i.e. have the someFunction make use of the doSomething function, some pseudo code below.
//utils.groovy
def doSomething(def a) {
def aPrime = a
if (a == 'somethingSpecial') {
//handle it
//some logic goes here
aPrime = b
}
return aPrime
}
def someFunction(def x) {
y = doSomething(x);
more stuff.. using y
return someResult
}
def dodad() {
...
}
def whatsIt(){
...
}
In my actual code I get error like No signature of method: groovysh_evaluate.myCommonFunct() is applicable for argument types: () values: []
Nevermind this does work.
I got the error when I tried to run the contents of the file locally in groovysh. But no errors when it ran in the Jenkins pipeline
Currently I'm trying to register findFiles step.
My set up is as follows:
src/
test/
groovy/
TestJavaLib.groovy
vars/
javaLib.groovy
javaApp.jenkinsfile
Inside TestJavaApp.groovy I have:
...
import com.lesfurets.jenkins.unit.RegressionTest
import com.lesfurets.jenkins.unit.BasePipelineTest
class TestJavaLibraryPipeline extends BasePipelineTest implements RegressionTest {
// Some overridden setUp() which loads shared libs
// and registers methods referenced in javaLib.groovy
void registerPipelineMethods() {
...
def fileList = [new File("testFile1"), new File("testFile2")]
helper.registerAllowedMethod('findFiles', { f -> return fileList })
...
}
}
and my javaLib.groovy contains this currently failing part:
...
def pomFiles = findFiles glob: "target/publish/**/${JOB_BASE_NAME}*.pom"
if (pomFiles.length < 1) { // Fails with java.lang.NullPointerException: Cannot get property 'length' on null object
error("no pom file found")
}
...
I have tried multiple closures returning various objects, but everytime I get NPE.
Question is - how to correctly register "findFiles" method?
N.B. That I'm very new to mocking and closures in groovy.
Looking at the source code and examples on GitHub, I see a few overloads of the method (here):
void registerAllowedMethod(String name, List<Class> args = [], Closure closure)
void registerAllowedMethod(MethodSignature methodSignature, Closure closure)
void registerAllowedMethod(MethodSignature methodSignature, Function callback)
void registerAllowedMethod(MethodSignature methodSignature, Consumer callback)
It doesn't look like you are registering the right signature with your call. I'm actually surprised you aren't getting a MissingMethodException with your current call pattern.
You need to add the rest of the method signature during registration. The findFiles method is taking a Map of parameters (glob: "target/publish/**/${JOB_BASE_NAME}*.pom" is a map literal in Groovy). One way to register that type would be like this:
helper.registerAllowedMethod('findFiles', [Map.class], { f -> return fileList })
I also faced the same issue. However, I was able to mock the findFiles() method using the following method signature:
helper.registerAllowedMethod(method('findFiles', Map.class), {map ->
return [['path':'testPath/test.zip']]
})
So I found a way on how to mock findFiles when I needed length property:
helper.registerAllowedMethod('findFiles', [Map.class], { [length: findFilesLength ?: 1] })
This also allows to change findFilesLength variable in tests to test different conditions in pipeline like the one in my OP.
I'm writing my own shared library. Now I want to use global variable in my code. How can I make that happen ?
I.E. I write a class.
class MyWork {
build() {
// here I want to use global docker(which is docker-plugin)
docker.doSomething {
}
}
}
enable the library in 'Global Pipeline Libraries' (i add the library 'pipeline-shared-lib')
create a shared-library (with the necessary structure)
src/net/kukinet/Utils.groovy
package net.kukinet;
def myVar = 1
def sayHello() {
print ('hello')
}
create a pipeline job and create the object
JenkinsJob.groovy
#!groovy
// this need to be enabled in jenkins configuration ( in: manage jenkins)
#Library('pipeline-shared-lib') import net.kukinet.*
node (){
u = new net.kukinet.Utils();
stage('preperations') {
print(u.myVar)
u.sayHello()
}
}