What is a replacement for meta runners in TeamCity Kotlin DSL? - kotlin

Apparently there's no support for metarunners generation in TeamCity Kotlin DSL. The files remain in plain XML.
How do I replace it using available DSL features? Say I'd like to do this:
steps {
step {
type = "mymetarunner" // compound meta-runner step
}
}
How do I define mymetarunner using Kotlin?

At the moment (TeamCity 2017.2), there is no way to define metarunners using Kotlin DSL.
Update
If having a real metarunner is not required, the solution is a small exercise in Kotlin DSL
Define a container class for settings you need for "metarunner"
class MyConfigClass {
var name = "Default Name"
var goals = "build"
var tasks = "build test"
var someUnusedProperty = 0
}
Define an extension function for steps block
fun BuildSteps.myMetaRunner(config: MyConfigClass.() -> Unit) {
val actualConfig = MyConfigClass() // new config instance
actualConfig.config() // apply closure to fill the config
// use the config to create actual steps
maven {
name = actualConfig.name
goals = actualConfig.goals
}
ant {
name = actualConfig.tasks
}
}
Use the extension function wherever you need
object A_Build : BuildType({
uuid = ...
steps {
myMetaRunner {
name = "This name will be used by maven step"
goals = "build whatever_goal"
tasks = "more ant tasks"
}
}
})
Bingo!

Related

org.openapitools.generator.gradle.plugin.tasks.GenerateTask' property 'inputSpec' doesn't have a configured value

I tried to generate two swagger yaml files in one build script
Here is my code
plugins {
kotlin("jvm")
id("org.openapi.generator")
}
sourceSets {
main {
java.srcDir("$buildDir/generate-resources/main/src/main/kotlin")
}
}
dependencies {
val jacksonVersion: String by project
implementation(kotlin("stdlib"))
testImplementation(kotlin("test-junit"))
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")
}
val dir = File("$rootDir/transport/spec/".toString())
val swaggerList: ArrayList<String> = ArrayList()
dir.walkTopDown().forEach {
val endsWith = it.name.endsWith(".yaml")
if (endsWith) {
swaggerList.add(it.name)
}
}
swaggerList.forEach {swaggerItem->
println(swaggerItem)
val apiName = swaggerItem.replace(".yaml", "")
tasks.create(
"openApiGenerate" + apiName.capitalize(),
org.openapitools.generator.gradle.plugin.tasks.GenerateTask::class)
{
val openapiGroup = "${rootProject.group}.openapi"
generatorName.set("kotlin")
packageName.set(openapiGroup)
apiPackage.set("$openapiGroup.api")
modelPackage.set("$openapiGroup.models")
invokerPackage.set("$openapiGroup.invoker")
inputSpec.set("$rootDir/transport/spec/$swaggerItem")
println("setup input spec")
globalProperties.apply {
put("models", "")
put("modelDocs", "false")
}
configOptions.set(
mapOf(
"dateLibrary" to "string",
"enumPropertyNaming" to "UPPERCASE",
"serializationLibrary" to "jackson",
"collectionType" to "list"
)
)
}
}
tasks {
compileKotlin {
dependsOn(openApiGenerate)
}
}
I launched this code via gradle build, but nothing works
I got this error
In plugin 'org.openapi.generator' type 'org.openapitools.generator.gradle.plugin.tasks.GenerateTask' property 'inputSpec' doesn't have a configured value.
Reason: This property isn't marked as optional and no value has been configured.
Possible solutions:
Assign a value to 'inputSpec'.
Mark property 'inputSpec' as optional.
I tried to assign this property in different ways it was unsuccessful
how I can fix it ?
In your build.gradle you are creating multiple tasks for each item in swaggerList. Each task is named: "openApiGenerate" + apiName.capitalize(). So when you call your build like this:
gradle openapiGenerate
it tries to use a default task configured with this name and not any of the ones you are creating.
Can you try to run it:
gradle openapiGenerateYOUR_API_NAME
and check what's the result?
In my case, the same error message was caused by having a compileJava.dependsOn tasks.openApiGenerate in the wrong build.gradle file.

Teamcity Kotlin Build Base Class Property Extension

I've extracted my Teamcity builds as Kotlin outputs. I want to create a base class that defines a number of common steps/settings, but allow individual builds to extend these properties.
e.g.
open class BuildBase(init: BuildBase.() -> Unit) : BuildType({
steps {
powerShell {
name = "Write First Message"
id = "RUNNER_FirstMessage"
scriptMode = script {
content = """
Write-Host "First Message"
""".trimIndent()
}
}
}
})
object Mybuild : BuildBase({
steps { // This would add a new step to the list, without wiping out the original step
powerShell {
name = "Write Last Message"
id = "RUNNER_LastMessage"
scriptMode = script {
content = """
Write-Host "Last Message"
""".trimIndent()
}
}
}
})
In this example, I want to inherit the step from the base class, but add additional steps relevant to the specific build. Additionally, I'd want to inherit the base disableSettings (if any) and disable other steps.
Is this even possible? if so, how would I go about structuring the classes to enable it?
You might have found a solution already but here's how I would solve your problem.
Like in the GUI, TeamCity supports build templates.
In your case you would have a template like following:
object MyBuildTemplate: Template({
id("MyBuildTemplate")
name = "My build template"
steps {
powerShell {
name = "Write First Message"
id = "RUNNER_FirstMessage"
scriptMode = script {
content = """
Write-Host "First Message"
""".trimIndent()
}
}
}
})
Then, you can define a build config extending this template:
object MyBuildConfig: BuildType({
id("MyBuildConfig")
name = "My build config"
steps { // This would add a new step to the list, without wiping out the original step
powerShell {
name = "Write Last Message"
id = "RUNNER_LastMessage"
scriptMode = script {
content = """
Write-Host "Last Message"
""".trimIndent()
}
}
// afaik TeamCity would append the build config's steps to the template's steps but there is way to explicitly define the order of the steps:
stepsOrder = arrayListOf("RUNNER_FirstMessage", "RUNNER_LastMessage")
}
})
This way, you should also be able to inherit disableSettings from the template.

How to show IntelliJ JSON editor in dialog?

I want to show below kind of an editor inside a dialog in an plugin that I'm developing for Kotlin and Java. I tried the below code snippet
editorTextField.setEnabled(true);
editorTextField.setOneLineMode(false);
editorTextField.setFileType(new JsonFileType());
Could someone point out how to achieve this?
Particularly I need the line numbers, JSON syntax highlighting and code folding I can see all the code specifications here. Please help me in learning how can I use them in my plugins.
JSON editor :
class JsonOutputDialog(language: Language, project: Project, text: String) : DialogWrapper(project) {
private val panel = JPanel(BorderLayout())
init {
super.setOKActionEnabled(false)
init()
val editorTextField = CustomEditorField(language, project, text)
editorTextField.setOneLineMode(false)
editorTextField.preferredSize = Dimension(800, 600)
editorTextField.isVisible = true
panel.add(editorTextField)
editorTextField.setCaretPosition(0)
}
override fun createCenterPanel() = panel
}
class CustomEditorField(language: Language, project: Project, s: String) : LanguageTextField(language, project, s) {
override fun createEditor(): EditorEx {
val editor = super.createEditor()
editor.setVerticalScrollbarVisible(true)
editor.setHorizontalScrollbarVisible(true)
val settings = editor.settings
settings.isLineNumbersShown = true
settings.isAutoCodeFoldingEnabled = true
settings.isFoldingOutlineShown = true
settings.isAllowSingleLogicalLineFolding = true
settings.isRightMarginShown=true
return editor
}
}
This is what you need to do. The key here is to use LanguageTextField instead of EditorTextField and override the createEditor() method to configure all the options that you are looking for like Line Numbers and Code Folding.

Gradle Javadoc plugin custom task with Doclava invalid flag -link

I am adding my custom gradle task in my libraries build.gradle with Doclava implementation as:
android.libraryVariants.all { variant ->
task("generateNew${variant.name.capitalize()}Javadoc", type: Javadoc) {
title = ""
destinationDir = new File("${project.getProjectDir()}/doc/compiled/", variant.baseName)
description "Generates Javadoc for $variant.name."
source = variant.javaCompile.source
ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar) + project.files(android.getBootClasspath().join(File.pathSeparator))
options {
memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PRIVATE
links "http://docs.oracle.com/javase/8/docs/api/"
linksOffline "http://d.android.com/reference", "${android.sdkDirectory}/docs/reference"
doclet = "com.google.doclava.Doclava"
List<File> pathList = new ArrayList<File>();
pathList.add(file('./libs/doclava-1.0.6.jar'))
docletpath = pathList
}
exclude '**/BuildConfig.java'
exclude '**/R.java'
}
}
and when I run the task gradle give me the error javadoc: error - invalid flag: -link. But the task can be run when I remove the links & linksOffline option. Another problem is the provided PRIVATE member option is not working, the generated document shows only public members.
Is there any way around to fix it so that with the Doclava doclet I can get the reference links to the Android and Java references?

How to pass parameters or arguments into a Gradle task?

I have a Gradle build script into which I am trying to include Eric Wendelin's CSS plugin.
It's easy enough to implement, and because I only want minification (rather than combining and gzipping), I've got the pertinent parts of the build script looking like this:
minifyCss {
source = "src/main/webapp/css/brandA/styles.css"
dest = "${buildDir}/brandA/styles.css"
yuicompressor {
lineBreakPos = -1
}
}
war {
baseName = 'ex-ren'
}
war.doFirst {
tasks.myTask.minifyCss.execute()
}
This is perfect - when I run the gradle war task, it calls the minifyCss task, takes the source css file, and creates a minified version in the buildDir
However, I have a handful of css files which need minify-ing, but not combining into one file (hence I'm not using the combineCss task)
What I'd like to be able to do is make the source and dest properties (assuming that's the correct terminology?) of the minifyCss task reference variables of some sort - either variables passed into the task in the signature, or global variables, or something ...
Something like this I guess (which doesn't work):
minifyCss(sourceFile, destFile) {
source = sourceFile
dest = destFile
yuicompressor {
lineBreakPos = -1
}
}
war {
baseName = 'ex-ren'
}
war.doFirst {
tasks.myTask.minifyCss.execute("src/main/webapp/css/brandA/styles.css", "${buildDir}/brandA/styles.css")
tasks.myTask.minifyCss.execute("src/main/webapp/css/brandB/styles.css", "${buildDir}/brandB/styles.css")
tasks.myTask.minifyCss.execute("src/main/webapp/css/brandC/styles.css", "${buildDir}/brandC/styles.css")
}
This doesn't work either:
def sourceFile = null
def destFile = null
minifyCss {
source = sourceFile
dest = destFile
yuicompressor {
lineBreakPos = -1
}
}
war {
baseName = 'ex-ren'
}
war.doFirst {
sourceFile = "src/main/webapp/css/brandA/styles.css"
destFile = "${buildDir}/brandA/styles.css"
tasks.myTask.minifyCss.execute()
}
For the life of me I cannot work out how to call a task and pass variables in :(
Any help very much appreciated;
You should consider passing the -P argument in invoking Gradle.
From Gradle Documentation :
--project-prop
Sets a project property of the root project, for example -Pmyprop=myvalue. See Section 14.2, “Gradle properties and system properties”.
Considering this build.gradle
task printProp << {
println customProp
}
Invoking Gradle -PcustomProp=myProp will give this output :
$ gradle -PcustomProp=myProp printProp
:printProp
myProp
BUILD SUCCESSFUL
Total time: 3.722 secs
This is the way I found to pass parameters.
If the task you want to pass parameters to is of type JavaExec and you are using Gradle 5, for example the application plugin's run task, then you can pass your parameters through the --args=... command line option. For example gradle run --args="foo --bar=true".
Otherwise there is no convenient builtin way to do this, but there are 3 workarounds.
1. If few values, task creation function
If the possible values are few and are known in advance, you can programmatically create a task for each of them:
void createTask(String platform) {
String taskName = "myTask_" + platform;
task (taskName) {
... do what you want
}
}
String[] platforms = ["macosx", "linux32", "linux64"];
for(String platform : platforms) {
createTask(platform);
}
You would then call your tasks the following way:
./gradlew myTask_macosx
2. Standard input hack
A convenient hack is to pass the arguments through standard input, and have your task read from it:
./gradlew myTask <<<"arg1 arg2 arg\ in\ several\ parts"
with code below:
String[] splitIntoTokens(String commandLine) {
String regex = "(([\"']).*?\\2|(?:[^\\\\ ]+\\\\\\s+)+[^\\\\ ]+|\\S+)";
Matcher matcher = Pattern.compile(regex).matcher(commandLine);
ArrayList<String> result = new ArrayList<>();
while (matcher.find()) {
result.add(matcher.group());
}
return result.toArray();
}
task taskName, {
doFirst {
String typed = new Scanner(System.in).nextLine();
String[] parsed = splitIntoTokens(typed);
println ("Arguments received: " + parsed.join(" "))
... do what you want
}
}
You will also need to add the following lines at the top of your build script:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Scanner;
3. -P parameters
The last option is to pass a -P parameter to Gradle:
./gradlew myTask -PmyArg=hello
You can then access it as myArg in your build script:
task myTask {
doFirst {
println myArg
... do what you want
}
}
Credit to #789 for his answer on splitting arguments into tokens
I would suggest the method presented on the Gradle forum:
def createMinifyCssTask(def brand, def sourceFile, def destFile) {
return tasks.create("minify${brand}Css", com.eriwen.gradle.css.tasks.MinifyCssTask) {
source = sourceFile
dest = destFile
}
}
I have used this method myself to create custom tasks, and it works very well.
task mathOnProperties << {
println Integer.parseInt(a)+Integer.parseInt(b)
println new Integer(a) * new Integer(b)
}
$ gradle -Pa=3 -Pb=4 mathOnProperties
:mathOnProperties
7
12
BUILD SUCCESSFUL
Its nothing more easy.
run command: ./gradlew clean -PjobId=9999
and
in gradle use: println(project.gradle.startParameter.projectProperties)
You will get clue.
I think you probably want to view the minification of each set of css as a separate task
task minifyBrandACss(type: com.eriwen.gradle.css.tasks.MinifyCssTask) {
source = "src/main/webapp/css/brandA/styles.css"
dest = "${buildDir}/brandA/styles.css"
}
etc etc
BTW executing your minify tasks in an action of the war task seems odd to me - wouldn't it make more sense to make them a dependency of the war task?
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