// Copyright 2019 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import com.google.common.base.CaseFormat import java.util.Optional plugins { id 'java-library' id 'maven-publish' } // Path to code generated by ad hoc tasks in this project. A separate path is // used for easy inspection. def generatedDir = "${project.buildDir}/generated/sources/custom/java/main" def resourcesDir = "${project.buildDir}/resources/main" def screenshotsDir = "${project.buildDir}/screenshots" def screenshotsForGoldensDir = "${project.buildDir}/screenshots_for_goldens" def newGoldensDir = "${project.buildDir}/new_golden_images" def goldensDir = "${javaTestDir}/google/registry/webdriver/goldens/chrome-linux" def jsDir = "${project.projectDir}/src/main/javascript" // Tests that conflict with (mostly unidentified) members of the main test // suite. It is unclear if they are offenders (i.e., those that pollute global // state) or victims. // TODO(weiminyu): identify cause and fix offending tests. def outcastTestPatterns = [ // Problem seems to lie with AppEngine TaskQueue for test. "google/registry/batch/RefreshDnsOnHostRenameActionTest.*", "google/registry/flows/CheckApiActionTest.*", "google/registry/flows/EppLifecycleHostTest.*", "google/registry/flows/domain/DomainCreateFlowTest.*", "google/registry/flows/domain/DomainUpdateFlowTest.*", "google/registry/tools/CreateDomainCommandTest.*", ] // Tests that fail when running Gradle in a docker container, e. g. when // building the release artifacts in Google Cloud Build. def dockerIncompatibleTestPatterns = [ // The webdriver tests start headless Chrome in a Docker container, // resulting in Docker-in-Docker complications. Likewise, // GenerateSqlSchemaCommandTest and DumpGoldenSchemaCommandTest launch // postgresql in a docker container. "google/registry/webdriver/*", "google/registry/tools/GenerateSqlSchemaCommandTest.*", "google/registry/tools/DumpGoldenSchemaCommandTest.*", // PathParameterTest includes tests which validate that file permissions are // respected. However when running in Docker the user is root by default, so // every file is read/write-able. There is no way to exclude specific test // methods, so we exclude the whole test class. "google/registry/tools/params/PathParameterTest.*", "google/registry/persistence/PersistenceModuleTest.*", ] // Tests that conflict with members of both the main test suite and the // outcast suite. They seem to be affected by global states outside of // Nomulus classes, e.g., threads and objects retained by frameworks. // TODO(weiminyu): identify cause and fix offending tests. def fragileTestPatterns = [ // Changes cache timeouts and for some reason appears to have contention // with other tests. "google/registry/whois/WhoisCommandFactoryTest.*", // Currently changes a global configuration parameter that for some reason // results in timestamp inversions for other tests. TODO(mmuller): fix. "google/registry/flows/host/HostInfoFlowTest.*", ] + dockerIncompatibleTestPatterns sourceSets { main { java { srcDirs += generatedDir } resources { exclude '**/*.xjb' } } nonprod { java { compileClasspath += main.output // Add the DB runtime classpath to nonprod so we can load the flyway // scripts. runtimeClasspath += main.output + rootProject.project(":db").sourceSets.main.runtimeClasspath } } test { java { compileClasspath += nonprod.output runtimeClasspath += nonprod.output // Javadoc API is deprecated in Java 11 and removed in Java 12. // TODO(jianglai): re-enable after migrating to the new Javadoc API if ((JavaVersion.current().majorVersion as Integer) >= 11) { exclude 'google/registry/documentation/**' } } resources { exclude '**/*.xjb', '**/*.xsd' } } } processTestResources { exclude '**/webdriver/*' } configurations { css jaxb soy closureCompiler devtool nonprodImplementation.extendsFrom implementation nonprodRuntime.extendsFrom runtime testImplementation.extendsFrom nonprodImplementation testRuntimeOnly.extendsFrom nonprodRuntime // Published jars that are used for server/schema compatibility tests. // See the integration project // for details. nomulus_test // Exclude non-canonical servlet-api jars. Our AppEngine deployment uses // javax.servlet:servlet-api:2.5 // For reasons we do not understand, marking the following dependencies as // compileOnly instead of compile does not exclude them from runtimeClasspath. all { // servlet-api:3.1 pulled in but not used by soy compiler exclude group: 'javax.servlet', module: 'javax.servlet-api' // Jetty's servlet-api:2.5 implementation, pulled in by other Jetty jars exclude group: 'org.mortbay.jetty', module: 'servlet-api' } } // Known issues: // - The (test/)compile/runtime labels are deprecated. We continue using these // labels due to nebula-lint. // TODO(weiminyu): switch to api/implementation labels. // See https://github.com/nebula-plugins/gradle-lint-plugin/issues/130 for // issue status. // - Nebula-lint's conflict between unused and undeclared dependency check. // If an undeclared dependency is added, the unused-dependency check will flag // it. For now we wrap affected dependency in gradleLint.ignore block. // TODO(weiminyu): drop gradleLint.ignore block when issue is fixed. // See https://github.com/nebula-plugins/gradle-lint-plugin/issues/181 for // issue status. dependencies { def deps = rootProject.dependencyMap testRuntimeOnly files(sourceSets.test.resources.srcDirs) implementation deps['com.beust:jcommander'] implementation deps['com.github.ben-manes.caffeine:caffeine'] implementation deps['com.google.api:gax'] implementation deps['com.google.api.grpc:proto-google-common-protos'] implementation deps['com.google.api.grpc:proto-google-cloud-secretmanager-v1'] implementation deps['com.google.api-client:google-api-client'] implementation deps['com.google.api-client:google-api-client-appengine'] implementation deps['com.google.api-client:google-api-client-servlet'] implementation deps['com.google.monitoring-client:metrics'] implementation deps['com.google.monitoring-client:stackdriver'] implementation deps['com.google.api-client:google-api-client-java6'] implementation deps['com.google.api.grpc:proto-google-cloud-tasks-v2'] implementation deps['com.google.apis:google-api-services-admin-directory'] implementation deps['com.google.apis:google-api-services-appengine'] implementation deps['com.google.apis:google-api-services-bigquery'] implementation deps['com.google.apis:google-api-services-dataflow'] implementation deps['com.google.apis:google-api-services-dns'] implementation deps['com.google.apis:google-api-services-drive'] implementation deps['com.google.apis:google-api-services-groupssettings'] implementation deps['com.google.apis:google-api-services-monitoring'] implementation deps['com.google.apis:google-api-services-sheets'] implementation deps['com.google.apis:google-api-services-storage'] testImplementation deps['com.google.appengine:appengine-api-stubs'] implementation deps['com.google.auth:google-auth-library-credentials'] implementation deps['com.google.auth:google-auth-library-oauth2-http'] implementation deps['com.google.cloud.bigdataoss:util'] implementation deps['com.google.cloud.sql:jdbc-socket-factory-core'] runtimeOnly deps['com.google.cloud.sql:postgres-socket-factory'] implementation deps['com.google.cloud:google-cloud-secretmanager'] implementation deps['com.google.code.gson:gson'] implementation deps['com.google.auto.service:auto-service-annotations'] implementation deps['com.google.auto.value:auto-value-annotations'] implementation deps['com.google.code.findbugs:jsr305'] implementation deps['com.google.dagger:dagger'] implementation deps['com.google.errorprone:error_prone_annotations'] implementation deps['com.google.flogger:flogger'] runtime deps['com.google.flogger:flogger-system-backend'] implementation deps['com.google.guava:guava'] implementation deps['com.google.protobuf:protobuf-java'] gradleLint.ignore('unused-dependency') { implementation deps['com.google.gwt:gwt-user'] } implementation deps['com.google.cloud:google-cloud-core'] implementation deps['com.google.cloud:google-cloud-storage'] implementation deps['com.google.cloud:google-cloud-tasks'] implementation deps['com.google.http-client:google-http-client'] implementation deps['com.google.http-client:google-http-client-appengine'] implementation deps['com.google.http-client:google-http-client-jackson2'] implementation deps['com.google.oauth-client:google-oauth-client'] implementation deps['com.google.oauth-client:google-oauth-client-java6'] implementation deps['com.google.oauth-client:google-oauth-client-jetty'] implementation deps['com.google.oauth-client:google-oauth-client-appengine'] implementation deps['com.google.oauth-client:google-oauth-client-servlet'] implementation deps['com.google.protobuf:protobuf-java'] implementation deps['com.google.re2j:re2j'] implementation deps['com.google.template:soy'] implementation deps['com.googlecode.json-simple:json-simple'] implementation deps['com.jcraft:jsch'] testImplementation deps['com.thoughtworks.qdox:qdox'] implementation deps['com.zaxxer:HikariCP'] implementation deps['dnsjava:dnsjava'] runtime deps['guru.nidi:graphviz-java-all-j2v8'] testImplementation deps['io.github.classgraph:classgraph'] testRuntimeOnly deps['io.github.java-diff-utils:java-diff-utils'] testImplementation deps['javax.annotation:javax.annotation-api'] testImplementation deps['javax.annotation:jsr250-api'] implementation deps['javax.mail:mail'] implementation deps['javax.inject:javax.inject'] implementation deps['javax.persistence:javax.persistence-api'] implementation deps['javax.servlet:servlet-api'] implementation deps['javax.xml.bind:jaxb-api'] implementation deps['jline:jline'] implementation deps['joda-time:joda-time'] implementation deps['org.apache.avro:avro'] testImplementation deps['org.apache.beam:beam-runners-core-construction-java'] testImplementation deps['org.apache.beam:beam-runners-direct-java'] implementation deps['org.apache.beam:beam-runners-google-cloud-dataflow-java'] implementation deps['org.apache.beam:beam-sdks-java-core'] implementation deps['org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core'] implementation deps['org.apache.beam:beam-sdks-java-io-google-cloud-platform'] implementation deps['org.apache.commons:commons-csv'] implementation deps['org.apache.commons:commons-lang3'] testImplementation deps['org.apache.commons:commons-text'] testImplementation deps['org.apache.ftpserver:ftplet-api'] testImplementation deps['org.apache.ftpserver:ftpserver-core'] implementation deps['org.apache.httpcomponents:httpclient'] implementation deps['org.apache.httpcomponents:httpcore'] testImplementation deps['org.apache.sshd:sshd-core'] testImplementation deps['org.apache.sshd:sshd-scp'] testImplementation deps['org.apache.sshd:sshd-sftp'] testImplementation deps['org.apache.tomcat:tomcat-annotations-api'] implementation deps['org.bouncycastle:bcpg-jdk15on'] implementation deps['org.bouncycastle:bcpkix-jdk15on'] implementation deps['org.bouncycastle:bcprov-jdk15on'] testImplementation deps['com.fasterxml.jackson.core:jackson-databind'] runtime deps['org.glassfish.jaxb:jaxb-runtime'] implementation deps['org.hibernate:hibernate-core'] implementation deps['org.hibernate:hibernate-hikaricp'] implementation deps['org.joda:joda-money'] implementation deps['org.json:json'] implementation deps['org.jsoup:jsoup'] testImplementation deps['org.mortbay.jetty:jetty'] implementation deps['org.postgresql:postgresql'] implementation "org.eclipse.jetty:jetty-server:9.4.49.v20220914" implementation "org.eclipse.jetty:jetty-servlet:9.4.49.v20220914" testImplementation deps['org.seleniumhq.selenium:selenium-api'] testImplementation deps['org.seleniumhq.selenium:selenium-chrome-driver'] testImplementation deps['org.seleniumhq.selenium:selenium-java'] testImplementation deps['org.seleniumhq.selenium:selenium-remote-driver'] runtimeOnly deps['org.slf4j:slf4j-jdk14'] testImplementation deps['org.testcontainers:jdbc'] testImplementation deps['org.testcontainers:junit-jupiter'] implementation deps['org.testcontainers:postgresql'] testImplementation deps['org.testcontainers:selenium'] testImplementation deps['org.testcontainers:testcontainers'] implementation deps['us.fatehi:schemacrawler'] implementation deps['us.fatehi:schemacrawler-api'] implementation deps['us.fatehi:schemacrawler-diagram'] implementation deps['us.fatehi:schemacrawler-tools'] implementation deps['xerces:xmlParserAPIs'] implementation deps['xpp3:xpp3'] // This dependency must come after javax.mail:mail as it would otherwise // shadow classes in package javax.mail with its own implementation. implementation deps['com.google.appengine:appengine-api-1.0-sdk'] // Known issue: nebula-lint misses inherited dependency. implementation project(':common') testImplementation project(path: ':common', configuration: 'testing') implementation project(':util') // Import NomulusPostreSql from ':db' for implementation but exclude dependencies. implementation project(path: ':db', configuration: 'implementationApi') testRuntimeOnly project(':db') annotationProcessor deps['com.google.auto.service:auto-service'] annotationProcessor deps['com.google.auto.value:auto-value'] testAnnotationProcessor deps['com.google.auto.value:auto-value'] annotationProcessor deps['com.google.dagger:dagger-compiler'] testAnnotationProcessor deps['com.google.dagger:dagger-compiler'] annotationProcessor project(':processor') testAnnotationProcessor project(':processor') testImplementation deps['com.google.cloud:google-cloud-nio'] testImplementation deps['com.google.appengine:appengine-testing'] testImplementation deps['com.google.guava:guava-testlib'] testImplementation deps['com.google.monitoring-client:contrib'] testImplementation deps['com.google.protobuf:protobuf-java-util'] testImplementation deps['com.google.truth:truth'] testImplementation deps['com.google.truth.extensions:truth-java8-extension'] testImplementation deps['org.checkerframework:checker-qual'] testImplementation deps['org.hamcrest:hamcrest'] testImplementation deps['org.hamcrest:hamcrest-core'] testImplementation deps['org.hamcrest:hamcrest-library'] testImplementation deps['junit:junit'] testImplementation deps['org.junit.jupiter:junit-jupiter-api'] testImplementation deps['org.junit.jupiter:junit-jupiter-engine'] testImplementation deps['org.junit.jupiter:junit-jupiter-migrationsupport'] testImplementation deps['org.junit.jupiter:junit-jupiter-params'] testImplementation deps['org.junit-pioneer:junit-pioneer'] testImplementation deps['org.junit.platform:junit-platform-runner'] testImplementation deps['org.junit.platform:junit-platform-suite-api'] testImplementation deps['org.mockito:mockito-core'] testImplementation deps['org.mockito:mockito-junit-jupiter'] runtime deps['org.postgresql:postgresql'] // Indirect dependency found by undeclared-dependency check. Such // dependencies should go after all other implementation and testImplementation // dependencies to avoid overriding them accidentally. implementation deps['com.google.oauth-client:google-oauth-client-java6'] // Dependencies needed for jaxb compilation. // Use jaxb 2.2.11 because 2.3 is known to break the Ant task we use. // TODO: upgrade jaxb versions to 2.4.0, already in beta by Sept 2018 jaxb deps['javax.xml.bind:jaxb-api'] jaxb deps['com.sun.activation:javax.activation'] jaxb deps['com.sun.xml.bind:jaxb-xjc'] jaxb deps['com.sun.xml.bind:jaxb-impl'] jaxb deps['com.sun.xml.bind:jaxb-osgi'] // Dependency needed for soy to java compilation. soy deps['com.google.inject:guice'] soy deps['com.google.protobuf:protobuf-java'] soy deps['com.google.template:soy'] // Dependencies needed for compiling stylesheets to javascript css deps['com.google.closure-stylesheets:closure-stylesheets'] css deps['args4j:args4j'] // Tool dependencies. used for doc generation. implementation files("${System.properties['java.home']}/../lib/tools.jar") // Flyway classes needed to generate the golden file. implementation deps['org.flywaydb:flyway-core'] closureCompiler deps['com.google.javascript:closure-compiler'] } task jaxbToJava { def xsdFilesDir = "${javaDir}/google/registry/xml/xsd" def bindingsFile = "${javaDir}/google/registry/xjc/bindings.xjb" def pkgInfoTemplate = "${javaDir}/google/registry/xjc/package-info.java.in" def pkgInfoMap = "${javaDir}/google/registry/xjc/package-info.map" def outputDir = "${generatedDir}/google/registry/xjc" inputs.dir xsdFilesDir inputs.files bindingsFile, pkgInfoTemplate, pkgInfoMap outputs.dir outputDir doLast { file(generatedDir).mkdirs() // Temp dir to hold schema and bindings files. Files must be in the same // directory because the bindings (.xjb) file does not declare relative // paths to schema (.xsd) files. def xjcTempSourceDir = file("${temporaryDir}/xjc") xjcTempSourceDir.mkdirs() ant.copy( todir: "${xjcTempSourceDir}", overwrite: true) { fileSet( dir: xsdFilesDir, includes: '**.xsd') } ant.copy( todir: "${xjcTempSourceDir}", overwrite: true, file: bindingsFile) ant.taskdef( name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath) ant.xjc( destdir: "${generatedDir}", binding: "${xjcTempSourceDir}/bindings.xjb", removeOldOutput: 'yes', extension: 'true') { project.fileTree( dir: new File("$xjcTempSourceDir"), include: ['**/*.xsd']) .addToAntBuilder(ant, 'schema', FileCollection.AntType.FileSet) // -npa: do not generate package-info.java files. They will be generated // below. arg(line: '-npa -quiet -extension') } new File(pkgInfoMap).eachLine { line -> def (packageName, namespace) = line.split() ant.copy( tofile: "${outputDir}/${packageName}/package-info.java", overwrite: true, file: "${pkgInfoTemplate}") { filterSet() { filter(token: 'PACKAGE', value: packageName) filter(token: 'NAMESPACE', value: namespace) } } } execInBash( "find . -name *.java -exec sed -i -e '/" + /\*

$/ + "/d' {} +", generatedDir) } } task soyToJava { // Relative paths of soy directories. def spec11SoyDir = "google/registry/reporting/spec11/soy" def toolsSoyDir = "google/registry/tools/soy" def registrarSoyDir = "google/registry/ui/soy/registrar" def soyRelativeDirs = [spec11SoyDir, toolsSoyDir, registrarSoyDir] soyRelativeDirs.each { inputs.dir "${resourcesSourceDir}/${it}" outputs.dir "${generatedDir}/${it}" } ext.soyToJava = { javaPackage, outputDirectory, soyFiles -> javaexec { main = "com.google.template.soy.SoyParseInfoGenerator" classpath configurations.soy args "--javaPackage", "${javaPackage}", "--outputDirectory", "${outputDirectory}", "--javaClassNameSource", "filename", "--allowExternalCalls", "true", "--srcs", "${soyFiles.join(',')}", "--compileTimeGlobalsFile", "${resourcesSourceDir}/google/registry/ui/globals.txt" } // Replace the "@link" tags after the "@deprecated" tags in the generated // files. The soy compiler doesn't generate imports for these, causing // us to get warnings when we generate javadocs. // TODO(b/200296387): To be fair, the deprecations are accurate: we're // using the old "SoyInfo" classes instead of the new "Templates" files. // When we convert to the new classes, this hack can go away. def outputs = fileTree(outputDirectory) { include '**/*.java' } outputs.each { file -> exec { commandLine 'sed', '-i""', '-e', 's/@link/LINK/g', file.getCanonicalPath() } } } doLast { soyToJava('google.registry.tools.soy', "${generatedDir}/${toolsSoyDir}", fileTree( dir: "${resourcesSourceDir}/${toolsSoyDir}", include: ['**/*.soy'])) soyToJava('google.registry.ui.soy.registrar', "${generatedDir}/${registrarSoyDir}", fileTree( dir: "${resourcesSourceDir}/${registrarSoyDir}", include: ['**/*.soy'])) soyToJava('google.registry.reporting.spec11.soy', "${generatedDir}/${spec11SoyDir}", fileTree( dir: "${resourcesSourceDir}/${spec11SoyDir}", include: ['**/*.soy'])) } } task soyToJS(type: JavaExec) { def rootSoyDirectory = "${resourcesSourceDir}/google/registry/ui/soy/registrar" def outputSoyDirectory = "${generatedDir}/google/registry/ui/soy/registrar" inputs.dir rootSoyDirectory outputs.dir outputSoyDirectory def inputSoyFiles = files { file("${rootSoyDirectory}").listFiles() }.filter { it.name.endsWith(".soy") } classpath configurations.soy main = "com.google.template.soy.SoyToJsSrcCompiler" args "--outputPathFormat", "${outputSoyDirectory}/{INPUT_FILE_NAME}.js", "--allowExternalCalls", "false", "--srcs", "${inputSoyFiles.join(',')}", "--compileTimeGlobalsFile", "${resourcesSourceDir}/google/registry/ui/globals.txt" } task stylesheetsToJavascript { def cssSourceDir = "${jsDir}/google/registry/ui/css" def outputDir = "${resourcesDir}/google/registry/ui/css" inputs.dir cssSourceDir outputs.dir outputDir ext.cssCompile = { outputName, debug, cssFiles -> javaexec { main = "com.google.common.css.compiler.commandline.ClosureCommandLineCompiler" classpath configurations.css def argsBuffer = [ "--output-file", "${outputName}.css", "--output-source-map", "${outputName}.css.map", "--input-orientation", "LTR", "--output-orientation", "NOCHANGE", "--output-renaming-map", "${outputName}.css.js", "--output-renaming-map-format", "CLOSURE_COMPILED_SPLIT_HYPHENS" ] if (debug) { argsBuffer.addAll(["--rename", "DEBUG", "--pretty-print"]) } else { argsBuffer.addAll(["--rename", "CLOSURE"]) } argsBuffer.addAll(cssFiles) args argsBuffer } } doLast { file("${outputDir}").mkdirs() def ignoredFiles = ["demo_css.css", "registrar_imports_raw.css"] def sourceFiles = [] // include all CSS files that we find except for the ones explicitly ignored fileTree(cssSourceDir).each { if (it.name.endsWith(".css") && !ignoredFiles.contains(it.name)) { sourceFiles << (cssSourceDir + "/" + it.name) } } // The css files have to be passed to the compiler in alphabetic order to // avoid some flaky style issues sourceFiles.sort() cssCompile("${outputDir}/registrar_bin", false, sourceFiles) cssCompile("${outputDir}/registrar_dbg", true, sourceFiles) } } task compileProdJS(type: JavaExec) { def outputDir = "${resourcesDir}/google/registry/ui" def nodeModulesDir = "${rootDir}/node_modules" def cssSourceDir = "${resourcesDir}/google/registry/ui/css" def jsSourceDir = "${jsDir}/google/registry/ui/js" def externsDir = "${jsDir}/google/registry/ui/externs" def soySourceDir = "${generatedDir}/google/registry/ui/soy" [nodeModulesDir, cssSourceDir, jsSourceDir, externsDir, soySourceDir].each { inputs.dir "${it}" } outputs.dir outputDir classpath configurations.closureCompiler main = 'com.google.javascript.jscomp.CommandLineRunner' def closureArgs = [] closureArgs << "--js_output_file=${outputDir}/registrar_bin.js" // sourcemap-related configuration closureArgs << "--create_source_map=${outputDir}/registrar_bin.js.map" closureArgs << "--source_map_include_content=true" closureArgs << "--source_map_location_mapping=${rootDir}/|" closureArgs << "--output_wrapper=\"%output% //# sourceMappingURL=registrar_bin.js.map\"" // compilation options closureArgs << "--compilation_level=ADVANCED" closureArgs << "--entry_point=goog:registry.registrar.main" closureArgs << "--generate_exports" // manually include all the required js files closureArgs << "--js=${nodeModulesDir}/google-closure-library/**/*.js" closureArgs << "--js=${jsDir}/*.js" closureArgs << "--js=${cssSourceDir}/registrar_bin.css.js" closureArgs << "--js=${jsSourceDir}/**.js" closureArgs << "--js=${externsDir}/json.js" closureArgs << "--js=${soySourceDir}/**.js" args closureArgs } compileProdJS.dependsOn soyToJava compileJava.dependsOn jaxbToJava compileJava.dependsOn soyToJava // The Closure JS compiler does not support Windows. It is fine to disable it if // all we want to do is to complile the Java code on Windows. if (!System.properties['os.name'].toLowerCase().contains('windows')) { compileJava.dependsOn compileProdJS assemble.dependsOn compileProdJS } // stylesheetsToJavascript must happen after processResources, which wipes the // resources folder before copying data into it. stylesheetsToJavascript.dependsOn processResources classes.dependsOn stylesheetsToJavascript compileProdJS.dependsOn stylesheetsToJavascript compileProdJS.dependsOn rootProject.npmInstall compileProdJS.dependsOn processResources compileProdJS.dependsOn processTestResources compileProdJS.dependsOn soyToJS // Make testing artifacts available to be depended up on by other projects. // TODO: factor out google.registry.testing to be a separate project. task testJar(type: Jar) { classifier = 'test' from sourceSets.test.output } artifacts { testRuntimeOnly testJar } task findGoldenImages(type: JavaExec) { classpath = sourceSets.test.runtimeClasspath main = 'google.registry.webdriver.GoldenImageFinder' def arguments = [] arguments << "--screenshots_for_goldens_dir=${screenshotsForGoldensDir}" arguments << "--new_goldens_dir=${newGoldensDir}" arguments << "--existing_goldens_dir=${goldensDir}" if (rootProject.findProperty("overrideExistingGoldens") == "true") { arguments << "--override_existing_goldens=true" } args arguments } Optional> getToolArgsList() { // If "-PtoolArgs=..." is present in the command line, use it to set the args, // otherwise use the default flag, which is "--args" to set the args. def toolArgs = rootProject.findProperty("toolArgs") if (toolArgs != null) { def delimiter = '|' toolArgs += delimiter def argsList = [] def currArg = '' for (def i = 0; i < toolArgs.length(); i++) { def currChar = toolArgs[i] if (currChar != delimiter) { currArg += currChar } else if (i != 0 && toolArgs[i - 1] == '\\') { currArg = currArg.substring(0, currArg.length() - 1) + currChar } else { argsList.add(currArg) currArg = '' } } return Optional.of(argsList) } return Optional.empty() } // To run the nomulus tools with these command line tokens: // "--foo", "bar baz", "--qux=quz" // gradle core:registryTool --args="--foo 'bar baz' --qux=quz" // or: // gradle core:registryTool -PtoolArgs="--foo|bar baz|--qux=quz" // Note that the delimiting pipe can be backslash escaped if it is part of a // parameter. ext.createToolTask = { taskName, mainClass, sourceSet = sourceSets.main -> project.tasks.create(taskName, JavaExec) { classpath = sourceSet.runtimeClasspath main = mainClass doFirst { getToolArgsList().ifPresent { args it } } } } createToolTask('registryTool', 'google.registry.tools.RegistryTool') createToolTask( 'devTool', 'google.registry.tools.DevTool', sourceSets.nonprod) createToolTask( 'createSyntheticDomainHistories', 'google.registry.tools.javascrap.CreateSyntheticDomainHistoriesPipeline') project.tasks.create('generateSqlSchema', JavaExec) { classpath = sourceSets.nonprod.runtimeClasspath main = 'google.registry.tools.DevTool' args = [ '-e', 'alpha', 'generate_sql_schema', '--start_postgresql', '-o', "${rootProject.projectRootDir}/db/src/main/resources/sql/schema/" + "db-schema.sql.generated" ] } task generateGoldenImages(type: FilteringTest) { tests = ["**/webdriver/*"] // Sets the maximum number of test executors that may exist at the same time. maxParallelForks 5 systemProperty 'test.screenshot.dir', screenshotsForGoldensDir systemProperty 'test.screenshot.runAllAttempts', 'true' systemProperty 'test.screenshot.maxAttempts', '5' doFirst { new File(screenshotsForGoldensDir).deleteDir() } } generateGoldenImages.finalizedBy(findGoldenImages) createUberJar('nomulus', 'nomulus', 'google.registry.tools.RegistryTool') // Build the Uber jar shared by all flex-template based BEAM pipelines. // This packages more code and dependency than necessary. However, without // restructuring the source tree it is difficult to generate leaner jars. createUberJar( 'beamPipelineCommon', 'beam_pipeline_common', '') // Create beam staging task if the environment is alpha. Production, sandbox and // qa use formally released pipelines through CloudBuild, whereas crash and // alpha use the pipelines staged on alpha deployment project. // // User should install gcloud and login to GCP before invoking this tasks. if (environment == 'alpha') { def pipelines = [ spec11 : [ mainClass: 'google.registry.beam.spec11.Spec11Pipeline', metaData : 'google/registry/beam/spec11_pipeline_metadata.json' ], invoicing : [ mainClass: 'google.registry.beam.billing.InvoicingPipeline', metaData : 'google/registry/beam/invoicing_pipeline_metadata.json' ], expandBilling : [ mainClass: 'google.registry.beam.billing.ExpandRecurringBillingEventsPipeline', metaData : 'google/registry/beam/expand_recurring_billing_events_pipeline_metadata.json' ], rde : [ mainClass: 'google.registry.beam.rde.RdePipeline', metaData : 'google/registry/beam/rde_pipeline_metadata.json' ], resaveAllEppResources: [ mainClass: 'google.registry.beam.resave.ResaveAllEppResourcesPipeline', metaData: 'google/registry/beam/resave_all_epp_resources_pipeline_metadata.json' ], ] project.tasks.create("stageBeamPipelines") { doLast { pipelines.each { if (rootProject.pipeline == ''|| rootProject.pipeline == it.key) { def mainClass = it.value['mainClass'] def metaData = it.value['metaData'] def pipelineName = CaseFormat.UPPER_CAMEL.to( CaseFormat.LOWER_UNDERSCORE, mainClass.substring(mainClass.lastIndexOf('.') + 1)) def imageName = "gcr.io/${gcpProject}/beam/${pipelineName}" def metaDataBaseName = metaData.substring(metaData.lastIndexOf('/') + 1) def uberJarName = tasks.beamPipelineCommon.outputs.files.asPath def command = "\ gcloud dataflow flex-template build \ gs://${gcpProject}-deploy/live/beam/${metaDataBaseName} \ --image-gcr-path ${imageName}:live \ --sdk-language JAVA \ --flex-template-base-image JAVA11 \ --metadata-file ${projectDir}/src/main/resources/${metaData} \ --jar ${uberJarName} \ --env FLEX_TEMPLATE_JAVA_MAIN_CLASS=${mainClass} \ --project ${gcpProject}".toString() rootProject.ext.execInBash(command, '/tmp') } } } }.dependsOn(tasks.beamPipelineCommon) } // A jar with classes and resources from main sourceSet, excluding internal // data. See comments on configurations.nomulus_test above for details. // TODO(weiminyu): release process should build this using the public repo to eliminate the need // for excludes. task nomulusFossJar (type: Jar) { archiveBaseName = 'nomulus' archiveClassifier = 'public' from (project.sourceSets.main.output) { exclude 'google/registry/config/files/**' exclude 'google/registry/proxy/config/**' } from (project.sourceSets.main.output) { include 'google/registry/config/files/default-config.yaml' include 'google/registry/config/files/nomulus-config-unittest.yaml' } } // An UberJar of registry test classes, resources and all dependencies. // See comments on configurations.nomulus_test above for details. createUberJar( 'testUberJar', 'nomulus-tests', '', [project.configurations.testRuntimeClasspath], [project.sourceSets.test.output], [ // Exclude SQL schema, which is a test dependency. 'sql/flyway/**', // ShadowJar includes java source files when used on // sourceSets.test.output. '**/*.java' ]) tasks.testUberJar { archiveClassifier = 'alldeps' } artifacts { nomulus_test nomulusFossJar nomulus_test testUberJar } publishing { repositories { maven { url project.publish_repo } } publications { nomulusTestsPublication(MavenPublication) { groupId 'google.registry' artifactId 'nomulus_test' version project.nomulus_version artifact nomulusFossJar artifact testUberJar } } } task buildToolImage(dependsOn: nomulus, type: Exec) { commandLine 'docker', 'build', '-t', 'nomulus-tool', '.' } // Build the devtool jar. createUberJar( 'devtool', 'devtool', 'google.registry.tools.DevTool', [ project.configurations.nonprodRuntimeClasspath ], [ project.sourceSets.nonprod.output, sourceSets.main.output ], [ '**/*.java' ]) artifacts { devtool devtool } task copyJsFilesForTestServer(dependsOn: assemble, type: Copy) { // Unfortunately the test server relies on having some compiled JS/CSS // in place, so copy it over here from "${resourcesDir}/google/registry/ui/" include '**/*.js' include '**/*.css' into "${project.projectDir}/src/main/resources/google/registry/ui/" } task runTestServer(dependsOn: copyJsFilesForTestServer, type: JavaExec) { main = 'google.registry.server.RegistryTestServerMain' classpath = sourceSets.test.runtimeClasspath } /** * We have to break out the test suites because some of the tests conflict * with one another, but unfortunately this breaks the "--tests" flag. The * --tests flag only applies to the task named on the command line (usually * just "test"), not for all tasks of type "Test". * * As a better solution, FilteringTest sets testNameIncludePatterns (the * internal property that --tests sets) from the value of the "testFilter" * property, allowing us to filter across all the tests in core without * explicitly specifying a test task or causing errors because there are no * matching tests in the main task. * * To use it, define "testFilter" to be a comma-separated collection of class * names (wildcards are allowed): * * ./gradlew test -P testFilter=*.FooBar,google.registry.tools.ShellCommandTest */ class FilteringTest extends Test { FilteringTest() { useJUnitPlatform(); } private void applyTestFilter() { if (project.testFilter) { testNameIncludePatterns = project.testFilter.split(',') // By default, gradle test tasks will produce a failure if no tests // match the include/exclude/filter rules. Since test filtering allows us // to select a set of tests from a particular task, we don't want this // behavior. filter.failOnNoMatchingTests = false } } /** * Set to false if you also want to include TestCase and TestSuite classes. * *

Must be defined before "test", if at all. */ @Input boolean excludeTestCases = true void setTests(List tests) { // Common exclude pattern. See README in parent directory for explanation. if (excludeTestCases) { exclude "**/*TestCase.*", "**/*TestSuite.*" } include tests applyTestFilter() } /** * Include all of the tests (except Test{Case,TestSuite}). This actually * doesn't explicitly "include" anything, in which cast the Test class tries * to include everything that is not explicitly excluded. */ void includeAllTests() { exclude "**/*TestCase.*", "**/*TestSuite.*" applyTestFilter() } } task fragileTest(type: FilteringTest) { // Common exclude pattern. See README in parent directory for explanation. tests = fragileTestPatterns if (rootProject.findProperty("skipDockerIncompatibleTests") == "true") { exclude dockerIncompatibleTestPatterns } // Run every test class in a freshly started process. forkEvery 1 doFirst { new File(screenshotsDir).deleteDir() } } task outcastTest(type: FilteringTest) { tests = outcastTestPatterns // Sets the maximum number of test executors that may exist at the same time. // Note that this number appears to contribute to NoClassDefFoundError // exceptions on certain machines and distros. The root cause is unclear. // Try reducing this number if you experience similar problems. maxParallelForks 3 } // Dedicated test suite for schema-dependent tests. task sqlIntegrationTest(type: FilteringTest) { // TestSuite still requires a JUnit 4 runner, which knows how to handle JUnit 5 tests. // Here we need to override parent's choice of JUnit 5. If changing this, remember to // change :integration:sqlIntegrationTest too. useJUnit() excludeTestCases = false tests = ['google/registry/schema/integration/SqlIntegrationTestSuite.*'] } // Verifies that RegistryTool can be instantiated: // - All dependencies are packaged in nomulus.jar // - JPA setup succeeds. task registryToolIntegrationTest(dependsOn: nomulus, type: FilteringTest) { tests = ['google/registry/tools/RegistryToolTest.*'] testClassesDirs = sourceSets.test.output.classesDirs classpath = nomulus.outputs.files.plus(configurations.testRuntimeClasspath) .plus(files(testClassesDirs)) } task standardTest(type: FilteringTest) { includeAllTests() exclude fragileTestPatterns exclude outcastTestPatterns // See SqlIntegrationTestSuite.java exclude '**/*BeforeSuiteTest.*', '**/*AfterSuiteTest.*' if (rootProject.findProperty("skipDockerIncompatibleTests") == "true") { exclude dockerIncompatibleTestPatterns } // Run every test class in its own process. // Uncomment to unblock build while troubleshooting inexplicable test errors. // This setting makes the build take 35 minutes, without it it takes about 10. // forkEvery 1 // Sets the maximum number of test executors that may exist at the same time. // Also, Gradle executes tests in 1 thread and some of our test infrastructures // depend on that, e.g. DualDatabaseTestInvocationContextProvider injects // different implementation of TransactionManager into TransactionManagerFactory. maxParallelForks 5 systemProperty 'test.projectRoot', rootProject.projectRootDir systemProperty 'test.resourcesDir', resourcesDir } test { // Don't run any tests from this task, all testing gets done in the // FilteringTest tasks. exclude "**" // TODO(weiminyu): Remove dependency on sqlIntegrationTest }.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest, sqlIntegrationTest) // When we override tests, we also break the cleanTest command. cleanTest.dependsOn(cleanFragileTest, cleanOutcastTest, cleanStandardTest, cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest) project.build.dependsOn devtool project.build.dependsOn buildToolImage project.build.dependsOn ':stage'