Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure AppEngine Gradle plugin using Kotlin DSL

As documented on https://cloud.google.com/appengine/docs/standard/java/tools/gradle-reference the AppEngine Gradle plugin offers configuration like:

appengine {  // App Engine tasks configuration
  run {      // local (dev_appserver) configuration (standard environments only)
    port = 8080                 // default
  }

  deploy {   // deploy configuration
    stopPreviousVersion = true  // default - stop the current version
    promote = true              // default - & make this the current version
  }
}

How should such a configuration look like when using a build.gradlke.kts?

I was looking at to see the AppEngine tasks but don't understand to connect this to a proper Kotlin DSL setup.

EDIT

When simply adding the above block to the build.gradle.kts IntelliJ complains with:

  • Unresolved reference: port
  • Unresolved reference: deploy

and when running Gradle from cml I get:

Could not open cache directory azhqxsd1d4xoovq4o5dzec6iw (/Users/test/.gradle/caches/4.5/gradle-kotlin-dsl/azhqxsd1d4xoovq4o5dzec6iw). Internal error: unable to compile script, see log for details

EDIT2

Added plugins and buildscript blocks below:

val kotlinVersion                    = property("kotlin.version")
val javaVersion                      = "1.8"

buildscript {
    repositories {
        jcenter()
        mavenCentral()
        mavenLocal()
        maven("https://plugins.gradle.org/m2/")
        maven("https://repo.spring.io/milestone")
        maven("https://repo.spring.io/snapshot")
    }
    dependencies {
        classpath("com.google.cloud.tools:appengine-gradle-plugin:1.3.4")
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.0.BUILD-SNAPSHOT")
    }
}

apply {
    plugin("com.google.cloud.tools.appengine")
    plugin("org.springframework.boot")
}

plugins {
    val kotlinVersion = "1.2.0"
    `war`
    `idea`
    id("org.jetbrains.kotlin.jvm") version kotlinVersion
    id("org.jetbrains.kotlin.kapt") version kotlinVersion
    id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion
    id("org.jetbrains.kotlin.plugin.noarg") version kotlinVersion
    id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion
    id("com.ewerk.gradle.plugins.querydsl") version "1.0.9"
    id("io.spring.dependency-management") version "1.0.3.RELEASE"
}

EDIT3

I see this being generated by kotlinDslAccessorsReport:

/**
 * Retrieves the [appengine][com.google.cloud.tools.gradle.appengine.core.AppEngineExtension] project extension.
 */
val Project.`appengine`: com.google.cloud.tools.gradle.appengine.core.AppEngineExtension get() =
    extensions.getByName("appengine") as com.google.cloud.tools.gradle.appengine.core.AppEngineExtension

/**
 * Configures the [appengine][com.google.cloud.tools.gradle.appengine.core.AppEngineExtension] project extension.
 */
fun Project.`appengine`(configure: com.google.cloud.tools.gradle.appengine.core.AppEngineExtension.() -> Unit): Unit =
    extensions.configure("appengine", configure)

But honestly I have no idea how this could help me further.

like image 570
Marcel Overdijk Avatar asked Jan 29 '18 13:01

Marcel Overdijk


2 Answers

In order to have kotlin-dsl generate static accessors before compile time for applied plugins you must use the plugins {} block and not the buildscript {} block. buildscript {} will still make the dependencies visible to the script classpath, but you will not get those.

As you noticed, the plugin's Maven coordinates that may be different than the plugin Id. You can handle this in the settings.gradle with the pluginManagement specification (an example for Android plugin is here. Here is how I handle that (and using war plugin for minimal application):

build.gradle,kts

plugins {
  id("com.google.cloud.tools.appengine") version "1.3.4"
  `war`
}

settings.gradle

pluginManagement {
  repositories {
    gradlePluginPortal()
    google()
  }
  resolutionStrategy {
    eachPlugin {
      if (requested.id.id == "com.google.cloud.tools.appengine") {
        useModule("com.google.cloud.tools:appengine-gradle-plugin:${requested.version}")
      }
    }
  }
}

Now, I have the plugin applied and kotlin-dsl will generate the accessors before script compilation.

Running ./gradlew kotlinDslAccessorsReport and perusing through it I see this in the output:

/**
 * Retrieves the [appengine][com.google.cloud.tools.gradle.appengine.core.AppEngineExtension] project extension.
 */
val Project.`appengine`: com.google.cloud.tools.gradle.appengine.core.AppEngineExtension get() =
  extensions.getByName("appengine") as com.google.cloud.tools.gradle.appengine.core.AppEngineExtension

/**
 * Configures the [appengine][com.google.cloud.tools.gradle.appengine.core.AppEngineExtension] project extension.
 */
fun Project.`appengine`(configure: com.google.cloud.tools.gradle.appengine.core.AppEngineExtension.() -> Unit): Unit =
    extensions.configure("appengine", configure)

Now, you can see that the appengine { ... } code block will work correctly in the top level. We just need to figure out what can go inside of it based on its type. Note that if we were using buildscript {} instead of plugins {}, you would have to either copy/paste these accessors yourself or do something like extensions.getByType(com.google.cloud.tools.gradle.appengine.core.AppEngineExtension::class) in your build script.

Doing some searching you can find the source code for AppEngineExtension on GitHub. Unfortunately, it does not have any methods or fields on it. It is basically used as an "extension holder" class, in that other extensions are added to it here and here (and probably other places). This means that we need to do some class cast tricks to be able to configure this object. The source code is IMO the only way to really figure out how to access these kinds of objects.

Below shows how we can configure the deploy extension, which is a DeployExtension and how we can configure the run extension, which is a RunExtension.

import com.google.cloud.tools.gradle.appengine.core.DeployExtension
import com.google.cloud.tools.gradle.appengine.standard.RunExtension

appengine {
  ((this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("run") as RunExtension).apply {
    port = 8080
  }
  ((this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("deploy") as DeployExtension).apply {
    stopPreviousVersion = true // default - stop the current version
    promote = true
  }
}

There are a few different ways to accomplish the above, but that is the approach I took. The plugin itself should offer friendlier methods for configuration until kotlin-dsl/457 is resolved, so I opened an issue

like image 108
mkobit Avatar answered Nov 03 '22 00:11

mkobit


Type-safe approach using version 2.0 of the appengine gradle plugin:

import com.google.cloud.tools.gradle.appengine.standard.AppEngineStandardExtension

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath("com.google.cloud.tools:appengine-gradle-plugin:2.0.0-rc5")
    }
}

plugins {
    java
    war
    kotlin("jvm") version "..."
}

repositories {
    jcenter()
}
apply {
    plugin("com.google.cloud.tools.appengine")
}

configure<AppEngineStandardExtension> {
    deploy {
        projectId = "..."
        version = "..."
        stopPreviousVersion = true // etc
    }
}
like image 24
MeinAccount Avatar answered Nov 03 '22 01:11

MeinAccount