diff --git a/.idea/modules.xml b/.idea/modules.xml index b45d65d478c1..b8d8fc8f0481 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -11,6 +11,7 @@ + diff --git a/build/bazel-generated-file-list.txt b/build/bazel-generated-file-list.txt index c8319df15fed..b744f8606c68 100644 --- a/build/bazel-generated-file-list.txt +++ b/build/bazel-generated-file-list.txt @@ -228,6 +228,7 @@ fleet/kernel fleet/ktor/network/tls fleet/lsp.protocol fleet/multiplatform.shims +fleet/preferences fleet/reporting/api fleet/reporting/shared fleet/rhizomedb diff --git a/fleet/preferences/BUILD.bazel b/fleet/preferences/BUILD.bazel new file mode 100644 index 000000000000..addbc7418182 --- /dev/null +++ b/fleet/preferences/BUILD.bazel @@ -0,0 +1,30 @@ +### auto-generated section `build fleet.preferences` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_provided_library") + +create_kotlinc_options( + name = "custom_preferences", + x_consistent_data_class_copy_visibility = True, + x_context_parameters = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_provided_library( + name = "fleet_util_multiplatform_provided", + lib = "//fleet/util/multiplatform" +) + +jvm_library( + name = "preferences", + module_name = "fleet.preferences", + visibility = ["//visibility:public"], + srcs = glob(["srcCommonMain/**/*.kt", "srcCommonMain/**/*.java", "srcCommonMain/**/*.form", "srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_preferences", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-io-core", + ":fleet_util_multiplatform_provided", + ] +) +### auto-generated section `build fleet.preferences` end \ No newline at end of file diff --git a/fleet/preferences/fleet.preferences.iml b/fleet/preferences/fleet.preferences.iml new file mode 100644 index 000000000000..a30911748cbe --- /dev/null +++ b/fleet/preferences/fleet.preferences.iml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + $MAVEN_REPOSITORY$/jetbrains/fleet/expects-compiler-plugin/2.2.20-0.1/expects-compiler-plugin-2.2.20-0.1.jar + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/preferences/gradlebuild/build.gradle.kts b/fleet/preferences/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..a64a0aa96e99 --- /dev/null +++ b/fleet/preferences/gradlebuild/build.gradle.kts @@ -0,0 +1,72 @@ +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + id("fleet.open-source-module-conventions") + alias(libs.plugins.dokka) + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + alias(jps.plugins.expects) + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.preferences" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + sourceSets.wasmJsMain.dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3") + } + + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-Xcontext-parameters", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + wasmJs { + browser {} + } + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.wasmJsMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcWasmJsMain")) } + sourceSets.wasmJsMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesWasmJsMain")) } + sourceSets.wasmJsTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcWasmJsTest")) } + sourceSets.wasmJsTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesWasmJsTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.io.core.jvm479158162.get().let { "${it.group}:kotlinx-io-core:${it.version}" }) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + } + } + sourceSets.jvmMain.dependencies { + compileOnly(project(":fleet.util.multiplatform")) + } + sourceSets.wasmJsMain.dependencies { + implementation(project(":fleet.util.multiplatform")) + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/preferences/srcCommonMain/.pseudoCommonKotlinSourceSet b/fleet/preferences/srcCommonMain/.pseudoCommonKotlinSourceSet new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/fleet/preferences/srcCommonMain/fleet/preferences/FleetFromSourcesPaths.kt b/fleet/preferences/srcCommonMain/fleet/preferences/FleetFromSourcesPaths.kt new file mode 100644 index 000000000000..af725e5183a3 --- /dev/null +++ b/fleet/preferences/srcCommonMain/fleet/preferences/FleetFromSourcesPaths.kt @@ -0,0 +1,64 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import kotlinx.io.IOException +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem + +// Paths for resources while running Fleet from sources +// None of its methods should be called on production call path +object FleetFromSourcesPaths { + val isRunningFromSources: Boolean by lazy { + !isFleetDistributionMode && findRepositoryRoot() != null + } + + val intellijProjectRoot: Path by lazy { + requireNotNull(findRepositoryRoot()) { + "Cannot find IntelliJ repository root" + }.let { SystemFileSystem.resolve(it) } + } + + val projectRoot: Path by lazy { + Path(intellijProjectRoot, "fleet") + } + + // TODO: remove the usages of this property and delete it. + // Ideally `skiko.library.path` should always be set by the tooling or distribution argfile, making `skikoLibraryDirectory` property redundant. + // Currently, this is needed for GalleryApp and some isolated UI tests. + // Once our test running logic is unified, we will be able to wire the proper preparation steps to test runs avoiding such code. + val skikoLibraryDirectory: Path by lazy { + val skiko = Path(projectRoot, "build/fleet-skiko/build/skiko/buildPlatform") + require(skiko.takeIf { SystemFileSystem.exists(it) }?.let { SystemFileSystem.list(it) }?.isNotEmpty() == true) { + """ + '$skiko' is empty or does not exist. + + Usually, this directory is automatically populated when required. However a few use cases are outside of the normal test flow. + If you are in such case (standalone noria UI tests, etc.), you should either: + - run `./fleet.sh :fleet-skiko:downloadSkikoForJps` Gradle command + - or, set the JVM system property `skiko.library.path` to a valid skiko downloaded on your machine + + If you ran through a JPS configuration, probably it is misconfigured, please contact #fleet-platform. + If you ran through the Fleet Gradle build tooling, probably it is misconfigured, please contact #fleet-platform. + """.trimIndent() + } + skiko + } + + private fun findRepositoryRoot(): Path? { + var directory: Path? = findFleetRootByClass() + while (directory != null) { + if (directory.name != "community") { + try { + val children = SystemFileSystem.list(directory).map(Path::name).toSet() + if (children.contains(".idea") && children.contains("fleet")) { + return directory + } + } + catch (_: IOException) { + } + } + directory = directory.parent + } + return null + } +} \ No newline at end of file diff --git a/fleet/preferences/srcCommonMain/fleet/preferences/FleetProperties.kt b/fleet/preferences/srcCommonMain/fleet/preferences/FleetProperties.kt new file mode 100644 index 000000000000..63cd8f0029a4 --- /dev/null +++ b/fleet/preferences/srcCommonMain/fleet/preferences/FleetProperties.kt @@ -0,0 +1,48 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import fleet.util.multiplatform.linkToActual + +// True means one wants to investigate how fleet itself works (e.g. enable verbose logging or increase transport delays) +val isFleetDebugMode: Boolean by lazy { fleetFlag("fleet.debug.mode") } + +// Enable auxiliary features to facilitate dev flow e.g. code reload etc. It means that we're running from sources. +@Deprecated("most probably you want use FleetFromSourcesPaths.isRunningFromSources or FleetCommonSettingsKeys.kt#isInternalMode") +val isFleetDevMode: Boolean by lazy { getFleetEnvironmentType() == FleetEnvironmentType.DEVELOPMENT } + +// Defined the default value for internal mode +@Deprecated( + "we still have usages due to lack of dependency injection, most probably you want use FleetCommonSettingsKeys.kt#isInternalMode") +val isFleetInternalDefaultValue: Boolean by lazy { fleetFlag("fleet.internal.mode.default") } + +// Fleet is running as a built distribution +val isFleetDistributionMode: Boolean by lazy { getFleetEnvironmentType() == FleetEnvironmentType.PRODUCTION } + +// The distribution is not published on the marketplace, features that depend on it, should do workarounds: +// e.g. upload required resources to the Space in the runtime +val isFleetUnpublishedBuild: Boolean by lazy { fleetFlag("fleet.unpublished.build") } + +// Defines whether we are under testing. If set to true, some components will be mocked. +val isFleetTestMode: Boolean by lazy { getFleetEnvironmentType() == FleetEnvironmentType.TEST } + +val isFleetShortCircuitMode: Boolean by lazy { + fleetFlag("fleet.shortCircuit", when { + isFleetTestMode -> false + isFleetInternalDefaultValue -> false + else -> true + }) +} + +val coroutinesDebugProbesEnabled: Boolean by lazy { fleetFlag("fleet.coroutines.debug.probes.enabled") } + +fun fleetFlag(name: String, defaultValue: Boolean = false): Boolean = fleetProperty(name)?.toBoolean() ?: defaultValue + +fun fleetProperty(name: String, defaultValue: String? = null): String? = linkToActual() + +internal enum class FleetEnvironmentType { + PRODUCTION, + DEVELOPMENT, + TEST +} + +internal fun getFleetEnvironmentType(): FleetEnvironmentType = linkToActual() \ No newline at end of file diff --git a/fleet/preferences/srcCommonMain/fleet/preferences/FleetRootByClass.kt b/fleet/preferences/srcCommonMain/fleet/preferences/FleetRootByClass.kt new file mode 100644 index 000000000000..f9c1e3be6725 --- /dev/null +++ b/fleet/preferences/srcCommonMain/fleet/preferences/FleetRootByClass.kt @@ -0,0 +1,7 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import fleet.util.multiplatform.linkToActual +import kotlinx.io.files.Path + +internal fun findFleetRootByClass(): Path? = linkToActual() \ No newline at end of file diff --git a/fleet/preferences/srcCommonMain/fleet/preferences/TracingConfigProperties.kt b/fleet/preferences/srcCommonMain/fleet/preferences/TracingConfigProperties.kt new file mode 100644 index 000000000000..16030a80e335 --- /dev/null +++ b/fleet/preferences/srcCommonMain/fleet/preferences/TracingConfigProperties.kt @@ -0,0 +1,46 @@ +package fleet.preferences + +object TracingConfigProperties { + //@fleet.kernel.plugins.InternalInPluginModules(where = ["fleet.reporting.opentelemetry"]) + val otelGrpcEndpoint: String? by lazy { fleetProperty("fleet.diagnostic.otel.grpc.endpoint", null) } + val otelHttpEndpoint: String? by lazy { fleetProperty("fleet.diagnostic.otel.http.endpoint", null) } + val otelTracesDir: String? by lazy { fleetProperty("fleet.diagnostic.otel.traces.dir", null) } + + //@fleet.kernel.plugins.InternalInPluginModules(where = ["fleet.reporting.opentelemetry"]) + val metricsFile: String? by lazy { fleetProperty("fleet.diagnostic.metrics.file", null) } + + val fahrplanStartupFile: String? by lazy { fleetProperty("fahrplan.startup", null) } + + //@fleet.kernel.plugins.InternalInPluginModules(where = ["fleet.reporting.opentelemetry"]) + val fahrplanEnabled: Boolean by lazy { + fahrplanStartupFile != null || + (fleetProperty("fleet.diagnostic.fahrplan.enabled", null)?.toBoolean() ?: isFleetInternalDefaultValue) + } + val openTelemetryEnabled: Boolean by lazy { + isFleetInternalDefaultValue || + otelGrpcEndpoint != null || + otelHttpEndpoint != null || + otelTracesDir != null || + metricsFile != null || + ijPerfFile != null + } + + //@fleet.kernel.plugins.InternalInPluginModules(where = ["fleet.reporting.opentelemetry"]) + val ijPerfFile: String? by lazy { fleetProperty("fleet.diagnostic.ijperf.file", null) } + + val fusSchemaDir: String? by lazy { fleetProperty("fleet.diagnostic.fus.schema.dir", null) } + val schemaGenerationMode: Boolean by lazy { fusSchemaDir != null } //todo [MM] maybe rewrite to fleet.dock.impl.RunMode + val ijPerfSpanPrefix: String? by lazy { fleetProperty("fleet.diagnostic.ijperf.span.prefix") } + + val performanceTestMode: Boolean by lazy { + (ijPerfFile != null || fahrplanStartupFile != null) && + !fleetProperty("fleet.diagnostic.suppress.quit", "false").toBoolean() + } + + //does a number of things to simplify FUS testing + // 1. does not update metadata (allows to work with modified metadata) + // 2. disables bucket check (sends always) + // 3. does not remove fus files (allows reading what is actually sent) + // 4. shows a directory with fus files on startup + val fusTestingMode: Boolean by lazy { fleetProperty("fleet.diagnostic.fus.testing.mode", "false").toBoolean() } +} \ No newline at end of file diff --git a/fleet/preferences/srcJvmMain/fleet/preferences/FleetProperty.jvm.kt b/fleet/preferences/srcJvmMain/fleet/preferences/FleetProperty.jvm.kt new file mode 100644 index 000000000000..f3a4ba57e193 --- /dev/null +++ b/fleet/preferences/srcJvmMain/fleet/preferences/FleetProperty.jvm.kt @@ -0,0 +1,19 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import fleet.util.multiplatform.Actual + +@Actual("fleetProperty") +internal fun fleetPropertyJvm(name: String, defaultValue: String?): String? { + val formattedName = name.replace('.', '_').uppercase() + return System.getProperty(name) ?: System.getenv(formattedName) ?: defaultValue +} + +@Actual +internal fun getFleetEnvironmentTypeJvm(): FleetEnvironmentType { + return when { + fleetFlag("fleet.distribution.mode") -> FleetEnvironmentType.PRODUCTION + fleetFlag("fleet.dev.mode") -> FleetEnvironmentType.DEVELOPMENT + else -> FleetEnvironmentType.TEST + } +} \ No newline at end of file diff --git a/fleet/preferences/srcJvmMain/fleet/preferences/FleetRootByClass.jvm.kt b/fleet/preferences/srcJvmMain/fleet/preferences/FleetRootByClass.jvm.kt new file mode 100644 index 000000000000..40294f9fc857 --- /dev/null +++ b/fleet/preferences/srcJvmMain/fleet/preferences/FleetRootByClass.jvm.kt @@ -0,0 +1,20 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import fleet.util.multiplatform.Actual +import kotlinx.io.files.Path +import java.net.URL +import kotlin.io.path.absolutePathString +import kotlin.io.path.pathString +import kotlin.io.path.toPath +import kotlin.jvm.javaClass + +@Actual("findFleetRootByClass") +internal fun findFleetRootByClassJvm(): Path? { + val url: URL? = FleetFromSourcesPaths.javaClass.getResource("/${FleetFromSourcesPaths.javaClass.name.replace('.', '/')}.class") + return when (url?.protocol) { + "file" -> url.toURI().toPath() + "jar" -> URL(url.file).toURI().toPath().pathString.split("!").firstOrNull()?.let { java.nio.file.Path.of(it) } + else -> null + }?.absolutePathString()?.let { Path(it) } +} \ No newline at end of file diff --git a/fleet/preferences/srcJvmMain/fleet/preferences/PathExt.kt b/fleet/preferences/srcJvmMain/fleet/preferences/PathExt.kt new file mode 100644 index 000000000000..7418b2026c16 --- /dev/null +++ b/fleet/preferences/srcJvmMain/fleet/preferences/PathExt.kt @@ -0,0 +1,9 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import java.nio.file.Path + +@Deprecated("java Paths are not multiplatform. Please consider using kotlnx.io Path if possible") +fun kotlinx.io.files.Path.toNioPath(): Path = Path.of(this.toString()) + +fun kotlinx.io.files.Path.resolve(childPath: String): kotlinx.io.files.Path = kotlinx.io.files.Path(this, childPath) \ No newline at end of file diff --git a/fleet/preferences/srcWasmJsMain/fleet/preferences/FleetProperty.wasm.kt b/fleet/preferences/srcWasmJsMain/fleet/preferences/FleetProperty.wasm.kt new file mode 100644 index 000000000000..6663df06f636 --- /dev/null +++ b/fleet/preferences/srcWasmJsMain/fleet/preferences/FleetProperty.wasm.kt @@ -0,0 +1,34 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import fleet.util.multiplatform.Actual +import kotlinx.browser.window + +@Actual("fleetProperty") +fun fleetPropertyWasmJs(name: String, defaultValue: String?): String? = when (name) { + "fleet.ai.service.configuration.url" -> url("aiconfig") + "fleet.ai.service.url" -> url("ai") + else -> defaultValue +} + +@Actual +internal fun getFleetEnvironmentTypeWasmJs(): FleetEnvironmentType { + return when (ENVIRONMENT) { + "production" -> FleetEnvironmentType.PRODUCTION + "development" -> FleetEnvironmentType.DEVELOPMENT + else -> FleetEnvironmentType.TEST + } +} + +/** + * the property represents webpack's `config.mode` value + */ +@JsName("ENVIRONMENT") +external val ENVIRONMENT: String + +private fun url(suffix: String): String { + return sequenceOf(window.location.origin, window.location.pathname, suffix) + .map { it.trim('/') } + .filter { it.isNotEmpty() } + .joinToString(separator = "/") +} \ No newline at end of file diff --git a/fleet/preferences/srcWasmJsMain/fleet/preferences/FleetRootByClass.wasm.kt b/fleet/preferences/srcWasmJsMain/fleet/preferences/FleetRootByClass.wasm.kt new file mode 100644 index 000000000000..6c7890d4efdb --- /dev/null +++ b/fleet/preferences/srcWasmJsMain/fleet/preferences/FleetRootByClass.wasm.kt @@ -0,0 +1,8 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.preferences + +import fleet.util.multiplatform.Actual +import kotlinx.io.files.Path + +@Actual("findFleetRootByClass") +internal fun findFleetRootByClassWasmJs(): Path? = null \ No newline at end of file