2017-02-11 12:12:26 -05:00
|
|
|
import org.elasticsearch.gradle.MavenFilteringHack
|
|
|
|
import org.elasticsearch.gradle.test.NodeInfo
|
2016-11-21 04:52:55 -05:00
|
|
|
|
2016-09-29 06:03:14 -04:00
|
|
|
import java.nio.charset.StandardCharsets
|
|
|
|
import java.nio.file.Files
|
|
|
|
import java.nio.file.Path
|
|
|
|
import java.nio.file.StandardCopyOption
|
2016-07-25 20:09:54 -04:00
|
|
|
|
2016-09-29 06:03:14 -04:00
|
|
|
group 'org.elasticsearch.plugin'
|
|
|
|
|
|
|
|
apply plugin: 'elasticsearch.esplugin'
|
|
|
|
esplugin {
|
|
|
|
name 'x-pack'
|
|
|
|
description 'Elasticsearch Expanded Pack Plugin'
|
|
|
|
classname 'org.elasticsearch.xpack.XPackPlugin'
|
2017-03-27 15:53:03 -04:00
|
|
|
hasNativeController true
|
2017-03-02 22:06:15 -05:00
|
|
|
licenseFile project(':x-pack-elasticsearch').file('LICENSE.txt')
|
|
|
|
noticeFile project(':x-pack-elasticsearch').file('NOTICE.txt')
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
2016-10-03 19:38:04 -04:00
|
|
|
archivesBaseName = 'x-pack' // for api jar
|
2016-09-29 06:03:14 -04:00
|
|
|
|
|
|
|
// TODO: fix this! https://github.com/elastic/x-plugins/issues/1066
|
|
|
|
ext.compactProfile = 'full'
|
|
|
|
|
2017-03-02 22:06:15 -05:00
|
|
|
dependencyLicenses {
|
|
|
|
mapping from: /netty-.*/, to: 'netty'
|
|
|
|
mapping from: /bc.*/, to: 'bouncycastle'
|
|
|
|
mapping from: /owasp-java-html-sanitizer.*/, to: 'owasp-java-html-sanitizer'
|
|
|
|
mapping from: /transport-netty.*/, to: 'elasticsearch'
|
2017-07-13 03:44:53 -04:00
|
|
|
mapping from: /elasticsearch-rest-client.*/, to: 'elasticsearch'
|
2017-03-02 22:06:15 -05:00
|
|
|
mapping from: /http.*/, to: 'httpclient' // pulled in by rest client
|
|
|
|
mapping from: /commons-.*/, to: 'commons' // pulled in by rest client
|
2017-07-13 03:44:53 -04:00
|
|
|
ignoreSha 'elasticsearch-rest-client'
|
2017-03-02 22:06:15 -05:00
|
|
|
ignoreSha 'transport-netty4'
|
2017-07-13 03:44:53 -04:00
|
|
|
ignoreSha 'elasticsearch-rest-client-sniffer'
|
2017-03-02 22:06:15 -05:00
|
|
|
}
|
2016-09-29 06:03:14 -04:00
|
|
|
|
|
|
|
licenseHeaders {
|
|
|
|
approvedLicenses << 'BCrypt (BSD-like)'
|
|
|
|
additionalLicense 'BCRYP', 'BCrypt (BSD-like)', 'Copyright (c) 2006 Damien Miller <djm@mindrot.org>'
|
|
|
|
}
|
|
|
|
|
2017-02-08 11:58:57 -05:00
|
|
|
configurations {
|
2017-03-19 11:58:19 -04:00
|
|
|
nativeBundle {
|
2017-02-08 11:58:57 -05:00
|
|
|
resolutionStrategy.dependencySubstitution {
|
2017-05-26 04:15:12 -04:00
|
|
|
if (findProject(':machine-learning-cpp') != null) {
|
|
|
|
substitute module("org.elasticsearch.ml:ml-cpp") with project(":machine-learning-cpp")
|
|
|
|
} else {
|
|
|
|
substitute module("org.elasticsearch.ml:ml-cpp") with project("${project.path}:ml-cpp-snapshot")
|
|
|
|
}
|
2017-02-08 11:58:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-29 06:03:14 -04:00
|
|
|
dependencies {
|
|
|
|
// security deps
|
|
|
|
compile project(path: ':modules:transport-netty4', configuration: 'runtime')
|
2016-10-20 06:37:30 -04:00
|
|
|
compile 'com.unboundid:unboundid-ldapsdk:3.2.0'
|
2016-09-29 06:03:14 -04:00
|
|
|
compile 'org.bouncycastle:bcprov-jdk15on:1.55'
|
|
|
|
compile 'org.bouncycastle:bcpkix-jdk15on:1.55'
|
|
|
|
testCompile 'com.google.jimfs:jimfs:1.1'
|
|
|
|
|
|
|
|
// watcher deps
|
|
|
|
compile 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:r239'
|
|
|
|
compile 'com.google.guava:guava:16.0.1' // needed by watcher for the html sanitizer and security tests for jimfs
|
2017-03-21 05:28:20 -04:00
|
|
|
compile 'com.sun.mail:javax.mail:1.5.6'
|
2016-09-29 06:03:14 -04:00
|
|
|
// HACK: java 9 removed javax.activation from the default modules, so instead of trying to add modules, which would have
|
|
|
|
// to be conditionalized for java 8/9, we pull in the classes directly
|
2017-03-21 05:28:20 -04:00
|
|
|
compile 'javax.activation:activation:1.1.1'
|
2016-09-29 06:03:14 -04:00
|
|
|
|
|
|
|
testCompile 'org.subethamail:subethasmtp:3.1.7'
|
|
|
|
// needed for subethasmtp, has @GuardedBy annotation
|
|
|
|
testCompile 'com.google.code.findbugs:jsr305:3.0.1'
|
|
|
|
|
|
|
|
// monitoring deps
|
2017-07-13 03:44:53 -04:00
|
|
|
compile "org.elasticsearch.client:elasticsearch-rest-client:${version}"
|
|
|
|
compile "org.elasticsearch.client:elasticsearch-rest-client-sniffer:${version}"
|
2016-09-29 06:03:14 -04:00
|
|
|
|
2017-02-08 11:58:57 -05:00
|
|
|
// ml deps
|
2017-02-09 17:08:36 -05:00
|
|
|
compile 'net.sf.supercsv:super-csv:2.4.0'
|
2017-05-26 04:15:12 -04:00
|
|
|
nativeBundle "org.elasticsearch.ml:ml-cpp:${project.version}@zip"
|
2017-02-09 17:08:36 -05:00
|
|
|
testCompile 'org.ini4j:ini4j:0.5.2'
|
2017-02-08 11:58:57 -05:00
|
|
|
|
2016-09-29 06:03:14 -04:00
|
|
|
// common test deps
|
|
|
|
testCompile 'org.elasticsearch:securemock:1.2'
|
2017-01-05 12:29:31 -05:00
|
|
|
testCompile "org.elasticsearch:mocksocket:${versions.mocksocket}"
|
2016-09-29 06:03:14 -04:00
|
|
|
testCompile 'org.slf4j:slf4j-log4j12:1.6.2'
|
|
|
|
testCompile 'org.slf4j:slf4j-api:1.6.2'
|
2017-05-11 14:23:55 -04:00
|
|
|
testCompile project(path: ':modules:reindex', configuration: 'runtime')
|
2017-05-12 09:59:40 -04:00
|
|
|
testCompile project(path: ':modules:parent-join', configuration: 'runtime')
|
2017-06-15 14:50:31 -04:00
|
|
|
testCompile project(path: ':modules:analysis-common', configuration: 'runtime')
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// make LicenseSigner available for testing signed licenses
|
|
|
|
sourceSets.test.java {
|
|
|
|
srcDir '../license-tools/src/main/java'
|
|
|
|
}
|
|
|
|
|
|
|
|
compileJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked"
|
|
|
|
compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked"
|
|
|
|
|
|
|
|
ext.expansions = [
|
|
|
|
'project.version': version,
|
|
|
|
]
|
|
|
|
|
|
|
|
processResources {
|
|
|
|
from(sourceSets.main.resources.srcDirs) {
|
|
|
|
exclude '**/public.key'
|
|
|
|
inputs.properties(expansions)
|
|
|
|
MavenFilteringHack.filter(it, expansions)
|
|
|
|
}
|
|
|
|
boolean snapshot = "true".equals(System.getProperty("build.snapshot", "true"))
|
|
|
|
if (snapshot) {
|
2016-10-01 03:46:43 -04:00
|
|
|
from 'keys/dev/public.key'
|
2016-09-29 06:03:14 -04:00
|
|
|
} else {
|
2016-10-01 03:46:43 -04:00
|
|
|
from 'keys/prod/public.key'
|
2016-02-03 11:21:55 -05:00
|
|
|
}
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
forbiddenPatterns {
|
|
|
|
exclude '**/*.key'
|
|
|
|
exclude '**/*.p12'
|
|
|
|
exclude '**/*.der'
|
|
|
|
exclude '**/*.zip'
|
|
|
|
}
|
2016-07-25 20:09:54 -04:00
|
|
|
|
2017-04-10 07:40:07 -04:00
|
|
|
task extractNativeLicenses(type: Copy) {
|
2017-05-26 04:15:12 -04:00
|
|
|
dependsOn configurations.nativeBundle
|
2017-04-10 07:40:07 -04:00
|
|
|
into "${buildDir}"
|
2017-05-26 04:15:12 -04:00
|
|
|
from {
|
|
|
|
project.zipTree(configurations.nativeBundle.singleFile)
|
|
|
|
}
|
2017-04-10 07:40:07 -04:00
|
|
|
include 'platform/licenses/**'
|
2017-05-19 08:36:40 -04:00
|
|
|
// This is to reduce the risk of credentials used to access the native bundle not
|
|
|
|
// having propagated throughout AWS by the time it's downloaded; the time needed
|
|
|
|
// to compile the Java is extra time during which the propagation can take place
|
|
|
|
shouldRunAfter compileJava
|
2017-04-10 07:40:07 -04:00
|
|
|
}
|
|
|
|
|
2016-09-29 06:03:14 -04:00
|
|
|
// TODO: standardize packaging config for plugins
|
|
|
|
bundlePlugin {
|
2017-05-26 04:15:12 -04:00
|
|
|
dependsOn configurations.nativeBundle
|
2016-09-29 06:03:14 -04:00
|
|
|
from('bin/x-pack') {
|
|
|
|
into 'bin'
|
|
|
|
}
|
2016-10-01 03:46:43 -04:00
|
|
|
from('config/x-pack') {
|
2016-09-29 06:03:14 -04:00
|
|
|
into 'config'
|
|
|
|
}
|
2017-05-26 04:15:12 -04:00
|
|
|
from {
|
|
|
|
project.zipTree(configurations.nativeBundle.singleFile)
|
|
|
|
}
|
2017-04-10 07:40:07 -04:00
|
|
|
// We don't ship the individual nativeBundle licenses - instead
|
|
|
|
// they get combined into the top level NOTICES file we ship
|
|
|
|
exclude 'platform/licenses/**'
|
2016-02-03 11:21:55 -05:00
|
|
|
}
|
2016-09-29 06:03:14 -04:00
|
|
|
|
|
|
|
// add api jar for extension authors to compile against
|
|
|
|
// note this is just the normal x-pack jar for now, with a different name
|
|
|
|
project.afterEvaluate {
|
|
|
|
task apiJar {
|
|
|
|
dependsOn('generatePomFileForApijarPublication', project.jar)
|
|
|
|
doFirst {
|
|
|
|
Path jarFile = project.jar.outputs.files.singleFile.toPath()
|
|
|
|
String apiFileName = jarFile.fileName.toString().replace(project.version, "api-${project.version}")
|
|
|
|
Files.copy(jarFile, jarFile.resolveSibling(apiFileName), StandardCopyOption.REPLACE_EXISTING)
|
|
|
|
|
2016-10-03 22:25:44 -04:00
|
|
|
String pomFileName = jarFile.fileName.toString().replace('.jar', '.pom')
|
2016-09-29 06:03:14 -04:00
|
|
|
String apiPomFileName = apiFileName.replace('.jar', '.pom')
|
|
|
|
Files.copy(jarFile.resolveSibling(pomFileName), jarFile.resolveSibling(apiPomFileName),
|
|
|
|
StandardCopyOption.REPLACE_EXISTING)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assemble.dependsOn(apiJar)
|
|
|
|
project.publishing {
|
|
|
|
publications {
|
|
|
|
apijar(MavenPublication) {
|
|
|
|
from project.components.java
|
2016-10-01 03:46:43 -04:00
|
|
|
artifactId = 'x-pack-api'
|
2016-09-29 06:03:14 -04:00
|
|
|
pom.withXml { XmlProvider xml ->
|
|
|
|
Node root = xml.asNode()
|
|
|
|
root.appendNode('name', project.pluginProperties.extension.name)
|
|
|
|
root.appendNode('description', project.pluginProperties.extension.description)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-10 07:40:07 -04:00
|
|
|
// Add an extra licenses directory to the combined notices
|
|
|
|
project.tasks.findByName('generateNotice').dependsOn extractNativeLicenses
|
|
|
|
project.tasks.findByName('generateNotice').licensesDir new File("${project.buildDir}/platform/licenses")
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
|
|
|
|
2017-02-22 03:56:52 -05:00
|
|
|
integTestRunner {
|
2016-09-29 06:03:14 -04:00
|
|
|
// TODO: fix this rest test to not depend on a hardcoded port!
|
2017-04-13 09:16:32 -04:00
|
|
|
systemProperty 'tests.rest.blacklist', 'getting_started/10_monitor_cluster_health/*'
|
2017-02-22 03:56:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
integTestCluster {
|
|
|
|
setting 'xpack.ml.enabled', 'true'
|
2017-04-20 12:18:11 -04:00
|
|
|
setting 'logger.org.elasticsearch.xpack.ml.datafeed', 'TRACE'
|
2017-04-04 08:44:40 -04:00
|
|
|
// Integration tests are supposed to enable/disable exporters before/after each test
|
|
|
|
setting 'xpack.monitoring.exporters._local.type', 'local'
|
|
|
|
setting 'xpack.monitoring.exporters._local.enabled', 'false'
|
|
|
|
setting 'xpack.monitoring.collection.interval', '-1'
|
2017-05-11 14:23:55 -04:00
|
|
|
distribution = 'zip' // this is important since we use the reindex module in ML
|
2017-04-04 08:44:40 -04:00
|
|
|
|
2017-02-22 03:56:52 -05:00
|
|
|
waitCondition = { NodeInfo node, AntBuilder ant ->
|
|
|
|
File tmpFile = new File(node.cwd, 'wait.success')
|
2017-06-29 16:27:57 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
HttpURLConnection httpURLConnection = null;
|
|
|
|
try {
|
|
|
|
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_xpack/security/user/elastic/_password")
|
|
|
|
.openConnection();
|
|
|
|
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
|
|
|
Base64.getEncoder().encodeToString("elastic:".getBytes(StandardCharsets.UTF_8)));
|
|
|
|
httpURLConnection.setRequestMethod("PUT");
|
|
|
|
httpURLConnection.setDoOutput(true);
|
|
|
|
httpURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
|
|
|
|
|
|
|
httpURLConnection.connect();
|
|
|
|
OutputStream out = httpURLConnection.getOutputStream();
|
|
|
|
out.write("{\"password\": \"x-pack-test-password\"}".getBytes(StandardCharsets.UTF_8));
|
|
|
|
out.close()
|
|
|
|
|
|
|
|
if (httpURLConnection.getResponseCode() == 200) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
httpURLConnection.disconnect()
|
|
|
|
if (i == 9) {
|
|
|
|
logger.error("final attempt to set elastic password", e)
|
|
|
|
} else {
|
|
|
|
logger.debug("failed to set elastic password", e)
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (httpURLConnection != null) {
|
|
|
|
httpURLConnection.disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// did not start, so wait a bit before trying again
|
|
|
|
Thread.sleep(500L);
|
|
|
|
}
|
|
|
|
|
2017-02-22 03:56:52 -05:00
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
// we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned
|
|
|
|
HttpURLConnection httpURLConnection = null;
|
|
|
|
try {
|
2017-03-15 13:23:26 -04:00
|
|
|
httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=${numNodes}&wait_for_status=yellow").openConnection();
|
2017-02-22 03:56:52 -05:00
|
|
|
httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
2017-06-29 16:27:57 -04:00
|
|
|
Base64.getEncoder().encodeToString("elastic:x-pack-test-password".getBytes(StandardCharsets.UTF_8)));
|
2017-02-22 03:56:52 -05:00
|
|
|
httpURLConnection.setRequestMethod("GET");
|
|
|
|
httpURLConnection.connect();
|
|
|
|
if (httpURLConnection.getResponseCode() == 200) {
|
|
|
|
tmpFile.withWriter StandardCharsets.UTF_8.name(), {
|
|
|
|
it.write(httpURLConnection.getInputStream().getText(StandardCharsets.UTF_8.name()))
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
|
|
|
}
|
2017-02-22 03:56:52 -05:00
|
|
|
} catch (Exception e) {
|
|
|
|
if (i == 9) {
|
|
|
|
logger.error("final attempt of calling cluster health failed", e)
|
|
|
|
} else {
|
|
|
|
logger.debug("failed to call cluster health", e)
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (httpURLConnection != null) {
|
|
|
|
httpURLConnection.disconnect();
|
|
|
|
}
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
2017-02-22 03:56:52 -05:00
|
|
|
|
|
|
|
// did not start, so wait a bit before trying again
|
|
|
|
Thread.sleep(500L);
|
|
|
|
}
|
|
|
|
return tmpFile.exists()
|
2016-09-29 06:03:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-01 19:27:45 -04:00
|
|
|
test {
|
|
|
|
/*
|
|
|
|
* We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each
|
|
|
|
* other if we allow them to set the number of available processors as it's set-once in Netty.
|
|
|
|
*/
|
|
|
|
systemProperty 'es.set.netty.runtime.available.processors', 'false'
|
|
|
|
}
|
|
|
|
|
|
|
|
integTestRunner {
|
|
|
|
/*
|
|
|
|
* We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each
|
|
|
|
* other if we allow them to set the number of available processors as it's set-once in Netty.
|
|
|
|
*/
|
|
|
|
systemProperty 'es.set.netty.runtime.available.processors', 'false'
|
|
|
|
}
|
|
|
|
|
2016-09-29 06:03:14 -04:00
|
|
|
// TODO: don't publish test artifacts just to run messy tests, fix the tests!
|
|
|
|
// https://github.com/elastic/x-plugins/issues/724
|
|
|
|
configurations {
|
|
|
|
testArtifacts.extendsFrom testRuntime
|
|
|
|
}
|
|
|
|
task testJar(type: Jar) {
|
|
|
|
classifier "test"
|
|
|
|
from sourceSets.test.output
|
|
|
|
}
|
|
|
|
artifacts {
|
|
|
|
// normal es plugins do not publish the jar but we need to since users need it for Transport Clients and extensions
|
|
|
|
archives jar
|
|
|
|
testArtifacts testJar
|
|
|
|
}
|
|
|
|
|
|
|
|
// classes are missing, e.g. com.ibm.icu.lang.UCharacter
|
|
|
|
thirdPartyAudit.excludes = [
|
|
|
|
// uses internal java api: sun.misc.Unsafe
|
|
|
|
'com.google.common.cache.Striped64',
|
|
|
|
'com.google.common.cache.Striped64$1',
|
|
|
|
'com.google.common.cache.Striped64$Cell',
|
|
|
|
'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator',
|
|
|
|
'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1',
|
|
|
|
|
|
|
|
// pulled in as external dependency to work on java 9
|
|
|
|
'com.sun.activation.registries.LineTokenizer',
|
|
|
|
'com.sun.activation.registries.LogSupport',
|
|
|
|
'com.sun.activation.registries.MailcapFile',
|
|
|
|
'com.sun.activation.registries.MailcapParseException',
|
|
|
|
'com.sun.activation.registries.MailcapTokenizer',
|
|
|
|
'com.sun.activation.registries.MimeTypeEntry',
|
|
|
|
'com.sun.activation.registries.MimeTypeFile',
|
|
|
|
'javax.activation.ActivationDataFlavor',
|
|
|
|
'javax.activation.CommandInfo',
|
|
|
|
'javax.activation.CommandMap',
|
|
|
|
'javax.activation.CommandObject',
|
|
|
|
'javax.activation.DataContentHandler',
|
|
|
|
'javax.activation.DataContentHandlerFactory',
|
|
|
|
'javax.activation.DataHandler$1',
|
|
|
|
'javax.activation.DataHandler',
|
|
|
|
'javax.activation.DataHandlerDataSource',
|
|
|
|
'javax.activation.DataSource',
|
|
|
|
'javax.activation.DataSourceDataContentHandler',
|
|
|
|
'javax.activation.FileDataSource',
|
|
|
|
'javax.activation.FileTypeMap',
|
|
|
|
'javax.activation.MailcapCommandMap',
|
|
|
|
'javax.activation.MimeType',
|
|
|
|
'javax.activation.MimeTypeParameterList',
|
|
|
|
'javax.activation.MimeTypeParseException',
|
|
|
|
'javax.activation.MimetypesFileTypeMap',
|
|
|
|
'javax.activation.ObjectDataContentHandler',
|
|
|
|
'javax.activation.SecuritySupport$1',
|
|
|
|
'javax.activation.SecuritySupport$2',
|
|
|
|
'javax.activation.SecuritySupport$3',
|
|
|
|
'javax.activation.SecuritySupport$4',
|
|
|
|
'javax.activation.SecuritySupport$5',
|
|
|
|
'javax.activation.SecuritySupport',
|
|
|
|
'javax.activation.URLDataSource',
|
|
|
|
'javax.activation.UnsupportedDataTypeException'
|
|
|
|
]
|
2016-11-21 04:52:55 -05:00
|
|
|
|
2017-01-05 11:45:17 -05:00
|
|
|
run {
|
2017-02-08 11:58:57 -05:00
|
|
|
setting 'xpack.ml.enabled', 'true'
|
2017-01-05 11:45:17 -05:00
|
|
|
setting 'xpack.graph.enabled', 'true'
|
|
|
|
setting 'xpack.security.enabled', 'true'
|
|
|
|
setting 'xpack.monitoring.enabled', 'true'
|
|
|
|
setting 'xpack.watcher.enabled', 'true'
|
|
|
|
}
|