diff --git a/.idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml b/.idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml
new file mode 100644
index 000000000000..b36b258cd706
--- /dev/null
+++ b/.idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml
@@ -0,0 +1,213 @@
+
+
+
+
+
+ 3d6cd0bf48c65e965903fa4720d713badeeaa5433bd7b278075c484e3a5709a8
+
+
+ 877ffeaa6fc96192da7383b3c3e16717cb52504f488b80937d796eb875e5565a
+
+
+ 2f12ed8fbd53e91cb3b2a163b5d06ae2c39f538bd74f23a678889f5be02e8675
+
+
+ 198b9b1ec6c33da8c460e6a2e0859d1308f1fcb3f7d4138100f7dd75207edd80
+
+
+ 633ccbae483c99b27e311a6a70157cfc0dfa19f8eb3ba263198f40f87266c09c
+
+
+ e738ebef7be523c4e1940f5e5b2390ba853d4333f547038b2b31fb3964e99933
+
+
+ d3f30ef5e3b11b358f28f42422226585b72e4a11bf13b1fc3411962dcfde64f3
+
+
+ aa584dcd05862a8bbe7eb7db73959d93ca6b527cc44536b7d10fcda733411d4b
+
+
+ 8a2323e7d08ff07d2f869e4bcae834773ce0a0e8704cd7404c9625992a748662
+
+
+ 67543f0736fc422ae927ed0e504b98bc5e269fda0d3500579337cb713da28412
+
+
+ ce382d40f998d8dd11008967c3bf4cd51c47182512b84b5f1d48e48d2a3f9770
+
+
+ 508bde368d4b174402aeeccec68c7e29ad9349a953b1efab75ebeaa6c0a21f0e
+
+
+ 01489854c4f9494de6cd5446df8e3c694ad3f6582c5369b9578eb18754f1720c
+
+
+ 71fe60ba15b5f9c43f5ee1da0edc13fd98f2a85dbf8c9dcafc666bc1c7e668e7
+
+
+ 6b7584f233e0715f5394edfbfd5536a480113efe99f3d49bfd58d2395072177a
+
+
+ 6d7028c6e695b872e0ba91679af4dbbabf8e1bfbd6f7fb52be30bb1e398d595d
+
+
+ 30cf05f9f7ec7d6676031ee2b0c8f4b33b107a93a179ca931d57baa983de70a3
+
+
+ dd0616302ce4a3fc1c03326bad37e5cf3509377692b6aca5ea17d581ad582d62
+
+
+ 94cc5ebcb2d819ebb8c628f64ffa352426f23b3b422834bfa8199cf497a1365d
+
+
+ 3f0d5d4e67a3097b56b32fa7b26589da9d6bd0968693b395ad1c8f49f60dbdfd
+
+
+ 79ed729b7e134609310de7d691b1b80870f467a95bc84f2f6bbf3e43ae83de85
+
+
+ 9664c8d36468c1f575b356f04025ce17417e584a0bb19b640b115b10a8dce72c
+
+
+ 31e3811fd85ba7fcba38a4067d63af8c13c1e05ae7062639fd8c954520315f51
+
+
+ bbbf92cbc8df78a8fa20f0b35613e8d41024ee6525b538ebda280917e1d7e812
+
+
+ 4bfb91b4c6373ac2b1d94a085c69935ca1b56b7b27b21c5228c5bc5da6f7e2dc
+
+
+ 506a1d3aba9f99b55570c7f8813cd79a44946531f29050d5b938474efdb4cf80
+
+
+ 1452873e0cc9637b7e1a717f2cca0cbccebd00c05aaaf67b8adb06777bf4f461
+
+
+ be3f9bd5ebfb83aa353f407a4389f7b3454a6945f82cea1d41e370aeedae71cd
+
+
+ c017ee899656cf5abf22c3793603cb33ae35f0a805b9fa0707f8146a73a14a7f
+
+
+ 72c8559a241af92ce122c0df0dc3749d638d616ebd5da3ba3749a95abca3b349
+
+
+ 4343154bb1b0a282d86b627932a465722eeda3cccd6cc4986fc5e5c4013bfef4
+
+
+ 66c0700ca0c27046b0b12e6cf4c4da423800b88c1c72b2709cd5cbc3e0f1eb64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_clikt.xml b/.idea/libraries/fleet_clikt.xml
new file mode 100644
index 000000000000..7a14dc5365e1
--- /dev/null
+++ b/.idea/libraries/fleet_clikt.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ 9cd19e04782f461dffeb183d17e576984febb7934932cb406646ac41e428863b
+
+
+ 9ed3b976fcccc78da746d49866fa8ebb8f10530a93c544ea0420259a607dd95e
+
+
+ 9cf9b46d1f49f2d6cf2635462b29dc59e0c29b1fb2f085b3312888bbe9c7cd31
+
+
+ 59f741adfe62053066782d8b1a45afd06685a4bc64b33277e54876b993ed885c
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_clikt_core.xml b/.idea/libraries/fleet_clikt_core.xml
new file mode 100644
index 000000000000..ef92ae111018
--- /dev/null
+++ b/.idea/libraries/fleet_clikt_core.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 69cba94a2291f74b78c88239cd8b67eaf3f08e0dad297749abe6477b3e9b81b1
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_github_luben_zstd_jni.xml b/.idea/libraries/fleet_github_luben_zstd_jni.xml
new file mode 100644
index 000000000000..ebdbe2edc3cd
--- /dev/null
+++ b/.idea/libraries/fleet_github_luben_zstd_jni.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ d75b2ced6059f81ad23e021c554259b906b6c4f2991cb772409827569ead4c1a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml
new file mode 100644
index 000000000000..b9c996f4d349
--- /dev/null
+++ b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 91527d86fea04bd6183d140a09f10dab2dea727af12d27d9165ee342ea3d0ede
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml
new file mode 100644
index 000000000000..c2134c9bc9d8
--- /dev/null
+++ b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 9ce273dcb860b92a5bf2ec36ad1e2a11b41aed89c6c984ca73e87eff5eda1bd0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_io_ktor_client_logging.xml b/.idea/libraries/fleet_io_ktor_client_logging.xml
new file mode 100644
index 000000000000..9957ab2c2a26
--- /dev/null
+++ b/.idea/libraries/fleet_io_ktor_client_logging.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ ecabf013b137f1a69c39cffeae1ba9e365659357b21688f1876ee6cf50fd00b9
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_io_ktor_client_mock.xml b/.idea/libraries/fleet_io_ktor_client_mock.xml
new file mode 100644
index 000000000000..d4369aba6af1
--- /dev/null
+++ b/.idea/libraries/fleet_io_ktor_client_mock.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 2ae0d57bb9fceb8ee2aeda6536acc6f3e1de355984b4c9da720566b85f1525e8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml b/.idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml
new file mode 100644
index 000000000000..69a329d23d7c
--- /dev/null
+++ b/.idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ c86d2bfa23d5d23f916d3574591a166b3df259bea6c23d39abea597b34ba022f
+
+
+ 74d953cec00048b892112f9602fc7b1113ea55337dc1ede32e449efcb2b231c7
+
+
+ cc65afe17daf2f9380d3c64460f5b10ec279f98b9461fbf0ccf62b3cb3ee6c37
+
+
+ 5a0d7557d494732a6165e4188eb11f10faca196350cad04d9950abd83e6dd148
+
+
+ 0181873ba85684d119134f5598432f5b63109a03adc455767a1500b2145df8a2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml b/.idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml
new file mode 100644
index 000000000000..f71553540f74
--- /dev/null
+++ b/.idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 7df07e447c1de46592dfe4b20345b61ff57c5f3b796b8e946086559b260f7690
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/fleet_tukaani_xz.xml b/.idea/libraries/fleet_tukaani_xz.xml
new file mode 100644
index 000000000000..4e2bb6af2d60
--- /dev/null
+++ b/.idea/libraries/fleet_tukaani_xz.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 211b306cfc44f8f96df3a0a3ddaf75ba8c5289eed77d60d72f889bb855f535e5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 724bf9ea248b..a811ba430e6b 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/build/bazel-generated-file-list.txt b/build/bazel-generated-file-list.txt
index 0f00dc93ea40..590ac37c3906 100644
--- a/build/bazel-generated-file-list.txt
+++ b/build/bazel-generated-file-list.txt
@@ -221,6 +221,15 @@ commandInterface
community-resources
fleet/andel
fleet/bifurcan
+fleet/build/authentication
+fleet/build/cli
+fleet/build/codecache
+fleet/build/fs
+fleet/build/http
+fleet/build/license
+fleet/build/platform
+fleet/build/s3
+fleet/build/sign
fleet/bundles
fleet/codecache
fleet/codecache/test
diff --git a/fleet/.gitignore b/fleet/.gitignore
index baf5c70f19e3..dd9a60b31472 100644
--- a/fleet/.gitignore
+++ b/fleet/.gitignore
@@ -1 +1 @@
-/**/build
\ No newline at end of file
+/**/gradlebuild/build
\ No newline at end of file
diff --git a/fleet/build/authentication/BUILD.bazel b/fleet/build/authentication/BUILD.bazel
new file mode 100644
index 000000000000..74163ca9d347
--- /dev/null
+++ b/fleet/build/authentication/BUILD.bazel
@@ -0,0 +1,31 @@
+### auto-generated section `build fleet.build.authentication` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_authentication",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "authentication",
+ module_name = "fleet.build.authentication",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_authentication",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:kotlinx-serialization-core",
+ "@lib//:kotlinx-serialization-json",
+ "@lib//:slf4j-api",
+ "@lib//:fleet-io-github-pdvrieze-xmlutil-serialization-jvm",
+ "@lib//:fleet-io-github-pdvrieze-xmlutil-core-jvm",
+ ]
+)
+### auto-generated section `build fleet.build.authentication` end
\ No newline at end of file
diff --git a/fleet/build/authentication/fleet.build.authentication.iml b/fleet/build/authentication/fleet.build.authentication.iml
new file mode 100644
index 000000000000..decd83bfca77
--- /dev/null
+++ b/fleet/build/authentication/fleet.build.authentication.iml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/authentication/gradlebuild/build.gradle.kts b/fleet/build/authentication/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..c4d5d2bedc3d
--- /dev/null
+++ b/fleet/build/authentication/gradlebuild/build.gradle.kts
@@ -0,0 +1,71 @@
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ alias(jps.plugins.kotlin.serialization)
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.authentication"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.serialization.core.jvm1739247612.get().let { "${it.group}:kotlinx-serialization-core:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.json.jvm231489733.get().let { "${it.group}:kotlinx-serialization-json:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ exclude(group = "org.slf4j", module = "slf4j-jdk14")
+ }
+ }
+ sourceSets.jvmMain.dependencies {
+ implementation(jps.io.github.pdvrieze.xmlutil.serialization.jvm796850685.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.io.github.pdvrieze.xmlutil.core.jvm535986330.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ }
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt b/fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt
new file mode 100644
index 000000000000..f60a19332cb7
--- /dev/null
+++ b/fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt
@@ -0,0 +1,61 @@
+package fleet.buildtool.authentication
+
+import kotlinx.serialization.Serializable
+import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi
+import nl.adaptivity.xmlutil.serialization.XML
+import nl.adaptivity.xmlutil.serialization.XmlConfig
+import nl.adaptivity.xmlutil.serialization.XmlElement
+import nl.adaptivity.xmlutil.serialization.XmlSerialName
+import org.slf4j.Logger
+import java.nio.file.Path
+import kotlin.io.path.exists
+import kotlin.io.path.readText
+
+const val intellijPrivateDependenciesId = "intellij-private-dependencies"
+
+val defaultSettingsPath by lazy {
+ Path.of(System.getProperty("user.home")).resolve(".m2/settings.xml")
+}
+
+@OptIn(ExperimentalXmlUtilApi::class)
+fun readMavenSettingsServerCredentials(
+ logger: Logger,
+ mavenSettingsPath: Path = defaultSettingsPath,
+): List {
+ logger.info("Reading servers credentials from Maven settings '$mavenSettingsPath'")
+ val xml = XML {
+ defaultPolicy {
+ unknownChildHandler = XmlConfig.IGNORING_UNKNOWN_CHILD_HANDLER
+ }
+ }
+ if (!mavenSettingsPath.exists()) {
+ logger.warn("WARNING: failed to find '$mavenSettingsPath', no repository credentials will be read")
+ return emptyList()
+ }
+ return try {
+ xml.decodeFromString(MavenSettings.serializer(), mavenSettingsPath.readText()).servers?.servers ?: emptyList()
+ }
+ catch (e: Exception) {
+ logger.warn("WARNING: failed to parse {}", mavenSettingsPath, e)
+ emptyList()
+ }
+}
+
+@Serializable
+@XmlSerialName(
+ value = "settings",
+ namespace = "http://maven.apache.org/SETTINGS/1.0.0",
+)
+private data class MavenSettings(@XmlElement val servers: MavenServers?)
+
+@Serializable
+@XmlSerialName("servers")
+data class MavenServers(@XmlElement val servers: List?)
+
+@Serializable
+@XmlSerialName("server")
+data class MavenServerCredentials(
+ @XmlElement @XmlSerialName("id") val id: String?,
+ @XmlElement @XmlSerialName("username") val username: String?,
+ @XmlElement @XmlSerialName("password") val password: String?,
+)
diff --git a/fleet/build/cli/BUILD.bazel b/fleet/build/cli/BUILD.bazel
new file mode 100644
index 000000000000..6bbb656e9515
--- /dev/null
+++ b/fleet/build/cli/BUILD.bazel
@@ -0,0 +1,44 @@
+load("@rules_java//java:defs.bzl", "java_binary")
+
+
+java_binary(
+ name = "haven",
+ main_class = "fleet.buildtool.cli.MainKt",
+ visibility = ["//visibility:public"],
+ runtime_deps = [":cli"],
+)
+
+### auto-generated section `build fleet.build.cli` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_cli",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "cli",
+ module_name = "fleet.build.cli",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_cli",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:kotlinx-io-core",
+ "@lib//:kotlinx-coroutines-core",
+ "@lib//:fleet-clikt-core",
+ "@lib//:fleet-clikt",
+ ],
+ exports = [
+ "@lib//:fleet-clikt-core",
+ "@lib//:fleet-clikt",
+ ]
+)
+### auto-generated section `build fleet.build.cli` end
\ No newline at end of file
diff --git a/fleet/build/cli/fleet.build.cli.iml b/fleet/build/cli/fleet.build.cli.iml
new file mode 100644
index 000000000000..42ead6491e38
--- /dev/null
+++ b/fleet/build/cli/fleet.build.cli.iml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/cli/gradlebuild/build.gradle.kts b/fleet/build/cli/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..c819b58471bf
--- /dev/null
+++ b/fleet/build/cli/gradlebuild/build.gradle.kts
@@ -0,0 +1,86 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.buildtool.cli"
+ importedFromJps {}
+ }
+}
+
+kotlin {
+ jvm {
+ @OptIn(ExperimentalKotlinGradlePluginApi::class)
+ binaries {
+ // Configures a JavaExec task named "runJvm" and a Gradle distribution for the "main" compilation in this target
+ executable {
+ mainClass.set("fleet.buildtool.cli.commands.GenerateInitModuleDescriptorCommandKt")
+ }
+ }
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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")
+ }
+ implementation(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) {
+ isTransitive = false
+ }
+ api(jps.com.github.ajalt.clikt.clikt.core.jvm23167398.get().let { "${it.group}:clikt-core:${it.version}" }) {
+ isTransitive = false
+ }
+ api(jps.com.github.ajalt.clikt.clikt.jvm1164206222.get().let { "${it.group}:clikt:${it.version}" }) {
+ exclude(group = "com.github.ajalt.clikt", module = "clikt-core-jvm")
+ exclude(group = "com.github.ajalt.clikt", module = "clikt-core")
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
+ exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-jna-jvm")
+ exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-jna")
+ exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-ffm-jvm")
+ exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-ffm")
+ exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-graal-ffi-jvm")
+ exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-graal-ffi")
+ }
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt b/fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt
new file mode 100644
index 000000000000..12f823e4726e
--- /dev/null
+++ b/fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt
@@ -0,0 +1,31 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package fleet.buildtool.cli
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.main
+import com.github.ajalt.clikt.core.subcommands
+import kotlin.io.path.Path
+import kotlin.io.path.readLines
+
+class Haven : CliktCommand(name = "haven") {
+ override fun run(): Unit = Unit
+}
+
+fun main(args: Array): Unit = Haven().subcommands(
+ // TODO: add commands here
+).main(parseArgs(args))
+
+private fun parseArgs(startupArgs: Array): List {
+ return when (startupArgs.size) {
+ 1 -> Path(startupArgs[0].removePrefixStrict("--flagfile=")).readLines()
+ else -> startupArgs.toList()
+ }
+}
+
+private fun String.removePrefixStrict(prefix: String): String {
+ val result = removePrefix(prefix)
+ check(result != this) {
+ "String must start with $prefix but was: $this"
+ }
+ return result
+}
\ No newline at end of file
diff --git a/fleet/build/codecache/BUILD.bazel b/fleet/build/codecache/BUILD.bazel
new file mode 100644
index 000000000000..8588550f7dfb
--- /dev/null
+++ b/fleet/build/codecache/BUILD.bazel
@@ -0,0 +1,34 @@
+### auto-generated section `build fleet.build.codecache` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_codecache",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "codecache",
+ module_name = "fleet.build.codecache",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_codecache",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "//fleet/bundles",
+ "//fleet/codecache",
+ "//fleet/build/platform",
+ "//fleet/build/fs",
+ "@lib//:kotlinx-coroutines-core",
+ "@lib//:slf4j-api",
+ "@lib//:fleet-jetbrains-intellij-platform-util-zip-squashed",
+ "@lib//:fleet-jetbrains-intellij-idea-community-build-zip",
+ ]
+)
+### auto-generated section `build fleet.build.codecache` end
\ No newline at end of file
diff --git a/fleet/build/codecache/fleet.build.codecache.iml b/fleet/build/codecache/fleet.build.codecache.iml
new file mode 100644
index 000000000000..7fc853dadcce
--- /dev/null
+++ b/fleet/build/codecache/fleet.build.codecache.iml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/codecache/gradlebuild/build.gradle.kts b/fleet/build/codecache/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..b7c97956cc34
--- /dev/null
+++ b/fleet/build/codecache/gradlebuild/build.gradle.kts
@@ -0,0 +1,69 @@
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.codecache"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ exclude(group = "org.slf4j", module = "slf4j-jdk14")
+ }
+ implementation(jps.com.jetbrains.intellij.platform.util.zip.squashed55996306.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.com.jetbrains.intellij.idea.idea.community.build.zip464377771.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
+ }
+ implementation(project(":fleet.bundles"))
+ implementation(project(":fleet.codecache"))
+ implementation(project(":fleet.build.platform"))
+ implementation(project(":fleet.build.fs"))
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt
new file mode 100644
index 000000000000..2d9a4c13aaa5
--- /dev/null
+++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt
@@ -0,0 +1,31 @@
+package fleet.buildtool.codecache
+
+import java.lang.module.ModuleDescriptor
+import java.lang.module.ModuleFinder
+import java.nio.file.Path
+
+data class HashedJar(
+ val hash: String,
+ val file: Path,
+ val moduleDescriptor: String?,
+) {
+ companion object {
+ fun fromFile(hash: String, file: Path, jdkVersionFeature: Int): HashedJar = HashedJar(
+ hash = hash,
+ file = file,
+ moduleDescriptor = findModuleDescriptorOrNull(file)?.serialize(jdkVersionFeature),
+ )
+ }
+}
+
+fun findModuleDescriptorOrNull(jarFile: Path): ModuleDescriptor? {
+ val allModules = ModuleFinder.of(jarFile).findAll()
+ return when {
+ allModules.isEmpty() -> null
+ allModules.size > 1 -> error("One jar file should have only one module: $jarFile")
+ else -> allModules.single().descriptor()
+ }
+}
+
+fun findModuleDescriptor(jarFile: Path): ModuleDescriptor = findModuleDescriptorOrNull(jarFile)
+ ?: error("a module descriptor must be able to be derived from '$jarFile'")
diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt
new file mode 100644
index 000000000000..db9dd1cfd053
--- /dev/null
+++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt
@@ -0,0 +1,370 @@
+package fleet.buildtool.codecache
+
+import java.lang.module.ModuleDescriptor
+import java.io.ByteArrayOutputStream
+import java.io.DataOutputStream
+import java.util.*
+
+/**
+ * Serialises this module descriptor into the class file format.
+ *
+ * Class file version will be inferred from the [jdkVersionFeature] parameter.
+ *
+ * If the module is "automatic" it will be converted into an "open" module.
+ *
+ * See: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html
+ */
+fun ModuleDescriptor.serialize(jdkVersionFeature: Int): String = let { descriptor ->
+ // As defined by https://www.oracle.com/java/technologies/javase/10-relnote-issues.html#Remaining
+ val CLASS_FILE_VERSION_MAJOR = 44 + jdkVersionFeature
+ val CLASS_FILE_VERSION_MINOR = 0
+
+ assert(CLASS_FILE_VERSION_MAJOR >= 53) {
+ "Modules are not supported by this class file version ($CLASS_FILE_VERSION_MAJOR.$CLASS_FILE_VERSION_MINOR)"
+ }
+
+ return byteArrayBuilder { dos ->
+ dos.writeInt(CLASS_FILE_MAGIC)
+
+ dos.writeShort(CLASS_FILE_VERSION_MINOR)
+ dos.writeShort(CLASS_FILE_VERSION_MAJOR)
+
+ // Constant pool is filled in by its users (e.g. writeModule).
+ val pool = ConstantPool()
+ val module = byteArrayBuilder { it.writeModule(descriptor.automaticToOpen(), pool) }
+
+ // Write constant pool.
+ dos.writeShort(pool.count)
+ for ((constant, _) in pool.constants.toList().sortedBy { it.second }) {
+ dos.writeConstant(constant)
+ }
+
+ // Write serialised module.
+ dos.write(module)
+ }.let {
+ Base64.getEncoder().encodeToString(it)
+ }
+}
+
+/**
+ * Converts an automatic module to an open module.
+ *
+ * Automatic modules are not serializable as by definition they are not represented by a `module-info.class`. We need them to be serialized
+ * for loading optimization at runtime.
+ *
+ * This function must be aligned with its runtime equivalent [fleet.util.modules.convertOpenToAutomatic]
+ */
+private fun ModuleDescriptor.automaticToOpen(): ModuleDescriptor {
+ val moduleDescriptor = this
+ if (!moduleDescriptor.isAutomatic) return moduleDescriptor
+ return ModuleDescriptor.newOpenModule(moduleDescriptor.name()).apply {
+ if (moduleDescriptor.version().isPresent) {
+ version(moduleDescriptor.version().get())
+ }
+ moduleDescriptor.requires().forEach {
+ requires(it)
+ }
+ moduleDescriptor.provides().forEach {
+ provides(it)
+ }
+ packages(moduleDescriptor.packages())
+ }.build()
+}
+
+/**
+ * Serialises the part of the class file that pertains to a particular module (its flags, attributes, main class, etc.)
+ */
+private fun DataOutputStream.writeModule(descriptor: ModuleDescriptor, pool: ConstantPool) {
+ // Access flags. ACC_MODULE means that this class file is a module.
+ writeShort(ACC_MODULE)
+ // this_class item must be "module-info" class in a module class file.
+ writeShort(pool.getOrCreateClass("module-info"))
+
+ // super_class item must be 0 in module class files.
+ writeShort(0)
+ // interfaces_count item must be 0 in module class files.
+ writeShort(0)
+ // fields_count item must be 0 in module class files.
+ writeShort(0)
+ // methods_count item must be 0 in module class files.
+ writeShort(0)
+
+
+ // We will write Module attribute, SourceFile attribute and, optionally, ModulePackages and ModuleMainClass
+ // attributes if they are present.
+ //
+ // This means the attributes count will be in the range 2 to 4.
+ val attributesCount = when {
+ descriptor.mainClass().isPresent && descriptor.packages().isNotEmpty() -> 4
+ descriptor.mainClass().isPresent || descriptor.packages().isNotEmpty() -> 3
+ else -> 2
+ }
+
+ writeShort(attributesCount)
+
+ // Write SourceFile attribute.
+ writeAttribute("SourceFile", pool) {
+ it.writeSourceFileAttributeInfo("module-info.java", pool)
+ }
+
+ // Write Module attribute.
+ writeAttribute("Module", pool) {
+ it.writeModuleAttributeInfo(descriptor, pool)
+ }
+
+ // Write ModuleMainClass attribute if present.
+ descriptor.mainClass().ifPresent { mainClass ->
+ writeAttribute("ModuleMainClass", pool) {
+ it.writeModuleMainClassAttributeInfo(mainClass, pool)
+ }
+ }
+
+ // Write ModulePackages attribute if any are present.
+ descriptor.packages().takeIf { it.isNotEmpty() }?.let { packages ->
+ writeAttribute("ModulePackages", pool) {
+ it.writeModulePackagesAttributeInfo(packages, pool)
+ }
+ }
+}
+
+private inline fun DataOutputStream.writeAttribute(attributeName: String, pool: ConstantPool, body: (DataOutputStream) -> Unit) {
+ // attribute_info {
+ // u2 attribute_name_index;
+ // u4 attribute_length;
+ // u1 info[attribute_length];
+ // }
+
+ val bytes = byteArrayBuilder { body(it) }
+
+ writeShort(pool.getOrCreateUtf8(attributeName))
+ writeInt(bytes.size)
+ write(bytes)
+}
+
+private fun DataOutputStream.writeModuleAttributeInfo(descriptor: ModuleDescriptor, pool: ConstantPool) {
+ // Write module name.
+ writeShort(pool.getOrCreateModule(descriptor.name()))
+
+ // Write module flags.
+ var flags = 0x0000
+ for (modifier in descriptor.modifiers()) {
+ when (modifier) {
+ ModuleDescriptor.Modifier.OPEN -> flags = flags or MODULE_ACC_OPEN
+ ModuleDescriptor.Modifier.SYNTHETIC -> flags = flags or MODULE_ACC_SYNTHETIC
+ ModuleDescriptor.Modifier.MANDATED -> flags = flags or MODULE_ACC_MANDATED
+ ModuleDescriptor.Modifier.AUTOMATIC, null -> Unit
+ }
+ }
+ writeShort(flags)
+
+ // Write module version.
+ writeShort(descriptor.rawVersion().map { pool.getOrCreateUtf8(it) }.orElse(0))
+
+ // Write requires.
+ writeShort(descriptor.requires().size)
+ for (require in descriptor.requires().sorted()) {
+ writeShort(pool.getOrCreateModule(require.name()))
+
+ flags = 0x0000
+ for (modifier in require.modifiers()) {
+ when (modifier) {
+ ModuleDescriptor.Requires.Modifier.TRANSITIVE -> flags = flags or REQ_ACC_TRANSITIVE
+ ModuleDescriptor.Requires.Modifier.STATIC -> flags = flags or REQ_ACC_STATIC_PHASE
+ ModuleDescriptor.Requires.Modifier.SYNTHETIC -> flags = flags or REQ_ACC_SYNTHETIC
+ ModuleDescriptor.Requires.Modifier.MANDATED -> flags = flags or REQ_ACC_MANDATED
+ null -> Unit
+ }
+ }
+ writeShort(flags)
+
+ writeShort(require.rawCompiledVersion().map { pool.getOrCreateUtf8(it) }.orElse(0))
+ }
+
+ // Write exports.
+ writeShort(descriptor.exports().size)
+ for (export in descriptor.exports().sorted()) {
+ writeShort(pool.getOrCreatePackage(export.source()))
+
+ flags = 0x0000
+ for (modifier in export.modifiers()) {
+ when (modifier) {
+ ModuleDescriptor.Exports.Modifier.SYNTHETIC -> flags = flags or EXP_ACC_SYNTHETIC
+ ModuleDescriptor.Exports.Modifier.MANDATED -> flags = flags or EXP_ACC_MANDATED
+ null -> Unit
+ }
+ }
+ writeShort(flags)
+
+ writeShort(export.targets().size)
+ for (target in export.targets().sorted()) {
+ writeShort(pool.getOrCreateModule(target))
+ }
+ }
+
+ // Write opens.
+ writeShort(descriptor.opens().size)
+ for (open in descriptor.opens().sorted()) {
+ writeShort(pool.getOrCreatePackage(open.source()))
+
+ flags = 0x0000
+ for (modifier in open.modifiers()) {
+ when (modifier) {
+ ModuleDescriptor.Opens.Modifier.SYNTHETIC -> flags = flags or OP_ACC_SYNTHETIC
+ ModuleDescriptor.Opens.Modifier.MANDATED -> flags = flags or OP_ACC_MANDATED
+ null -> Unit
+ }
+ }
+ writeShort(flags)
+
+ writeShort(open.targets().size)
+ for (target in open.targets().sorted()) {
+ writeShort(pool.getOrCreateModule(target))
+ }
+ }
+
+ // Write uses.
+ writeShort(descriptor.uses().size)
+ for (use in descriptor.uses().sorted()) {
+ writeShort(pool.getOrCreateClass(use))
+ }
+
+ // Write provides.
+ writeShort(descriptor.provides().size)
+ for (provide in descriptor.provides().sorted()) {
+ writeShort(pool.getOrCreateClass(provide.service()))
+ writeShort(provide.providers().size)
+ for (provider in provide.providers().sorted()) {
+ writeShort(pool.getOrCreateClass(provider))
+ }
+ }
+}
+
+private fun DataOutputStream.writeSourceFileAttributeInfo(fileName: String, pool: ConstantPool) {
+ writeShort(pool.getOrCreateUtf8(fileName))
+}
+
+private fun DataOutputStream.writeModuleMainClassAttributeInfo(mainClass: String, pool: ConstantPool) {
+ writeShort(pool.getOrCreateClass(mainClass))
+}
+
+private fun DataOutputStream.writeModulePackagesAttributeInfo(packages: Set, pool: ConstantPool) {
+ writeShort(packages.size)
+ for (packageName in packages.sorted()) {
+ writeShort(pool.getOrCreatePackage(packageName))
+ }
+}
+
+private inline fun byteArrayBuilder(body: (DataOutputStream) -> Unit): ByteArray {
+ return ByteArrayOutputStream().use { baos ->
+ DataOutputStream(baos).use { dos -> body(dos) }
+
+ baos.toByteArray()
+ }
+}
+
+private fun DataOutputStream.writeConstant(constant: Constant) {
+ when (constant) {
+ is Constant.Utf8 -> {
+ writeByte(ConstantPoolTag.Utf8.value)
+ writeUTF(constant.value)
+ }
+
+ is Constant.Class -> {
+ writeByte(ConstantPoolTag.Class.value)
+ writeShort(constant.classNameIndex)
+ }
+
+ is Constant.Module -> {
+ writeByte(ConstantPoolTag.Module.value)
+ writeShort(constant.moduleNameIndex)
+ }
+
+ is Constant.Package -> {
+ writeByte(ConstantPoolTag.Package.value)
+ writeShort(constant.packageNameIndex)
+ }
+ }
+}
+
+private class ConstantPool {
+ private var index = 1
+
+ val constants = mutableMapOf()
+
+ // Specification defines constant pool count to be count of entries in the constant pool table plus one.
+ val count get() = constants.size + 1
+
+ fun getOrCreateUtf8(string: String): Int {
+ return constants.computeIfAbsent(Constant.Utf8(string)) { index++ }
+ }
+
+ fun getOrCreateClass(className: String): Int {
+ val classNameIndex = getOrCreateUtf8(internalFormName(className))
+ return constants.computeIfAbsent(Constant.Class(classNameIndex)) { index++ }
+ }
+
+ fun getOrCreateModule(moduleName: String): Int {
+ // Module names are NOT encoded in the "internal form".
+ val moduleNameIndex = getOrCreateUtf8(moduleName)
+ return constants.computeIfAbsent(Constant.Module(moduleNameIndex)) { index++ }
+ }
+
+ fun getOrCreatePackage(packageName: String): Int {
+ val packageNameIndex = getOrCreateUtf8(internalFormName(packageName))
+ return constants.computeIfAbsent(Constant.Package(packageNameIndex)) { index++ }
+ }
+
+ private fun internalFormName(name: String): String {
+ return name.replace(".", "/")
+ }
+}
+
+private sealed interface Constant {
+ data class Utf8(val value: String) : Constant
+ data class Class(val classNameIndex: Int) : Constant
+ data class Module(val moduleNameIndex: Int) : Constant
+ data class Package(val packageNameIndex: Int) : Constant
+}
+
+private enum class ConstantPoolTag(val value: Int) {
+ Class(7),
+ Fieldref(9),
+ Methodref(10),
+ InterfaceMethodref(11),
+ String(8),
+ Integer(3),
+ Float(4),
+ Long(5),
+ Double(6),
+ NameAndType(12),
+ Utf8(1),
+ MethodHandle(15),
+ MethodType(16),
+ InvokeDynamic(18),
+ Module(19),
+ Package(20),
+}
+
+private const val CLASS_FILE_MAGIC = 0xcafebabe.toInt()
+
+// Access flag denoting that this class file is a module.
+private const val ACC_MODULE = 0x8000
+
+// Module flags.
+private const val MODULE_ACC_OPEN = 0x0020
+private const val MODULE_ACC_SYNTHETIC = 0x1000
+private const val MODULE_ACC_MANDATED = 0x8000
+
+// Requires flags.
+private const val REQ_ACC_TRANSITIVE = 0x0020
+private const val REQ_ACC_STATIC_PHASE = 0x0040
+private const val REQ_ACC_SYNTHETIC = 0x1000
+private const val REQ_ACC_MANDATED = 0x8000
+
+// Exports flags.
+private const val EXP_ACC_SYNTHETIC = 0x1000
+private const val EXP_ACC_MANDATED = 0x8000
+
+// Opens flags.
+private const val OP_ACC_SYNTHETIC = 0x1000
+private const val OP_ACC_MANDATED = 0x8000
diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt
new file mode 100644
index 000000000000..098b6b683e01
--- /dev/null
+++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt
@@ -0,0 +1,393 @@
+package fleet.buildtool.codecache
+
+import com.intellij.util.lang.ImmutableZipFile
+import fleet.buildtool.fs.extractZip
+import fleet.buildtool.platform.Arch
+import fleet.buildtool.platform.Platform
+import fleet.buildtool.platform.toS3DistributionSlug
+import kotlinx.coroutines.*
+import org.slf4j.Logger
+import org.jetbrains.intellij.build.io.*
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+import java.util.concurrent.ConcurrentHashMap
+import kotlin.io.path.*
+
+const val allowedLicenseClientConsumerModule: String = "fleet.plugins.ship.common"
+private fun allowedLicenseClientConsumerJar(version: String) = "fleet.common-$version.jar" // fleet.common jar
+const val licenseClientApiJpmsModuleName: String = "ls.client.api"
+private const val licenseClientApiJarName = "ls-client-api.jar"
+private val jnaJarNamePattern = Regex("jna-\\d.*\\.jar")
+private val kotlinStdlibJarNamePattern = Regex("kotlin-stdlib-\\d.*\\.jar")
+const val allowedDockCoroutinesConsumerModule: String = "fleet.dock.runtime"
+const val dockCoroutinesJpmsModuleName: String = "fleet.dock.coroutines"
+private fun dockCoroutinesJarName(version: String) = "fleet.dock.coroutines-$version.jar"
+
+private typealias Jar = Path
+
+data class PackedJar(
+ val path: Path,
+ val needsScrambling: Boolean,
+ val nativeFilesByPlatform: Map>,
+)
+
+data class PackedModule(
+ val moduleName: String,
+ val jarFiles: List,
+)
+
+/**
+ * Represents a module that can be packed as one or more jars.
+ */
+interface ModuleToPack {
+ val name: String
+ val filesToPack: List
+}
+
+sealed class NativeLibraryExtractor {
+ /**
+ * Jars would be left untouched.
+ */
+ object Noop : NativeLibraryExtractor()
+
+ /**
+ * Native libraries inside jars will be extracted to [directory]
+ */
+ data class ExtractTo(val directory: Path) : NativeLibraryExtractor()
+}
+
+class ModulePacker(
+ private val directory: Path,
+ private val nativeLibraryExtractor: NativeLibraryExtractor,
+ private val version: String,
+ private val logger: Logger,
+) {
+ private val cache = ConcurrentHashMap()
+
+ private data class CacheEntry(
+ val jar: PackedJar,
+ val shadowed: Path?,
+ )
+
+ fun packModule(module: ModuleToPack): PackedModule {
+ val licenseClientApiJar = module.filesToPack.singleOrNullOrThrow { // direct dependency on license client?
+ it.name.contains(licenseClientApiJarName)
+ }
+ require(licenseClientApiJar == null || module.name == allowedLicenseClientConsumerModule) {
+ "module '${module.name} is not allowed to pack '$licenseClientApiJarName'"
+ }
+ val allowedLicenseClientConsumerJarName = allowedLicenseClientConsumerJar(version)
+ require(licenseClientApiJar == null || module.filesToPack.singleOrNull { it.name == allowedLicenseClientConsumerJarName } != null) {
+ "module '${module.name} shadows '$licenseClientApiJar' so it must want to pack a jar '$allowedLicenseClientConsumerJarName'"
+ }
+
+ val dockCoroutinesHackJar = module.filesToPack.singleOrNullOrThrow { it.name == dockCoroutinesJarName(version) }
+ require(dockCoroutinesHackJar == null || module.name == allowedDockCoroutinesConsumerModule) {
+ "module '${module.name} is not allowed to pack '$dockCoroutinesHackJar'"
+ }
+ require(dockCoroutinesHackJar == null || module.filesToPack.singleOrNull { f -> kotlinStdlibJarNamePattern.matches(f.name) } != null) {
+ "Module '${module.name}' tries to shadow '$dockCoroutinesHackJar' but is not containing only a single jar matching '$kotlinStdlibJarNamePattern'"
+ }
+
+ require(licenseClientApiJar == null || dockCoroutinesHackJar == null) {
+ "Module '${module.name}' tries to shadow both '$licenseClientApiJar' and '$dockCoroutinesHackJar', this is not allowed by our build tool, contact #fleet-platform"
+ }
+
+ val toPack = module.filesToPack.filter { it != licenseClientApiJar && it != dockCoroutinesHackJar } // never pack the shadowed jar
+ val jars = runBlocking(Dispatchers.IO) {
+ toPack.mapConcurrent { path ->
+ val (shadowed, needsScrambling) = when {
+ licenseClientApiJar == null && dockCoroutinesHackJar == null -> null to false
+ dockCoroutinesHackJar != null && kotlinStdlibJarNamePattern.matches(path.name) -> dockCoroutinesHackJar to false
+ licenseClientApiJar != null && path.name == allowedLicenseClientConsumerJarName -> licenseClientApiJar to true
+ else -> null to false
+ }
+ pack(path, needsScrambling = needsScrambling, shadowed = shadowed)
+ }
+ }
+ return PackedModule(
+ moduleName = module.name,
+ jarFiles = jars,
+ )
+ }
+
+ /**
+ * Packs the given [file] into an [ImmutableZipFile] jar.
+ * Also, shadows the given [shadowed], if any, inside the resulting packed jar.
+ * Also, extracts JNA native libraries files from the resulting jar and expose them through [PackedJar.nativeFiles].
+ *
+ * @param file either a directory of classes representing an output of compilation of a module, or a `jar` to be repacked as immutable
+ * @param needsScrambling whether the resulting packed jar will require scrambling in further step of the build tool
+ * @param shadowed if specified, a jar to shadow (extract inside) the resulting packed jar
+ */
+ @OptIn(ExperimentalPathApi::class)
+ private fun pack(file: Path, needsScrambling: Boolean, shadowed: Path?): PackedJar {
+ logger.info("Packing/repacking '$file' (needsScrambling=$needsScrambling, shadowing=$shadowed)")
+ require(!file.absolutePathString().contains(licenseClientApiJarName)) {
+ "$licenseClientApiJarName should have been shadowed, contact #fleet-support"
+ }
+ return cache.compute(file) { fileToPack, existing ->
+ when (existing) {
+ null -> {
+ directory.createDirectories()
+ logger.debug("'{}' (needsScrambling={}, shadowing={}) not found in cache, packing it...", file, needsScrambling, shadowed)
+ val jar = when {
+ Files.isDirectory(fileToPack) -> packToImmutableJar(
+ path = fileToPack,
+ needsScrambling = needsScrambling,
+ shadowed = shadowed,
+ target = directory.resolve("${fileToPack.fileName}-$version.jar"),
+ logger = logger,
+ )
+
+ nativeLibraryExtractor is NativeLibraryExtractor.ExtractTo && jnaJarNamePattern.matches(fileToPack.name) -> repackToImmutableJarExtractingNativeFiles(
+ fileToPack,
+ needsScrambling,
+ shadowed,
+ targetDirectory = directory,
+ nativeLibrariesTargetDirectory = nativeLibraryExtractor.directory,
+ logger = logger,
+ )
+
+ nativeLibraryExtractor is NativeLibraryExtractor.ExtractTo && fileToPack.name.contains("kotlin-desktop-toolkit") -> repackToImmutableJarExtractingNativeFiles2(
+ fileToPack,
+ needsScrambling,
+ shadowed,
+ targetDirectory = directory,
+ nativeLibrariesTargetDirectory = nativeLibraryExtractor.directory,
+ logger = logger,
+ )
+
+ fileToPack.name.endsWith(".jar") -> packToImmutableJar(
+ path = fileToPack,
+ needsScrambling = needsScrambling,
+ shadowed = shadowed,
+ target = directory.resolve(fileToPack.name),
+ logger = logger,
+ )
+
+ else -> error("Cannot pack file which is neither directory or jar: '$file'")
+ }
+
+ CacheEntry(
+ jar = jar,
+ shadowed = shadowed,
+ )
+ }
+
+ else -> {
+ require(needsScrambling == existing.jar.needsScrambling) {
+ """
+ |'$file' has already been packed/repacked but with different instructions about scrambling, this is very likely a bug in the build tool, contact #fleet-support
+ |Existing: ${existing.jar.needsScrambling}
+ |Current: ${needsScrambling}
+ """.trimMargin()
+ }
+ require(shadowed == existing.shadowed) {
+ """
+ |'$file' has already been packed/repacked but with different instructions about shadowing, this is very likely a bug in the build tool, contact #fleet-support
+ |Existing: ${existing.shadowed}
+ |Current: ${shadowed}
+ """.trimMargin()
+ }
+ existing
+ }
+ }
+ }?.jar ?: error("must have either found the jar in cache, or pack/repack it")
+ }
+}
+
+fun packToImmutableJar(path: Path, needsScrambling: Boolean, shadowed: Path?, target: Path, logger: Logger): PackedJar {
+ target.deleteIfExists()
+ target.parent.createDirectories()
+ val intermediateJar = when {
+ path.isDirectory() -> packDirectoriesToImmutableJar(target, listOf(path)) // TODO: add direct support for directories packing in `packToImmutableJar` instead
+ else -> path
+ }
+ val jar = packToImmutableJar(target, listOfNotNull(shadowed, intermediateJar), logger) // order of jars list matters, shadowed content overrides original content (reason: DebugProbes.kt of shadowed `:dock-coroutines` subproject must override `kotlin-stdlib` ones)
+ return PackedJar(
+ path = jar,
+ needsScrambling = needsScrambling,
+ nativeFilesByPlatform = emptyMap(),
+ )
+}
+
+// TODO: make it more generic, not only for JNA
+@OptIn(ExperimentalPathApi::class)
+private fun repackToImmutableJarExtractingNativeFiles(jar: Path, needsScrambling: Boolean, shadowed: Path?, targetDirectory: Path, nativeLibrariesTargetDirectory: Path, logger: Logger): PackedJar {
+ require(shadowed == null) { "shadowing not supported" }
+ val tmp = Files.createTempDirectory("repacking").resolve(jar.name)
+ extractZip(archive = jar, destination = tmp, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tmp, logger = logger)
+ val nativeFiles = tmp.resolve("com/sun/jna").listDirectoryEntries()
+ .filter { it.isDirectory() }
+ .mapNotNull { it.listDirectoryEntries().singleOrNull() }
+ .filter { lib -> lib.extension in setOf("jnilib", "dylib", "so", "tbd", "dll", "a") }
+ .mapNotNull { lib ->
+ val libraryPlatform = detectNativeLibraryPlatform(lib.parent.name)
+ if (libraryPlatform != null) {
+ val target = nativeLibrariesTargetDirectory.resolve(libraryPlatform.toS3DistributionSlug()).createDirectories()
+ val targetFile = lib.moveTo(target.resolve(lib.name), overwrite = true)
+ libraryPlatform to targetFile
+ }
+ else {
+ lib.deleteExisting()
+ null
+ }
+ }.groupBy({ (platform, _) -> platform }, { (_, file) -> file })
+ val jar = packDirectoriesToImmutableJar(targetDirectory.resolve(jar.name), listOf(tmp))
+ tmp.deleteRecursively()
+ return PackedJar(
+ path = jar,
+ needsScrambling = needsScrambling,
+ nativeFilesByPlatform = nativeFiles,
+ )
+}
+
+// TODO: delete once `repackToImmutableJarExtractingNativeFiles` is generic
+@OptIn(ExperimentalPathApi::class)
+private fun repackToImmutableJarExtractingNativeFiles2(jar: Path, needsScrambling: Boolean, shadowed: Path?, targetDirectory: Path, nativeLibrariesTargetDirectory: Path, logger: Logger): PackedJar {
+ require(shadowed == null) { "shadowing not supported" }
+ val tmp = Files.createTempDirectory("repacking").resolve(jar.name)
+ extractZip(archive = jar, destination = tmp, cleanDestination = false, stripTopLevelFolder = false, temporaryDir = tmp, logger = logger)
+ val nativeFiles = tmp.listDirectoryEntries()
+ .filter { lib -> lib.extension in setOf("dylib", "so", "dll") }
+ .mapNotNull { lib ->
+ val libName = lib.name
+ val libraryPlatform = detectNativeLibraryPlatformBasedOnItsName(libName)
+ if (libraryPlatform != null) {
+ val target = nativeLibrariesTargetDirectory.resolve(libraryPlatform.toS3DistributionSlug()).createDirectories()
+ val targetFile = lib.moveTo(target.resolve(lib.name), overwrite = true)
+ libraryPlatform to targetFile
+ }
+ else {
+ lib.deleteExisting()
+ null
+ }
+ }.groupBy({ (platform, _) -> platform }, { (_, file) -> file })
+ val jar = packDirectoriesToImmutableJar(targetDirectory.resolve(jar.name), listOf(tmp))
+ tmp.deleteRecursively()
+ return PackedJar(
+ path = jar,
+ needsScrambling = needsScrambling,
+ nativeFilesByPlatform = nativeFiles,
+ )
+}
+
+private fun packToImmutableJar(destinationJar: Path, jars: List, logger: Logger): Jar {
+ require(jars.map { it.extension }.all { it == "jar" }) { "only jars are supported, got '$jars'" }
+ val uniqueJar = jars.singleOrNull()
+ when {
+ uniqueJar != null && ImmutableZipFile.load(uniqueJar).use { it is ImmutableZipFile } -> uniqueJar.copyTo(destinationJar) // already an immutable jar, not repacking
+ else -> {
+ val packageIndexBuilder = PackageIndexBuilder(AddDirEntriesMode.NONE)
+ destinationJar.parent.createDirectories()
+ writeZipUsingTempFile(destinationJar, packageIndexBuilder = packageIndexBuilder) { zipOut ->
+ val uniqueNames = HashMap()
+
+ for (jar in jars) {
+ readZipFile(jar) { name, dataSupplier ->
+ when { // TODO: instead of fixing the FIXMEs, could we instead write a `META-INF/MANIFEST.MF` merger with heuristics and hard failures in case it's not heuristically possible to merge them?
+ name == "META-INF/MANIFEST.MF" && jar.name.contains("fleet.dock.coroutines") && jars.any { it.name.contains("kotlin-stdlib") } -> { // FIXME: remove this hack, we control `fleet.dock.coroutines`, could we remove the `META-INF/MANIFEST.MF` instead to avoid the conflict?
+ logger.warn("WARNING: '$name' from '${jar.name}' is ignored, `fleet.dock.coroutines` jar has a `META-INF/MANIFEST.MF` which conflicts with the `kotlin-stdlib` one")
+ }
+ uniqueNames.contains(name) -> {
+ logger.warn("WARNING: '$name' from '${jar.name}' is ignored, reason: tooling already packed the one present in '${uniqueNames[name]}'")
+ }
+ else -> {
+ uniqueNames[name] = jar
+
+ packageIndexBuilder.addFile(name)
+ val data = dataSupplier()
+ zipOut.uncompressedData(name, data)
+ }
+ }
+ ZipEntryProcessorResult.CONTINUE
+ }
+ }
+ }
+ }
+ }
+ return destinationJar
+}
+
+private fun packDirectoriesToImmutableJar(destinationJar: Path, directories: List): Jar {
+ require(directories.all { it.isDirectory() }) { "'$directories' must be a directory" }
+ val tmpFile = Files.createTempFile(destinationJar.fileName.toString(), "")
+ zip(tmpFile, directories.associateWith { "" }, overwrite = true)
+ return tmpFile.moveTo(destinationJar)
+}
+
+private suspend inline fun Iterable.mapConcurrent(crossinline transform: suspend (T) -> U): List =
+ coroutineScope {
+ map {
+ async { transform(it) }
+ }.awaitAll()
+ }
+
+private fun Iterable.singleOrNullOrThrow(p: (T) -> Boolean = { true }): T? {
+ var single: T? = null
+ var found = false
+ for (element in this) {
+ if (p(element)) {
+ if (found) {
+ throw IllegalArgumentException("Collection contains more than one matching element: $single, $element")
+ }
+ single = element
+ found = true
+ }
+ }
+ return single
+}
+
+private fun detectNativeLibraryPlatform(path: String): Platform? {
+ return when {
+ path.contains("x86-64") -> Arch.X64
+ path.contains("aarch64") -> Arch.AARCH64
+ else -> null
+ }?.let { arch ->
+ when {
+ path.startsWith("win32") -> Platform.windows(arch)
+ path.startsWith("linux") -> Platform.linux(arch)
+ path.startsWith("darwin") -> Platform.macos(arch)
+ else -> null
+ }
+ }
+}
+
+/**
+ * Skiko and KotlinDesktopToolkit follow this pattern
+ */
+private fun detectNativeLibraryPlatformBasedOnItsName(libName: String): Platform? {
+ return when {
+ libName.contains("x64") -> Arch.X64
+ libName.contains("arm64") -> Arch.AARCH64
+ else -> null
+ }?.let { arch ->
+ when {
+ libName.contains("windows") -> Platform.windows(arch)
+ libName.contains("linux") -> Platform.linux(arch)
+ libName.contains("macos") -> Platform.macos(arch)
+ else -> null
+ }
+ }
+}
+
+fun Iterable.patchKotlinStdlibDebugProbes(dockCoroutinesJar: Path, repackedJarDir: Path, logger: Logger): List {
+ // This is a dirty hack to avoid bytebuddy injection at runtime
+ // Business wise, it's valid, implementation wise, it's hacky as it breaks encapsulation.
+ // All of that is supposed to change soon with the Gradle/Bazel plans.
+ val (stdlibJars, others) = this.partition { kotlinStdlibJarNamePattern.matches(it.name) }
+ val stdlibJar = stdlibJars.singleOrNull()?.toPath()
+ return when {
+ stdlibJar != null -> {
+ val repackedStdlibJar = repackedJarDir.resolve(stdlibJar.name)
+ repackedStdlibJar.deleteIfExists()
+ repackedStdlibJar.parent.createDirectories()
+ val jar = packToImmutableJar(repackedStdlibJar, listOf(dockCoroutinesJar, stdlibJar), logger)
+ others + jar.toFile()
+ }
+
+ else -> others
+ }
+}
\ No newline at end of file
diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt
new file mode 100644
index 000000000000..8cb4362261b9
--- /dev/null
+++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt
@@ -0,0 +1,44 @@
+package fleet.buildtool.codecache
+
+import fleet.buildtool.platform.Platform
+import fleet.bundles.Coordinates
+import fleet.bundles.PluginDescriptor
+import fleet.bundles.PluginName
+import fleet.bundles.PluginVersion
+import fleet.bundles.metaAsCoordinates
+import java.nio.file.Path
+import kotlin.io.path.name
+
+fun urlForPluginResource(baseUrl: String, plugin: PluginId, resourceFilename: String): String = "$baseUrl/api/fleet/download?xmlId=${plugin.name.name}&version=${plugin.version.versionString}&module=$resourceFilename"
+
+data class PluginId(
+ val name: PluginName,
+ val version: PluginVersion,
+)
+
+sealed class PluginPart {
+ data class Bundled(val jar: HashedJar) : PluginPart() {
+ override fun toCoordinates(baseUrl: String, pluginId: PluginId): Coordinates = Coordinates.Remote(
+ url = urlForPluginResource(baseUrl, pluginId, jar.file.name),
+ hash = jar.hash,
+ )
+
+ override val codeCacheFile: Path = jar.file
+ override val relativePathInCodeCache: Path = Path.of(jar.hash, codeCacheFile.fileName.toString())
+ }
+
+ abstract fun toCoordinates(baseUrl: String, pluginId: PluginId): Coordinates
+ abstract val codeCacheFile: Path
+
+ abstract val relativePathInCodeCache: Path
+}
+
+fun PluginDescriptor.fsdaemonCoordinates(platform: Platform): Coordinates? = metaAsCoordinates(fsdaemonCoordinatesMetadataAttributeOf(platform))
+fun fsdaemonCoordinatesMetadataAttributeOf(platform: Platform): String = when (platform) {
+ Platform.Linux.LinuxAarch64 -> "fsd-binaries-linux_aarch64"
+ Platform.Linux.LinuxX64 -> "fsd-binaries-linux_x64"
+ Platform.MacOs.MacOsAarch64 -> "fsd-binaries-macos_aarch64"
+ Platform.MacOs.MacOsX64 -> "fsd-binaries-macos_x64"
+ Platform.Windows.WindowsAarch64 -> "fsd-binaries-windows_aarch64"
+ Platform.Windows.WindowsX64 -> "fsd-binaries-windows_x64"
+}
diff --git a/fleet/build/fleet.bzl b/fleet/build/fleet.bzl
new file mode 100644
index 000000000000..991ea2a6ee3c
--- /dev/null
+++ b/fleet/build/fleet.bzl
@@ -0,0 +1,3 @@
+load("//:fleet/build/rules/fleet_module.bzl", _fleet_module = "fleet_module")
+
+fleet_module = _fleet_module
diff --git a/fleet/build/fs/BUILD.bazel b/fleet/build/fs/BUILD.bazel
new file mode 100644
index 000000000000..e60be36a8411
--- /dev/null
+++ b/fleet/build/fs/BUILD.bazel
@@ -0,0 +1,67 @@
+### auto-generated section `build fleet.build.fs` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
+
+create_kotlinc_options(
+ name = "custom_fs",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+resourcegroup(
+ name = "fs_test_resources",
+ srcs = glob(["resourcesJvmTest/**/*"]),
+ strip_prefix = "resourcesJvmTest"
+)
+
+jvm_library(
+ name = "fs",
+ module_name = "fleet.build.fs",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_fs",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:slf4j-api",
+ "@lib//:fleet-github-luben-zstd-jni",
+ "@lib//:fleet-tukaani-xz",
+ "@lib//:commons-compress",
+ "@lib//:commons-io",
+ "@lib//:commons-lang3",
+ ]
+)
+
+jvm_library(
+ name = "fs_test_lib",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmTest/**/*.kt", "srcJvmTest/**/*.java", "srcJvmTest/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ resources = [":fs_test_resources"],
+ kotlinc_opts = ":custom_fs",
+ associates = [":fs"],
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:slf4j-api",
+ "@lib//:fleet-github-luben-zstd-jni",
+ "@lib//:fleet-tukaani-xz",
+ "//fleet/junit4",
+ "@lib//:kotlin-test",
+ "@lib//:commons-compress",
+ "@lib//:commons-io",
+ "@lib//:commons-lang3",
+ ]
+)
+### auto-generated section `build fleet.build.fs` end
+
+### auto-generated section `test fleet.build.fs` start
+load("@community//build:tests-options.bzl", "jps_test")
+
+jps_test(
+ name = "fs_test",
+ runtime_deps = [":fs_test_lib"]
+)
+### auto-generated section `test fleet.build.fs` end
\ No newline at end of file
diff --git a/fleet/build/fs/fleet.build.fs.iml b/fleet/build/fs/fleet.build.fs.iml
new file mode 100644
index 000000000000..23e00f3641da
--- /dev/null
+++ b/fleet/build/fs/fleet.build.fs.iml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/fs/gradlebuild/build.gradle.kts b/fleet/build/fs/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..dbd15b386409
--- /dev/null
+++ b/fleet/build/fs/gradlebuild/build.gradle.kts
@@ -0,0 +1,76 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.fs"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ exclude(group = "org.slf4j", module = "slf4j-jdk14")
+ }
+ implementation(jps.com.github.luben.zstd.jni705422806.get())
+ implementation(jps.org.tukaani.xz14489971.get())
+ implementation(jps.org.apache.commons.commons.compress347955106.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ exclude(group = "commons-codec", module = "commons-codec")
+ exclude(group = "commons-io", module = "commons-io")
+ exclude(group = "org.apache.commons", module = "commons-lang3")
+ }
+ implementation(jps.commons.io.commons.io645698317.get())
+ implementation(jps.org.apache.commons.commons.lang3579297339.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ }
+ }
+ sourceSets.commonTest.dependencies {
+ implementation(jps.org.jetbrains.kotlin.kotlin.test542871666.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
+ }
+ }
+ sourceSets.jvmTest.dependencies {
+ implementation(project(":fleet.junit4"))
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/fs/resourcesJvmTest/.gitattributes b/fleet/build/fs/resourcesJvmTest/.gitattributes
new file mode 100644
index 000000000000..b3fe708f8862
--- /dev/null
+++ b/fleet/build/fs/resourcesJvmTest/.gitattributes
@@ -0,0 +1,3 @@
+example-abi-crlf.txt eol=crlf
+example-abi-lf.txt eol=lf
+example-abi-cr.txt eol=cr
diff --git a/fleet/build/fs/resourcesJvmTest/archive/empty_folder.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/empty_folder.tar.gz
new file mode 100644
index 000000000000..d2b58df2002e
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/empty_folder.tar.gz differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/empty_folder.zip b/fleet/build/fs/resourcesJvmTest/archive/empty_folder.zip
new file mode 100644
index 000000000000..a445e4af29d6
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/empty_folder.zip differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/mixed.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/mixed.tar.gz
new file mode 100644
index 000000000000..d7c9961df0a4
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/mixed.tar.gz differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/mixed.zip b/fleet/build/fs/resourcesJvmTest/archive/mixed.zip
new file mode 100644
index 000000000000..f7fa6556c3a2
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/mixed.zip differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/readonly.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/readonly.tar.gz
new file mode 100644
index 000000000000..10c4416fb8aa
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/readonly.tar.gz differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/readonly.zip b/fleet/build/fs/resourcesJvmTest/archive/readonly.zip
new file mode 100644
index 000000000000..5707c4bc0c58
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/readonly.zip differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/single_symlink.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/single_symlink.tar.gz
new file mode 100644
index 000000000000..3f32e088222d
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/single_symlink.tar.gz differ
diff --git a/fleet/build/fs/resourcesJvmTest/archive/single_symlink.zip b/fleet/build/fs/resourcesJvmTest/archive/single_symlink.zip
new file mode 100644
index 000000000000..9d6408b5dd4b
Binary files /dev/null and b/fleet/build/fs/resourcesJvmTest/archive/single_symlink.zip differ
diff --git a/fleet/build/fs/resourcesJvmTest/example-abi-cr.txt b/fleet/build/fs/resourcesJvmTest/example-abi-cr.txt
new file mode 100644
index 000000000000..b3bf453920b0
--- /dev/null
+++ b/fleet/build/fs/resourcesJvmTest/example-abi-cr.txt
@@ -0,0 +1 @@
+public abstract interface class fleet/dock/api/DockApi : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/DockApi$Companion;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun getRecentShips ()Lfleet/dock/api/RecentShips;
public abstract fun getShipScope ()Lfleet/dock/api/ShipScope;
public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V
public static synthetic fun perform$default (Lfleet/dock/api/DockApi;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V
public abstract fun withShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class fleet/dock/api/DockApi$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/DockApi$DefaultImpls {
public static fun fold (Lfleet/dock/api/DockApi;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class fleet/dock/api/DockContextElement : kotlin/coroutines/CoroutineContext$Element {
public fun (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)V
public final fun component1 ()Lfleet/dock/api/DockContextElement$Key;
public final fun component2 ()Ljava/lang/Object;
public final fun copy (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)Lfleet/dock/api/DockContextElement;
public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement;Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement;
public fun equals (Ljava/lang/Object;)Z
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public fun getKey ()Lfleet/dock/api/DockContextElement$Key;
public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public final fun getValue ()Ljava/lang/Object;
public fun hashCode ()I
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/DockContextElement$Key : kotlin/coroutines/CoroutineContext$Key {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockContextElement$Key;
public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement$Key;
public fun equals (Ljava/lang/Object;)Z
public final fun getKey ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/DockExit : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/DockExit$Companion;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun quit ()V
}
public final class fleet/dock/api/DockExit$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/DockExit$DefaultImpls {
public static fun fold (Lfleet/dock/api/DockExit;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public abstract interface class fleet/dock/api/DockIdentity : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/DockIdentity$Companion;
public abstract fun getHostId ()Ljava/lang/String;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun getUserId ()Ljava/lang/String;
}
public final class fleet/dock/api/DockIdentity$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/DockIdentity$DefaultImpls {
public static fun fold (Lfleet/dock/api/DockIdentity;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class fleet/dock/api/DockPath {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockPath;
public static synthetic fun copy$default (Lfleet/dock/api/DockPath;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockPath;
public fun equals (Ljava/lang/Object;)Z
public final fun getKey ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/DockPaths : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/DockPaths$Companion;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun getPath (Lfleet/dock/api/DockPath;)Ljava/lang/Object;
}
public final class fleet/dock/api/DockPaths$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/DockPaths$DefaultImpls {
public static fun fold (Lfleet/dock/api/DockPaths;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public abstract interface class fleet/dock/api/LocalStorage : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/LocalStorage$Companion;
public abstract fun get (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer;
public fun getGlobal ()Lfleet/dock/api/LocalStorage;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun put (Lfleet/dock/api/LocalStorageKey;Ljava/nio/ByteBuffer;)V
public abstract fun remove (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer;
}
public final class fleet/dock/api/LocalStorage$Companion : kotlin/coroutines/CoroutineContext$Key {
public final fun withPrefix (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class fleet/dock/api/LocalStorage$DefaultImpls {
public static fun fold (Lfleet/dock/api/LocalStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class fleet/dock/api/LocalStorageKey {
public fun (Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/LocalStorageKey;
public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageKey;
public fun equals (Ljava/lang/Object;)Z
public final fun getKey ()Ljava/lang/String;
public final fun getVersion ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/LocalStorageKt {
public static final fun getString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;)Ljava/lang/String;
public static final fun putString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;)V
}
public final class fleet/dock/api/LocalStorageState : kotlin/coroutines/CoroutineContext$Element {
public fun (Lfleet/dock/api/LocalStorageState$Key;Lkotlinx/coroutines/flow/MutableStateFlow;)V
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public fun getKey ()Lfleet/dock/api/LocalStorageState$Key;
public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public final fun getState ()Lkotlinx/coroutines/flow/MutableStateFlow;
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class fleet/dock/api/LocalStorageState$Key : kotlin/coroutines/CoroutineContext$Key {
public fun (Lfleet/dock/api/LocalStorageKey;)V
public final fun component1 ()Lfleet/dock/api/LocalStorageKey;
public final fun copy (Lfleet/dock/api/LocalStorageKey;)Lfleet/dock/api/LocalStorageState$Key;
public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageState$Key;Lfleet/dock/api/LocalStorageKey;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageState$Key;
public fun equals (Ljava/lang/Object;)Z
public final fun getKey ()Lfleet/dock/api/LocalStorageKey;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/RecentShipInfo {
public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V
public final fun component1 ()Lfleet/dock/api/ShipType;
public final fun component2 ()Lfleet/dock/api/ShipParams;
public final fun component3 ()Lfleet/dock/api/ShipState;
public final fun component4 ()Lfleet/dock/api/ShipDescriptor;
public final fun component5 ()Ljava/util/Map;
public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/RecentShipInfo;
public static synthetic fun copy$default (Lfleet/dock/api/RecentShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RecentShipInfo;
public fun equals (Ljava/lang/Object;)Z
public final fun getMeta ()Ljava/util/Map;
public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
public final fun getShipParams ()Lfleet/dock/api/ShipParams;
public final fun getShipState ()Lfleet/dock/api/ShipState;
public final fun getShipType ()Lfleet/dock/api/ShipType;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/RecentShips {
public abstract fun forget (Lfleet/dock/api/ShipId;)V
public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow;
}
public final class fleet/dock/api/RunningShipInfo {
public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)V
public final fun component1 ()Lfleet/dock/api/ShipType;
public final fun component2 ()Lfleet/dock/api/ShipParams;
public final fun component3 ()Lfleet/dock/api/ShipDescriptor;
public final fun component4 ()Lfleet/dock/api/ShipHandle;
public final fun component5 ()Ljava/util/Map;
public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)Lfleet/dock/api/RunningShipInfo;
public static synthetic fun copy$default (Lfleet/dock/api/RunningShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RunningShipInfo;
public fun equals (Ljava/lang/Object;)Z
public final fun getHandle ()Lfleet/dock/api/ShipHandle;
public final fun getMeta ()Ljava/util/Map;
public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
public final fun getShipParams ()Lfleet/dock/api/ShipParams;
public final fun getShipType ()Lfleet/dock/api/ShipType;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/SelectedThemeState : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/SelectedThemeState$Companion;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun getTheme ()Lkotlinx/coroutines/flow/StateFlow;
public abstract fun setTheme (Lfleet/dock/api/ThemeId;)V
}
public final class fleet/dock/api/SelectedThemeState$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/SelectedThemeState$DefaultImpls {
public static fun fold (Lfleet/dock/api/SelectedThemeState;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class fleet/dock/api/SelectedThemeStateKt {
public static final fun getCurrent (Lfleet/dock/api/SelectedThemeState;)Lfleet/dock/api/ThemeId;
}
public abstract interface class fleet/dock/api/Ship {
public abstract fun getMeta ()Ljava/util/Map;
public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow;
public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V
public static synthetic fun perform$default (Lfleet/dock/api/Ship;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V
}
public final class fleet/dock/api/ShipBundle {
public fun (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V
public final fun component1 ()Ljava/lang/ModuleLayer;
public final fun component2 ()Ljava/util/Map;
public final fun component3 ()Lfleet/dock/api/ShipDescriptor;
public final fun component4 ()Ljava/util/Map;
public final fun copy (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/ShipBundle;
public static synthetic fun copy$default (Lfleet/dock/api/ShipBundle;Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipBundle;
public fun equals (Ljava/lang/Object;)Z
public final fun getLayers ()Ljava/util/Map;
public final fun getMeta ()Ljava/util/Map;
public final fun getModuleLayer ()Ljava/lang/ModuleLayer;
public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipDescriptor {
public fun (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Lfleet/dock/api/ShipVersion;
public final fun component3 ()Ljava/util/Map;
public final fun component4 ()Lfleet/dock/api/ShipSignature;
public final fun component5 ()Ljava/util/Map;
public final fun copy (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)Lfleet/dock/api/ShipDescriptor;
public static synthetic fun copy$default (Lfleet/dock/api/ShipDescriptor;Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipDescriptor;
public fun equals (Ljava/lang/Object;)Z
public final fun getLayers ()Ljava/util/Map;
public final fun getMeta ()Ljava/util/Map;
public final fun getName ()Ljava/lang/String;
public final fun getSignature ()Lfleet/dock/api/ShipSignature;
public final fun getVersion ()Lfleet/dock/api/ShipVersion;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/ShipHandle {
public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun shipIfCompleted ()Lfleet/dock/api/Ship;
public abstract fun shutdown ()V
}
public final class fleet/dock/api/ShipId {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipId;
public static synthetic fun copy$default (Lfleet/dock/api/ShipId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipId;
public fun equals (Ljava/lang/Object;)Z
public final fun getId ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract class fleet/dock/api/ShipJarCoordinates {
}
public final class fleet/dock/api/ShipJarCoordinates$Local : fleet/dock/api/ShipJarCoordinates {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Local;
public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Local;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Local;
public fun equals (Ljava/lang/Object;)Z
public final fun getPath ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipJarCoordinates$Remote : fleet/dock/api/ShipJarCoordinates {
public fun (Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Remote;
public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Remote;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Remote;
public fun equals (Ljava/lang/Object;)Z
public final fun getHash ()Ljava/lang/String;
public final fun getUrl ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipJarFile {
public fun (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)V
public final fun component1 ()Lfleet/dock/api/ShipJarCoordinates;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)Lfleet/dock/api/ShipJarFile;
public static synthetic fun copy$default (Lfleet/dock/api/ShipJarFile;Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarFile;
public fun equals (Ljava/lang/Object;)Z
public final fun getCoordinates ()Lfleet/dock/api/ShipJarCoordinates;
public final fun getSerializedModuleDescriptor ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/ShipLauncher {
public abstract fun withShip (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class fleet/dock/api/ShipLauncherProvider {
public abstract fun withShipLauncher (Lfleet/dock/api/ShipBundle;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class fleet/dock/api/ShipLayer {
public fun (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()Ljava/lang/ModuleLayer;
public final fun component3 ()Ljava/util/Map;
public final fun copy (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)Lfleet/dock/api/ShipLayer;
public static synthetic fun copy$default (Lfleet/dock/api/ShipLayer;Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipLayer;
public fun equals (Ljava/lang/Object;)Z
public final fun getMeta ()Ljava/util/Map;
public final fun getModuleLayer ()Ljava/lang/ModuleLayer;
public final fun getModules ()Ljava/util/List;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipLayerDescriptor {
public fun (Ljava/util/Set;Ljava/util/Set;)V
public final fun component1 ()Ljava/util/Set;
public final fun component2 ()Ljava/util/Set;
public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lfleet/dock/api/ShipLayerDescriptor;
public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerDescriptor;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerDescriptor;
public fun equals (Ljava/lang/Object;)Z
public final fun getModulePath ()Ljava/util/Set;
public final fun getModules ()Ljava/util/Set;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipLayerSelector {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipLayerSelector;
public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerSelector;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerSelector;
public fun equals (Ljava/lang/Object;)Z
public final fun getSelector ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipParams {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipParams;
public static synthetic fun copy$default (Lfleet/dock/api/ShipParams;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipParams;
public fun equals (Ljava/lang/Object;)Z
public final fun getConfig ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract class fleet/dock/api/ShipProgressEvent {
}
public final class fleet/dock/api/ShipProgressEvent$Finished : fleet/dock/api/ShipProgressEvent {
public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Finished;
}
public final class fleet/dock/api/ShipProgressEvent$Progress : fleet/dock/api/ShipProgressEvent {
public fun (Ljava/lang/String;Ljava/lang/Double;)V
public synthetic fun (Ljava/lang/String;Ljava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/Double;
public final fun copy (Ljava/lang/String;Ljava/lang/Double;)Lfleet/dock/api/ShipProgressEvent$Progress;
public static synthetic fun copy$default (Lfleet/dock/api/ShipProgressEvent$Progress;Ljava/lang/String;Ljava/lang/Double;ILjava/lang/Object;)Lfleet/dock/api/ShipProgressEvent$Progress;
public fun equals (Ljava/lang/Object;)Z
public final fun getFraction ()Ljava/lang/Double;
public final fun getMessage ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipProgressEvent$Started : fleet/dock/api/ShipProgressEvent {
public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Started;
}
public abstract interface class fleet/dock/api/ShipProgressReporting : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/ShipProgressReporting$Companion;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun report (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipProgressEvent;)V
}
public final class fleet/dock/api/ShipProgressReporting$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/ShipProgressReporting$DefaultImpls {
public static fun fold (Lfleet/dock/api/ShipProgressReporting;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public abstract interface class fleet/dock/api/ShipScope {
public abstract fun getShips ()Lkotlinx/coroutines/flow/StateFlow;
public abstract fun launchShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;)Lfleet/dock/api/ShipHandle;
public abstract fun restartShip (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipDescriptor;)Lfleet/dock/api/ShipHandle;
}
public final class fleet/dock/api/ShipSignature {
public fun ([B)V
public final fun component1 ()[B
public final fun copy ([B)Lfleet/dock/api/ShipSignature;
public static synthetic fun copy$default (Lfleet/dock/api/ShipSignature;[BILjava/lang/Object;)Lfleet/dock/api/ShipSignature;
public fun equals (Ljava/lang/Object;)Z
public final fun getBytes ()[B
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipState {
public fun (Ljava/util/Map;)V
public final fun component1 ()Ljava/util/Map;
public final fun copy (Ljava/util/Map;)Lfleet/dock/api/ShipState;
public static synthetic fun copy$default (Lfleet/dock/api/ShipState;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipState;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Ljava/util/Map;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipType {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipType;
public static synthetic fun copy$default (Lfleet/dock/api/ShipType;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipType;
public fun equals (Ljava/lang/Object;)Z
public final fun getType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class fleet/dock/api/ShipVersion {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipVersion;
public static synthetic fun copy$default (Lfleet/dock/api/ShipVersion;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipVersion;
public fun equals (Ljava/lang/Object;)Z
public final fun getVersion ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public abstract interface class fleet/dock/api/SpaceTokensStorage : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lfleet/dock/api/SpaceTokensStorage$Companion;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public abstract fun getSpaceToken (Ljava/lang/String;)Ljava/lang/String;
public abstract fun getTokens ()Lkotlinx/coroutines/flow/StateFlow;
public abstract fun putSpaceToken (Ljava/lang/String;Ljava/lang/String;)V
public abstract fun removeSpaceToken (Ljava/lang/String;)V
}
public final class fleet/dock/api/SpaceTokensStorage$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class fleet/dock/api/SpaceTokensStorage$DefaultImpls {
public static fun fold (Lfleet/dock/api/SpaceTokensStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static fun get (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public static fun minusKey (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public static fun plus (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class fleet/dock/api/ThemeId {
public fun (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ThemeId;
public static synthetic fun copy$default (Lfleet/dock/api/ThemeId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ThemeId;
public fun equals (Ljava/lang/Object;)Z
public final fun getIdent ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
\ No newline at end of file
diff --git a/fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt b/fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt
new file mode 100644
index 000000000000..bb7c60148eab
--- /dev/null
+++ b/fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt
@@ -0,0 +1,514 @@
+public abstract interface class fleet/dock/api/DockApi : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockApi$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getRecentShips ()Lfleet/dock/api/RecentShips;
+ public abstract fun getShipScope ()Lfleet/dock/api/ShipScope;
+ public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V
+ public static synthetic fun perform$default (Lfleet/dock/api/DockApi;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V
+ public abstract fun withShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/DockApi$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockApi$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockApi;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/DockContextElement : kotlin/coroutines/CoroutineContext$Element {
+ public fun (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)V
+ public final fun component1 ()Lfleet/dock/api/DockContextElement$Key;
+ public final fun component2 ()Ljava/lang/Object;
+ public final fun copy (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)Lfleet/dock/api/DockContextElement;
+ public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement;Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public fun getKey ()Lfleet/dock/api/DockContextElement$Key;
+ public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public final fun getValue ()Ljava/lang/Object;
+ public fun hashCode ()I
+ public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/DockContextElement$Key : kotlin/coroutines/CoroutineContext$Key {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockContextElement$Key;
+ public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement$Key;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/DockExit : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockExit$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun quit ()V
+}
+
+public final class fleet/dock/api/DockExit$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockExit$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockExit;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public abstract interface class fleet/dock/api/DockIdentity : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockIdentity$Companion;
+ public abstract fun getHostId ()Ljava/lang/String;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getUserId ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/DockIdentity$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockIdentity$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockIdentity;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/DockPath {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockPath;
+ public static synthetic fun copy$default (Lfleet/dock/api/DockPath;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockPath;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/DockPaths : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockPaths$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getPath (Lfleet/dock/api/DockPath;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/DockPaths$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockPaths$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockPaths;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public abstract interface class fleet/dock/api/LocalStorage : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/LocalStorage$Companion;
+ public abstract fun get (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer;
+ public fun getGlobal ()Lfleet/dock/api/LocalStorage;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun put (Lfleet/dock/api/LocalStorageKey;Ljava/nio/ByteBuffer;)V
+ public abstract fun remove (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer;
+}
+
+public final class fleet/dock/api/LocalStorage$Companion : kotlin/coroutines/CoroutineContext$Key {
+ public final fun withPrefix (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/LocalStorage$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/LocalStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/LocalStorageKey {
+ public fun (Ljava/lang/String;Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/LocalStorageKey;
+ public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageKey;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public final fun getVersion ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/LocalStorageKt {
+ public static final fun getString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;)Ljava/lang/String;
+ public static final fun putString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;)V
+}
+
+public final class fleet/dock/api/LocalStorageState : kotlin/coroutines/CoroutineContext$Element {
+ public fun (Lfleet/dock/api/LocalStorageState$Key;Lkotlinx/coroutines/flow/MutableStateFlow;)V
+ public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public fun getKey ()Lfleet/dock/api/LocalStorageState$Key;
+ public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public final fun getState ()Lkotlinx/coroutines/flow/MutableStateFlow;
+ public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/LocalStorageState$Key : kotlin/coroutines/CoroutineContext$Key {
+ public fun (Lfleet/dock/api/LocalStorageKey;)V
+ public final fun component1 ()Lfleet/dock/api/LocalStorageKey;
+ public final fun copy (Lfleet/dock/api/LocalStorageKey;)Lfleet/dock/api/LocalStorageState$Key;
+ public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageState$Key;Lfleet/dock/api/LocalStorageKey;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageState$Key;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Lfleet/dock/api/LocalStorageKey;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/RecentShipInfo {
+ public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V
+ public final fun component1 ()Lfleet/dock/api/ShipType;
+ public final fun component2 ()Lfleet/dock/api/ShipParams;
+ public final fun component3 ()Lfleet/dock/api/ShipState;
+ public final fun component4 ()Lfleet/dock/api/ShipDescriptor;
+ public final fun component5 ()Ljava/util/Map;
+ public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/RecentShipInfo;
+ public static synthetic fun copy$default (Lfleet/dock/api/RecentShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RecentShipInfo;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
+ public final fun getShipParams ()Lfleet/dock/api/ShipParams;
+ public final fun getShipState ()Lfleet/dock/api/ShipState;
+ public final fun getShipType ()Lfleet/dock/api/ShipType;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/RecentShips {
+ public abstract fun forget (Lfleet/dock/api/ShipId;)V
+ public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow;
+}
+
+public final class fleet/dock/api/RunningShipInfo {
+ public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)V
+ public final fun component1 ()Lfleet/dock/api/ShipType;
+ public final fun component2 ()Lfleet/dock/api/ShipParams;
+ public final fun component3 ()Lfleet/dock/api/ShipDescriptor;
+ public final fun component4 ()Lfleet/dock/api/ShipHandle;
+ public final fun component5 ()Ljava/util/Map;
+ public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)Lfleet/dock/api/RunningShipInfo;
+ public static synthetic fun copy$default (Lfleet/dock/api/RunningShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RunningShipInfo;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getHandle ()Lfleet/dock/api/ShipHandle;
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
+ public final fun getShipParams ()Lfleet/dock/api/ShipParams;
+ public final fun getShipType ()Lfleet/dock/api/ShipType;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/SelectedThemeState : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/SelectedThemeState$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getTheme ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun setTheme (Lfleet/dock/api/ThemeId;)V
+}
+
+public final class fleet/dock/api/SelectedThemeState$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/SelectedThemeState$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/SelectedThemeState;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/SelectedThemeStateKt {
+ public static final fun getCurrent (Lfleet/dock/api/SelectedThemeState;)Lfleet/dock/api/ThemeId;
+}
+
+public abstract interface class fleet/dock/api/Ship {
+ public abstract fun getMeta ()Ljava/util/Map;
+ public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V
+ public static synthetic fun perform$default (Lfleet/dock/api/Ship;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V
+}
+
+public final class fleet/dock/api/ShipBundle {
+ public fun (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V
+ public final fun component1 ()Ljava/lang/ModuleLayer;
+ public final fun component2 ()Ljava/util/Map;
+ public final fun component3 ()Lfleet/dock/api/ShipDescriptor;
+ public final fun component4 ()Ljava/util/Map;
+ public final fun copy (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/ShipBundle;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipBundle;Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipBundle;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getLayers ()Ljava/util/Map;
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getModuleLayer ()Ljava/lang/ModuleLayer;
+ public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipDescriptor {
+ public fun (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Lfleet/dock/api/ShipVersion;
+ public final fun component3 ()Ljava/util/Map;
+ public final fun component4 ()Lfleet/dock/api/ShipSignature;
+ public final fun component5 ()Ljava/util/Map;
+ public final fun copy (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)Lfleet/dock/api/ShipDescriptor;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipDescriptor;Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipDescriptor;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getLayers ()Ljava/util/Map;
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getName ()Ljava/lang/String;
+ public final fun getSignature ()Lfleet/dock/api/ShipSignature;
+ public final fun getVersion ()Lfleet/dock/api/ShipVersion;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/ShipHandle {
+ public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun shipIfCompleted ()Lfleet/dock/api/Ship;
+ public abstract fun shutdown ()V
+}
+
+public final class fleet/dock/api/ShipId {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipId;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipId;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getId ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract class fleet/dock/api/ShipJarCoordinates {
+}
+
+public final class fleet/dock/api/ShipJarCoordinates$Local : fleet/dock/api/ShipJarCoordinates {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Local;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Local;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Local;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getPath ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipJarCoordinates$Remote : fleet/dock/api/ShipJarCoordinates {
+ public fun (Ljava/lang/String;Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Remote;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Remote;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Remote;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getHash ()Ljava/lang/String;
+ public final fun getUrl ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipJarFile {
+ public fun (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)V
+ public final fun component1 ()Lfleet/dock/api/ShipJarCoordinates;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun copy (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)Lfleet/dock/api/ShipJarFile;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipJarFile;Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarFile;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getCoordinates ()Lfleet/dock/api/ShipJarCoordinates;
+ public final fun getSerializedModuleDescriptor ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/ShipLauncher {
+ public abstract fun withShip (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class fleet/dock/api/ShipLauncherProvider {
+ public abstract fun withShipLauncher (Lfleet/dock/api/ShipBundle;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/ShipLayer {
+ public fun (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)V
+ public final fun component1 ()Ljava/util/List;
+ public final fun component2 ()Ljava/lang/ModuleLayer;
+ public final fun component3 ()Ljava/util/Map;
+ public final fun copy (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)Lfleet/dock/api/ShipLayer;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipLayer;Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipLayer;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getModuleLayer ()Ljava/lang/ModuleLayer;
+ public final fun getModules ()Ljava/util/List;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipLayerDescriptor {
+ public fun (Ljava/util/Set;Ljava/util/Set;)V
+ public final fun component1 ()Ljava/util/Set;
+ public final fun component2 ()Ljava/util/Set;
+ public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lfleet/dock/api/ShipLayerDescriptor;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerDescriptor;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerDescriptor;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getModulePath ()Ljava/util/Set;
+ public final fun getModules ()Ljava/util/Set;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipLayerSelector {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipLayerSelector;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerSelector;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerSelector;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getSelector ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipParams {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipParams;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipParams;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipParams;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getConfig ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract class fleet/dock/api/ShipProgressEvent {
+}
+
+public final class fleet/dock/api/ShipProgressEvent$Finished : fleet/dock/api/ShipProgressEvent {
+ public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Finished;
+}
+
+public final class fleet/dock/api/ShipProgressEvent$Progress : fleet/dock/api/ShipProgressEvent {
+ public fun (Ljava/lang/String;Ljava/lang/Double;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/Double;
+ public final fun copy (Ljava/lang/String;Ljava/lang/Double;)Lfleet/dock/api/ShipProgressEvent$Progress;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipProgressEvent$Progress;Ljava/lang/String;Ljava/lang/Double;ILjava/lang/Object;)Lfleet/dock/api/ShipProgressEvent$Progress;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getFraction ()Ljava/lang/Double;
+ public final fun getMessage ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipProgressEvent$Started : fleet/dock/api/ShipProgressEvent {
+ public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Started;
+}
+
+public abstract interface class fleet/dock/api/ShipProgressReporting : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/ShipProgressReporting$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun report (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipProgressEvent;)V
+}
+
+public final class fleet/dock/api/ShipProgressReporting$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/ShipProgressReporting$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/ShipProgressReporting;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public abstract interface class fleet/dock/api/ShipScope {
+ public abstract fun getShips ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun launchShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;)Lfleet/dock/api/ShipHandle;
+ public abstract fun restartShip (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipDescriptor;)Lfleet/dock/api/ShipHandle;
+}
+
+public final class fleet/dock/api/ShipSignature {
+ public fun ([B)V
+ public final fun component1 ()[B
+ public final fun copy ([B)Lfleet/dock/api/ShipSignature;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipSignature;[BILjava/lang/Object;)Lfleet/dock/api/ShipSignature;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getBytes ()[B
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipState {
+ public fun (Ljava/util/Map;)V
+ public final fun component1 ()Ljava/util/Map;
+ public final fun copy (Ljava/util/Map;)Lfleet/dock/api/ShipState;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipState;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipState;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getData ()Ljava/util/Map;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipType {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipType;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipType;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipType;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getType ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipVersion {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipVersion;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipVersion;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipVersion;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getVersion ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/SpaceTokensStorage : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/SpaceTokensStorage$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getSpaceToken (Ljava/lang/String;)Ljava/lang/String;
+ public abstract fun getTokens ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun putSpaceToken (Ljava/lang/String;Ljava/lang/String;)V
+ public abstract fun removeSpaceToken (Ljava/lang/String;)V
+}
+
+public final class fleet/dock/api/SpaceTokensStorage$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/SpaceTokensStorage$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/SpaceTokensStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/ThemeId {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ThemeId;
+ public static synthetic fun copy$default (Lfleet/dock/api/ThemeId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ThemeId;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getIdent ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
diff --git a/fleet/build/fs/resourcesJvmTest/example-abi-lf.txt b/fleet/build/fs/resourcesJvmTest/example-abi-lf.txt
new file mode 100644
index 000000000000..bb7c60148eab
--- /dev/null
+++ b/fleet/build/fs/resourcesJvmTest/example-abi-lf.txt
@@ -0,0 +1,514 @@
+public abstract interface class fleet/dock/api/DockApi : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockApi$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getRecentShips ()Lfleet/dock/api/RecentShips;
+ public abstract fun getShipScope ()Lfleet/dock/api/ShipScope;
+ public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V
+ public static synthetic fun perform$default (Lfleet/dock/api/DockApi;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V
+ public abstract fun withShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/DockApi$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockApi$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockApi;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/DockContextElement : kotlin/coroutines/CoroutineContext$Element {
+ public fun (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)V
+ public final fun component1 ()Lfleet/dock/api/DockContextElement$Key;
+ public final fun component2 ()Ljava/lang/Object;
+ public final fun copy (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)Lfleet/dock/api/DockContextElement;
+ public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement;Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public fun getKey ()Lfleet/dock/api/DockContextElement$Key;
+ public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public final fun getValue ()Ljava/lang/Object;
+ public fun hashCode ()I
+ public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/DockContextElement$Key : kotlin/coroutines/CoroutineContext$Key {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockContextElement$Key;
+ public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement$Key;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/DockExit : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockExit$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun quit ()V
+}
+
+public final class fleet/dock/api/DockExit$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockExit$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockExit;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public abstract interface class fleet/dock/api/DockIdentity : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockIdentity$Companion;
+ public abstract fun getHostId ()Ljava/lang/String;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getUserId ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/DockIdentity$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockIdentity$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockIdentity;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/DockPath {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockPath;
+ public static synthetic fun copy$default (Lfleet/dock/api/DockPath;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockPath;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/DockPaths : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/DockPaths$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getPath (Lfleet/dock/api/DockPath;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/DockPaths$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/DockPaths$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/DockPaths;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public abstract interface class fleet/dock/api/LocalStorage : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/LocalStorage$Companion;
+ public abstract fun get (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer;
+ public fun getGlobal ()Lfleet/dock/api/LocalStorage;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun put (Lfleet/dock/api/LocalStorageKey;Ljava/nio/ByteBuffer;)V
+ public abstract fun remove (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer;
+}
+
+public final class fleet/dock/api/LocalStorage$Companion : kotlin/coroutines/CoroutineContext$Key {
+ public final fun withPrefix (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/LocalStorage$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/LocalStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/LocalStorageKey {
+ public fun (Ljava/lang/String;Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/LocalStorageKey;
+ public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageKey;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public final fun getVersion ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/LocalStorageKt {
+ public static final fun getString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;)Ljava/lang/String;
+ public static final fun putString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;)V
+}
+
+public final class fleet/dock/api/LocalStorageState : kotlin/coroutines/CoroutineContext$Element {
+ public fun (Lfleet/dock/api/LocalStorageState$Key;Lkotlinx/coroutines/flow/MutableStateFlow;)V
+ public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public fun getKey ()Lfleet/dock/api/LocalStorageState$Key;
+ public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public final fun getState ()Lkotlinx/coroutines/flow/MutableStateFlow;
+ public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/LocalStorageState$Key : kotlin/coroutines/CoroutineContext$Key {
+ public fun (Lfleet/dock/api/LocalStorageKey;)V
+ public final fun component1 ()Lfleet/dock/api/LocalStorageKey;
+ public final fun copy (Lfleet/dock/api/LocalStorageKey;)Lfleet/dock/api/LocalStorageState$Key;
+ public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageState$Key;Lfleet/dock/api/LocalStorageKey;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageState$Key;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Lfleet/dock/api/LocalStorageKey;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/RecentShipInfo {
+ public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V
+ public final fun component1 ()Lfleet/dock/api/ShipType;
+ public final fun component2 ()Lfleet/dock/api/ShipParams;
+ public final fun component3 ()Lfleet/dock/api/ShipState;
+ public final fun component4 ()Lfleet/dock/api/ShipDescriptor;
+ public final fun component5 ()Ljava/util/Map;
+ public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/RecentShipInfo;
+ public static synthetic fun copy$default (Lfleet/dock/api/RecentShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RecentShipInfo;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
+ public final fun getShipParams ()Lfleet/dock/api/ShipParams;
+ public final fun getShipState ()Lfleet/dock/api/ShipState;
+ public final fun getShipType ()Lfleet/dock/api/ShipType;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/RecentShips {
+ public abstract fun forget (Lfleet/dock/api/ShipId;)V
+ public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow;
+}
+
+public final class fleet/dock/api/RunningShipInfo {
+ public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)V
+ public final fun component1 ()Lfleet/dock/api/ShipType;
+ public final fun component2 ()Lfleet/dock/api/ShipParams;
+ public final fun component3 ()Lfleet/dock/api/ShipDescriptor;
+ public final fun component4 ()Lfleet/dock/api/ShipHandle;
+ public final fun component5 ()Ljava/util/Map;
+ public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)Lfleet/dock/api/RunningShipInfo;
+ public static synthetic fun copy$default (Lfleet/dock/api/RunningShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RunningShipInfo;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getHandle ()Lfleet/dock/api/ShipHandle;
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
+ public final fun getShipParams ()Lfleet/dock/api/ShipParams;
+ public final fun getShipType ()Lfleet/dock/api/ShipType;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/SelectedThemeState : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/SelectedThemeState$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getTheme ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun setTheme (Lfleet/dock/api/ThemeId;)V
+}
+
+public final class fleet/dock/api/SelectedThemeState$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/SelectedThemeState$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/SelectedThemeState;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/SelectedThemeStateKt {
+ public static final fun getCurrent (Lfleet/dock/api/SelectedThemeState;)Lfleet/dock/api/ThemeId;
+}
+
+public abstract interface class fleet/dock/api/Ship {
+ public abstract fun getMeta ()Ljava/util/Map;
+ public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V
+ public static synthetic fun perform$default (Lfleet/dock/api/Ship;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V
+}
+
+public final class fleet/dock/api/ShipBundle {
+ public fun (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V
+ public final fun component1 ()Ljava/lang/ModuleLayer;
+ public final fun component2 ()Ljava/util/Map;
+ public final fun component3 ()Lfleet/dock/api/ShipDescriptor;
+ public final fun component4 ()Ljava/util/Map;
+ public final fun copy (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/ShipBundle;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipBundle;Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipBundle;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getLayers ()Ljava/util/Map;
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getModuleLayer ()Ljava/lang/ModuleLayer;
+ public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipDescriptor {
+ public fun (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Lfleet/dock/api/ShipVersion;
+ public final fun component3 ()Ljava/util/Map;
+ public final fun component4 ()Lfleet/dock/api/ShipSignature;
+ public final fun component5 ()Ljava/util/Map;
+ public final fun copy (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)Lfleet/dock/api/ShipDescriptor;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipDescriptor;Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipDescriptor;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getLayers ()Ljava/util/Map;
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getName ()Ljava/lang/String;
+ public final fun getSignature ()Lfleet/dock/api/ShipSignature;
+ public final fun getVersion ()Lfleet/dock/api/ShipVersion;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/ShipHandle {
+ public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun shipIfCompleted ()Lfleet/dock/api/Ship;
+ public abstract fun shutdown ()V
+}
+
+public final class fleet/dock/api/ShipId {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipId;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipId;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getId ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract class fleet/dock/api/ShipJarCoordinates {
+}
+
+public final class fleet/dock/api/ShipJarCoordinates$Local : fleet/dock/api/ShipJarCoordinates {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Local;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Local;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Local;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getPath ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipJarCoordinates$Remote : fleet/dock/api/ShipJarCoordinates {
+ public fun (Ljava/lang/String;Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Remote;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Remote;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Remote;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getHash ()Ljava/lang/String;
+ public final fun getUrl ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipJarFile {
+ public fun (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)V
+ public final fun component1 ()Lfleet/dock/api/ShipJarCoordinates;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun copy (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)Lfleet/dock/api/ShipJarFile;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipJarFile;Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarFile;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getCoordinates ()Lfleet/dock/api/ShipJarCoordinates;
+ public final fun getSerializedModuleDescriptor ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/ShipLauncher {
+ public abstract fun withShip (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class fleet/dock/api/ShipLauncherProvider {
+ public abstract fun withShipLauncher (Lfleet/dock/api/ShipBundle;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class fleet/dock/api/ShipLayer {
+ public fun (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)V
+ public final fun component1 ()Ljava/util/List;
+ public final fun component2 ()Ljava/lang/ModuleLayer;
+ public final fun component3 ()Ljava/util/Map;
+ public final fun copy (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)Lfleet/dock/api/ShipLayer;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipLayer;Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipLayer;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMeta ()Ljava/util/Map;
+ public final fun getModuleLayer ()Ljava/lang/ModuleLayer;
+ public final fun getModules ()Ljava/util/List;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipLayerDescriptor {
+ public fun (Ljava/util/Set;Ljava/util/Set;)V
+ public final fun component1 ()Ljava/util/Set;
+ public final fun component2 ()Ljava/util/Set;
+ public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lfleet/dock/api/ShipLayerDescriptor;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerDescriptor;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerDescriptor;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getModulePath ()Ljava/util/Set;
+ public final fun getModules ()Ljava/util/Set;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipLayerSelector {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipLayerSelector;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerSelector;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerSelector;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getSelector ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipParams {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipParams;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipParams;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipParams;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getConfig ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract class fleet/dock/api/ShipProgressEvent {
+}
+
+public final class fleet/dock/api/ShipProgressEvent$Finished : fleet/dock/api/ShipProgressEvent {
+ public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Finished;
+}
+
+public final class fleet/dock/api/ShipProgressEvent$Progress : fleet/dock/api/ShipProgressEvent {
+ public fun (Ljava/lang/String;Ljava/lang/Double;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/Double;
+ public final fun copy (Ljava/lang/String;Ljava/lang/Double;)Lfleet/dock/api/ShipProgressEvent$Progress;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipProgressEvent$Progress;Ljava/lang/String;Ljava/lang/Double;ILjava/lang/Object;)Lfleet/dock/api/ShipProgressEvent$Progress;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getFraction ()Ljava/lang/Double;
+ public final fun getMessage ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipProgressEvent$Started : fleet/dock/api/ShipProgressEvent {
+ public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Started;
+}
+
+public abstract interface class fleet/dock/api/ShipProgressReporting : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/ShipProgressReporting$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun report (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipProgressEvent;)V
+}
+
+public final class fleet/dock/api/ShipProgressReporting$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/ShipProgressReporting$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/ShipProgressReporting;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public abstract interface class fleet/dock/api/ShipScope {
+ public abstract fun getShips ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun launchShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;)Lfleet/dock/api/ShipHandle;
+ public abstract fun restartShip (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipDescriptor;)Lfleet/dock/api/ShipHandle;
+}
+
+public final class fleet/dock/api/ShipSignature {
+ public fun ([B)V
+ public final fun component1 ()[B
+ public final fun copy ([B)Lfleet/dock/api/ShipSignature;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipSignature;[BILjava/lang/Object;)Lfleet/dock/api/ShipSignature;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getBytes ()[B
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipState {
+ public fun (Ljava/util/Map;)V
+ public final fun component1 ()Ljava/util/Map;
+ public final fun copy (Ljava/util/Map;)Lfleet/dock/api/ShipState;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipState;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipState;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getData ()Ljava/util/Map;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipType {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipType;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipType;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipType;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getType ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class fleet/dock/api/ShipVersion {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipVersion;
+ public static synthetic fun copy$default (Lfleet/dock/api/ShipVersion;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipVersion;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getVersion ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class fleet/dock/api/SpaceTokensStorage : kotlin/coroutines/CoroutineContext$Element {
+ public static final field Companion Lfleet/dock/api/SpaceTokensStorage$Companion;
+ public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
+ public abstract fun getSpaceToken (Ljava/lang/String;)Ljava/lang/String;
+ public abstract fun getTokens ()Lkotlinx/coroutines/flow/StateFlow;
+ public abstract fun putSpaceToken (Ljava/lang/String;Ljava/lang/String;)V
+ public abstract fun removeSpaceToken (Ljava/lang/String;)V
+}
+
+public final class fleet/dock/api/SpaceTokensStorage$Companion : kotlin/coroutines/CoroutineContext$Key {
+}
+
+public final class fleet/dock/api/SpaceTokensStorage$DefaultImpls {
+ public static fun fold (Lfleet/dock/api/SpaceTokensStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
+ public static fun get (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
+ public static fun minusKey (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
+ public static fun plus (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
+}
+
+public final class fleet/dock/api/ThemeId {
+ public fun (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ThemeId;
+ public static synthetic fun copy$default (Lfleet/dock/api/ThemeId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ThemeId;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getIdent ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
diff --git a/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt
new file mode 100644
index 000000000000..34f73db2d395
--- /dev/null
+++ b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt
@@ -0,0 +1,533 @@
+package fleet.buildtool.fs
+
+import org.apache.commons.compress.archivers.ArchiveEntry
+import org.apache.commons.compress.archivers.ArchiveOutputStream
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
+import org.apache.commons.compress.archivers.tar.TarConstants.LF_SYMLINK
+import org.apache.commons.compress.archivers.zip.UnixStat
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
+import org.apache.commons.compress.compressors.CompressorStreamFactory
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream
+import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
+import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream
+import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream
+import org.slf4j.Logger
+import java.io.InputStream
+import java.nio.file.Files
+import java.nio.file.LinkOption
+import java.nio.file.Path
+import java.nio.file.attribute.PosixFileAttributeView
+import java.nio.file.attribute.PosixFilePermission
+import java.util.zip.*
+import kotlin.io.path.*
+import kotlin.io.path.exists
+import kotlin.io.path.outputStream
+import kotlin.io.path.pathString
+
+/**
+ * Like [org.gradle.kotlin.dsl.support.zipTo] but reproducible
+ */
+fun zip(targetZipFile: Path, directory: Path, withTopLevelFolder: Boolean) {
+ val filesToZip = directory.toFile()
+ .walkTopDown()
+ .filter { it.isFile }
+ .map { file ->
+ val relativePath = when (withTopLevelFolder) {
+ true -> file.toPath().relativeTo(directory.parent)
+ false -> file.toPath().relativeTo(directory)
+ }.invariantSeparatorsPathString // reproducible ZIP entry names regardless of the platform
+ relativePath to file.inputStream()
+ }
+ zip(targetZipFile, filesToZip)
+}
+
+fun zip(targetZipFile: Path, filesToZip: Sequence>) {
+ ZipOutputStream(targetZipFile.outputStream()).use { zipOut ->
+ filesToZip
+ .sortedBy { (path, _) -> path } // reproducibile order of file in the archive
+ .forEach { (path, content) ->
+ zipOut.putNextEntry(ZipEntry(path).apply {
+ time = 0 // reproducible timestamp
+ })
+ content.use { it.copyTo(zipOut) }
+ zipOut.closeEntry()
+ }
+ }
+}
+
+fun gz(targetGzFile: Path, fileToPack: Path) {
+ fileToPack.inputStream().use { content ->
+ GZIPOutputStream(targetGzFile.outputStream()).use { gzOut ->
+ content.copyTo(gzOut)
+ }
+ }
+}
+
+/**
+ * Extracts a ZIP archive to a specified output path. Output files will preserve symlinks and file permissions from an archive.
+ *
+ * Warning: to preserve necessary meta-data, we use [ZipFile] instead of [ZipInputStream] or [ZipArchiveInputStream],
+ * due to limitations of latter ([source](https://stackoverflow.com/questions/20220256/unzip-symlinks-from-a-ziparchiveinputstream-on-android)).
+ * This might result in performance drawbacks. Also, it's not recommended to use this method to extract large archives.
+ * Please use it with caution.
+ */
+fun extractZip(
+ archive: Path,
+ destination: Path,
+ stripTopLevelFolder: Boolean,
+ cleanDestination: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+) = extract(archive, destination, stripTopLevelFolder, cleanDestination, ArchiveType.ZIP, "", temporaryDir, logger)
+
+/**
+ * Extracts a single file from a ZIP archive to a specified output path. The method assumes
+ * that the ZIP archive contains a single file and will log an error if directories or multiple
+ * files do not match the specified criteria.
+ *
+ * @param archive the path to the ZIP archive to be extracted
+ * @param outputFile the path where the extracted file will be written
+ * @param logger the logger instance used to log messages during the extraction process
+ * @param fileMatcher a predicate used to match the specific file to be extracted
+ */
+fun extractSingleFileZip(archive: Path, outputFile: Path, logger: Logger, fileMatcher: (ZipEntry) -> Boolean) {
+ logger.info("Extracting '$archive' to '$outputFile'")
+ // Ensure destination directory exists
+ if (Files.notExists(outputFile.parent)) {
+ Files.createDirectories(outputFile.parent)
+ }
+
+ ZipInputStream(archive.inputStream()).use { zipInputStream ->
+ var entry = zipInputStream.nextEntry
+ if (entry == null) {
+ error("No entry found in '$archive', while expecting at least a single file")
+ }
+ while (entry != null) {
+ if (entry.isDirectory) {
+ error("Directory found in '$archive', while expecting a plain file archive")
+ }
+ if (fileMatcher(entry)) {
+ outputFile.outputStream().use { output ->
+ zipInputStream.copyTo(output)
+ }
+ zipInputStream.closeEntry()
+ break
+ }
+
+ zipInputStream.closeEntry()
+ entry = zipInputStream.nextEntry
+ }
+ }
+}
+
+
+fun extractGz(
+ archive: Path,
+ destinationFile: Path,
+ logger: Logger,
+) {
+ logger.info("Extracting '$archive' to '$destinationFile'")
+
+ if (destinationFile.parent.notExists()) {
+ destinationFile.parent.createDirectories()
+ }
+
+ GZIPInputStream(archive.inputStream()).use { gzipIn ->
+ destinationFile.outputStream().use { out ->
+ gzipIn.copyTo(out)
+ }
+ }
+}
+
+fun extractTarZst(
+ archive: Path,
+ destination: Path,
+ stripTopLevelFolder: Boolean,
+ cleanDestination: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+) = extractCompressedTar(archive, destination, stripTopLevelFolder, cleanDestination, CompressorStreamFactory.ZSTANDARD, temporaryDir, logger)
+
+fun extractTarGz(
+ archive: Path,
+ destination: Path,
+ stripTopLevelFolder: Boolean,
+ cleanDestination: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+ encoding: String? = null,
+) = extractCompressedTar(archive, destination, stripTopLevelFolder, cleanDestination, CompressorStreamFactory.GZIP, temporaryDir, logger, encoding)
+
+fun extractTarXz(
+ archive: Path,
+ destination: Path,
+ stripTopLevelFolder: Boolean,
+ cleanDestination: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+ encoding: String? = null,
+) = extractCompressedTar(archive, destination, stripTopLevelFolder, cleanDestination, CompressorStreamFactory.XZ, temporaryDir, logger, encoding)
+
+
+private fun extractCompressedTar(
+ archive: Path,
+ destination: Path,
+ stripTopLevelFolder: Boolean,
+ cleanDestination: Boolean,
+ compressorName: String,
+ temporaryDir: Path,
+ logger: Logger,
+ encoding: String? = null,
+) = extract(archive, destination, stripTopLevelFolder, cleanDestination, ArchiveType.TAR, compressorName, temporaryDir, logger, encoding)
+
+@OptIn(ExperimentalPathApi::class)
+private fun extract(
+ archive: Path,
+ destination: Path,
+ stripTopLevelFolder: Boolean,
+ cleanDestination: Boolean,
+ archiveType: ArchiveType,
+ compressorName: String,
+ temporaryDir: Path,
+ logger: Logger,
+ encoding: String? = null,
+) {
+ val tmpFolder = temporaryDir.resolve("${archive.fileName}_extracted")
+ tmpFolder.deleteRecursively()
+ tmpFolder.createDirectories()
+ logger.info("Extracting '$archive' to '$tmpFolder'")
+
+ when (archiveType) {
+ ArchiveType.ZIP -> {
+ ZipFile.builder().setPath(archive).get()
+ .use { zipFile ->
+ zipFile.entries.asSequence().forEach { entry ->
+ extractEntry(
+ entry = entry,
+ destination = destination,
+ stripTopLevelFolder = stripTopLevelFolder,
+ isSymbolicLink = entry.isUnixSymlink,
+ unixMode = entry.unixMode,
+ symLink = zipFile.getUnixSymlink(entry),
+ logger = logger,
+ entryInputStreamProducer = { zipFile.getInputStream(entry).buffered() },
+ )
+ }
+ }
+ }
+ ArchiveType.TAR ->
+ archive.inputStream().buffered().use { bufferedInputStream ->
+ when (compressorName) {
+ CompressorStreamFactory.ZSTANDARD -> ZstdCompressorInputStream(bufferedInputStream)
+ CompressorStreamFactory.XZ -> XZCompressorInputStream(bufferedInputStream)
+ else -> CompressorStreamFactory().createCompressorInputStream(compressorName, bufferedInputStream)
+ }.use { `in` ->
+ TarArchiveInputStream(`in`, encoding).use { archiveInputStream ->
+ archiveInputStream.extractEntriesTo(stripTopLevelFolder, tmpFolder, logger)
+ }
+ }
+ }
+ }
+ logger.info("Extracted '$archive' to '$tmpFolder'")
+ logger.info("Moving '$tmpFolder' to '$destination'")
+ when {
+ cleanDestination -> destination.deleteRecursively()
+ else -> require(!destination.exists() || destination.isDirectory()) { "destination be a directory is it exists" }
+ }
+ destination.parent.createDirectories()
+ tmpFolder.copyToRecursively(destination, followLinks = false, overwrite = cleanDestination)
+ tmpFolder.deleteRecursively()
+ logger.info("Moved '$tmpFolder' to '$destination'")
+}
+
+
+private fun TarArchiveInputStream.extractEntriesTo(stripTopLevelFolder: Boolean, destination: Path, logger: Logger) {
+ val archiveInputStream = this
+ var entry = archiveInputStream.nextEntry
+ while (entry != null) {
+ extractEntry(
+ entry = entry,
+ destination = destination,
+ stripTopLevelFolder = stripTopLevelFolder,
+ isSymbolicLink = entry.isSymbolicLink,
+ symLink = entry.linkName,
+ unixMode = entry.mode,
+ logger = logger
+ ) { archiveInputStream }
+ entry = archiveInputStream.nextEntry
+ }
+}
+
+/**
+ * Extracts a given archive entry to a specified destination path, applying optional transformations
+ * such as stripping the top-level folder, handling symbolic links, and transferring file permissions.
+ */
+private fun extractEntry(
+ entry: ArchiveEntry, destination: Path,
+ stripTopLevelFolder: Boolean,
+ isSymbolicLink: Boolean,
+ symLink: String?,
+ unixMode: Int,
+ logger: Logger,
+ entryInputStreamProducer: () -> InputStream,
+) {
+ val relative = when {
+ stripTopLevelFolder -> entry.name.split("/").drop(1).joinToString("/")
+ else -> entry.name
+ }
+ if (relative != "") {
+ val destinationFile = destination.resolve(relative).normalize()
+ val parent = destinationFile.parent
+ val realParent = when {
+ parent.exists(LinkOption.NOFOLLOW_LINKS) && parent.isSymbolicLink() -> parent.parent.resolve(parent.readSymbolicLink())
+ else -> parent
+ }
+ realParent.createDirectories()
+
+ when {
+ entry.isDirectory -> if (!destinationFile.exists()) {
+ destinationFile.createDirectories()
+ }
+ isSymbolicLink -> {
+ requireNotNull(symLink) { "symLink must not be null when isSymbolicLink is true" }
+ destinationFile.createSymbolicLinkPointingTo(Path.of(symLink))
+ }
+ else -> destinationFile.outputStream().use { fileOut ->
+ entryInputStreamProducer().copyTo(fileOut)
+ }
+ }
+
+ restorePermissions(destinationFile, unixMode, isSymbolicLink, logger)
+ }
+}
+
+private fun restorePermissions(destinationFile: Path, unixMode: Int, isSymbolicLink: Boolean, logger: Logger) {
+ when {
+ isSymbolicLink -> {}
+ unixMode == 0 -> {}
+ else -> try {
+ val attr = destinationFile.fileAttributesView(LinkOption.NOFOLLOW_LINKS)
+ attr.setPermissions(unixMode.toPosixPermissions())
+ }
+ catch (_: UnsupportedOperationException) {
+ logger.debug("Could not restore permissions on {}, file system is not POSIX compliant", destinationFile)
+ }
+ }
+}
+
+/**
+ * Compresses a given [source] folder to an [outputFile] in ZIP format.
+ *
+ * TODO: Make it reproducible by using the methods from `zip(Path, Path, Boolean)` method (Make it optional, not to break backwards compatibility):
+ * 1. Sort entries alphabetically
+ * 2. Use invariant separators
+ * 3. Reset lastModified to 0
+ */
+fun zip(
+ source: Path,
+ outputFile: Path,
+ withTopLevelFolder: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+) = compress(source, outputFile, withTopLevelFolder, ArchiveType.ZIP, compressorName = "", temporaryDir, logger)
+
+fun tarGz(
+ source: Path,
+ outputFile: Path,
+ withTopLevelFolder: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+) = tarWithCompression(source, outputFile, withTopLevelFolder, CompressorStreamFactory.GZIP, temporaryDir, logger)
+
+fun tarZst(
+ source: Path,
+ outputFile: Path,
+ withTopLevelFolder: Boolean,
+ temporaryDir: Path,
+ logger: Logger,
+) = tarWithCompression(source, outputFile, withTopLevelFolder, CompressorStreamFactory.ZSTANDARD, temporaryDir, logger)
+
+private fun tarWithCompression(
+ source: Path,
+ outputFile: Path,
+ withTopLevelFolder: Boolean,
+ compressorName: String,
+ temporaryDir: Path,
+ logger: Logger,
+): Path = compress(source, outputFile, withTopLevelFolder, ArchiveType.TAR, compressorName, temporaryDir, logger)
+
+@OptIn(ExperimentalPathApi::class)
+private fun compress(
+ source: Path,
+ outputFile: Path,
+ withTopLevelFolder: Boolean,
+ archiveType: ArchiveType,
+ compressorName: String,
+ temporaryDir: Path,
+ logger: Logger,
+): Path {
+ val tmpArchive = temporaryDir.resolve("tmp_${outputFile.fileName}")
+ tmpArchive.deleteRecursively()
+ if (temporaryDir.notExists()) {
+ temporaryDir.createDirectories()
+ }
+
+ logger.info("Compressing '$source' to '$tmpArchive'...")
+ tmpArchive.outputStream().buffered().use { bufferedOutputStream ->
+ val compressorStream = when (archiveType) {
+ ArchiveType.ZIP -> bufferedOutputStream // zip uses the default compression method, no need to wrap it
+ ArchiveType.TAR -> when (compressorName) {
+ CompressorStreamFactory.ZSTANDARD -> ZstdCompressorOutputStream(bufferedOutputStream)
+ CompressorStreamFactory.XZ -> XZCompressorOutputStream(bufferedOutputStream)
+ else -> CompressorStreamFactory().createCompressorOutputStream(compressorName, bufferedOutputStream)
+ }
+ }
+
+ compressorStream.use { out ->
+ val archiveStream = when (archiveType) {
+ ArchiveType.ZIP -> ZipArchiveOutputStream(out)
+ ArchiveType.TAR -> TarArchiveOutputStream(out).apply { setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU) }
+ } as ArchiveOutputStream
+
+ archiveStream.use { outputStream ->
+ when {
+ source.isDirectory() -> {
+ val base = when {
+ withTopLevelFolder -> {
+ outputStream.addEntry(source, source.name, archiveType)
+ "${source.name}/"
+ }
+
+ else -> "./"
+ }
+
+ source.walk().forEach { // must not follow symlinks
+ outputStream.addEntry(it, "$base${it.relativeTo(source)}", archiveType)
+ }
+ }
+
+ else -> outputStream.addEntry(source, source.name, archiveType = archiveType)
+ }
+ }
+ }
+ }
+ logger.info("Compressed '$source' to '$tmpArchive'")
+ logger.info("Moving '$tmpArchive' to '$outputFile'")
+ outputFile.deleteRecursively()
+ outputFile.parent.createDirectories()
+ tmpArchive.moveTo(outputFile)
+ logger.info("Moved '$tmpArchive' to '$outputFile'")
+ return outputFile
+}
+
+private fun ArchiveOutputStream.addEntry(path: Path, relativePathInArchive: String, archiveType: ArchiveType) {
+ when {
+ path.isSymbolicLink() -> {
+ when (archiveType) {
+ ArchiveType.TAR -> {
+ val entry = TarArchiveEntry(relativePathInArchive, LF_SYMLINK).also {
+ it.linkName = path.readSymbolicLink().pathString
+ when (val permissions = path.getFilePermissions(LinkOption.NOFOLLOW_LINKS)) {
+ FilePermissions.Other -> {}
+ is FilePermissions.Posix -> it.mode = permissions.toInt()
+ }
+ }
+ putArchiveEntry(entry)
+ closeArchiveEntry()
+ }
+ ArchiveType.ZIP -> {
+ val link = path.readSymbolicLink().pathString
+ val entry = ZipArchiveEntry(relativePathInArchive).also {
+ when (val permissions = path.getFilePermissions(LinkOption.NOFOLLOW_LINKS)) {
+ FilePermissions.Other -> {}
+ is FilePermissions.Posix -> it.unixMode = UnixStat.LINK_FLAG or permissions.toInt()
+ }
+ it.size = link.toByteArray().size.toLong()
+ }
+ putArchiveEntry(entry)
+ write(link.toByteArray())
+ closeArchiveEntry()
+ }
+ }
+ }
+
+ path.isDirectory() -> {} // this must be checked AFTER `path.isSymbolicLink()`, otherwise symlink pointing to directory will be ignored
+
+ else -> path.inputStream().use { fileIn ->
+ val entry: ArchiveEntry = when (archiveType) {
+ ArchiveType.TAR -> TarArchiveEntry(path, relativePathInArchive).also {
+ when (val permissions = path.getFilePermissions()) {
+ FilePermissions.Other -> {}
+ is FilePermissions.Posix -> it.mode = permissions.toInt()
+ }
+ }
+ ArchiveType.ZIP -> ZipArchiveEntry(path, relativePathInArchive).also {
+ when (val permissions = path.getFilePermissions()) {
+ FilePermissions.Other -> {}
+ is FilePermissions.Posix -> it.unixMode = permissions.toInt()
+ }
+ }
+ }
+ putArchiveEntry(entry)
+ fileIn.copyTo(this)
+ closeArchiveEntry()
+ }
+ }
+}
+
+
+private fun Path.getFilePermissions(vararg options: LinkOption): FilePermissions {
+ return try {
+ FilePermissions.Posix(getPosixFilePermissions(*options))
+ }
+ catch (_: UnsupportedOperationException) {
+ FilePermissions.Other
+ }
+}
+
+internal fun Int.toPosixPermissions(): Set {
+ val perms = mutableSetOf()
+ if (this and 0b100000000 != 0) perms.add(PosixFilePermission.OWNER_READ)
+ if (this and 0b010000000 != 0) perms.add(PosixFilePermission.OWNER_WRITE)
+ if (this and 0b001000000 != 0) perms.add(PosixFilePermission.OWNER_EXECUTE)
+ if (this and 0b000100000 != 0) perms.add(PosixFilePermission.GROUP_READ)
+ if (this and 0b000010000 != 0) perms.add(PosixFilePermission.GROUP_WRITE)
+ if (this and 0b000001000 != 0) perms.add(PosixFilePermission.GROUP_EXECUTE)
+ if (this and 0b000000100 != 0) perms.add(PosixFilePermission.OTHERS_READ)
+ if (this and 0b000000010 != 0) perms.add(PosixFilePermission.OTHERS_WRITE)
+ if (this and 0b000000001 != 0) perms.add(PosixFilePermission.OTHERS_EXECUTE)
+ return perms
+}
+
+internal fun Set.toInt(): Int =
+ map { permission ->
+ when (permission) {
+ PosixFilePermission.OWNER_READ -> 0b100000000
+ PosixFilePermission.OWNER_WRITE -> 0b010000000
+ PosixFilePermission.OWNER_EXECUTE -> 0b001000000
+ PosixFilePermission.GROUP_READ -> 0b000100000
+ PosixFilePermission.GROUP_WRITE -> 0b000010000
+ PosixFilePermission.GROUP_EXECUTE -> 0b000001000
+ PosixFilePermission.OTHERS_READ -> 0b000000100
+ PosixFilePermission.OTHERS_WRITE -> 0b000000010
+ PosixFilePermission.OTHERS_EXECUTE -> 0b000000001
+ }
+ }.sum()
+
+private sealed class FilePermissions {
+ object Other : FilePermissions()
+ data class Posix(val permissions: Set) : FilePermissions() {
+
+ fun toInt() = permissions.toInt()
+ }
+}
+
+private enum class ArchiveType {
+ ZIP,
+ TAR,
+}
\ No newline at end of file
diff --git a/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt
new file mode 100644
index 000000000000..3cccd5cec4c6
--- /dev/null
+++ b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt
@@ -0,0 +1,65 @@
+package fleet.buildtool.fs
+
+import java.io.ByteArrayOutputStream
+import java.nio.ByteBuffer
+import java.nio.charset.Charset
+import java.nio.file.Path
+import java.nio.file.attribute.PosixFilePermission
+import java.security.MessageDigest
+import kotlin.io.path.getPosixFilePermissions
+import kotlin.io.path.readText
+import kotlin.io.path.setPosixFilePermissions
+
+val Path.pathString: String get() = toString()
+
+fun sha256(bytes: ByteArray): String {
+ val md = MessageDigest.getInstance("SHA-256")
+ val digest = md.digest(bytes)
+ return digest.fold(StringBuilder()) { sb, it -> sb.append("%02x".format(it)) }.toString()
+}
+
+/**
+ * Read content of file at [this] path and normalize it for reliable cross-platform SHA256 hashing
+ *
+ * Line endings will be normalized to '\n' to ensure the hash result produces identical result on Windows and UNIX.
+ */
+fun Path.readBytesForSha256(charset: Charset = Charsets.UTF_8): ByteArray = readText(charset).normaliseLineSeparators().toByteArray(charset)
+
+private fun String.normaliseLineSeparators() = lineSequence().joinToString("\n")
+
+/**
+ * Set POSIX file permissions of this [Path] to ensure the output is the same on all platforms.
+ *
+ * After zip extraction, execute permissions are lost on Windows which creates a different directory output.
+ *
+ * @this [Path] an extracted path from a ZIP archive, which extraction is intended to be done on all platforms reproducibly.
+ */
+fun Path.makeReproducibleAfterCrossOSExtraction() {
+ try {
+ val originalPermissions = getPosixFilePermissions()
+ val newPermissions = originalPermissions.minus(setOf(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_EXECUTE))
+ setPosixFilePermissions(newPermissions)
+ } catch (_: UnsupportedOperationException) {
+ // do nothing
+ }
+}
+
+/**
+ * Inefficiently normalize '\r\n' and '\r' to '\n'.
+ */
+fun ByteBuffer.normaliseLineSeparators(): ByteBuffer =
+ ByteBuffer.wrap(ByteArray(remaining()).also { get(it) }.normaliseLineSeparators())
+
+private fun ByteArray.normaliseLineSeparators(): ByteArray {
+ val result = ByteArrayOutputStream(size)
+ var i = 0
+ while (i < size) {
+ when {
+ i + 1 < size && this[i] == '\r'.code.toByte() && this[i + 1] == '\n'.code.toByte() -> {}
+ this[i] == '\r'.code.toByte() -> result.write('\n'.code)
+ else -> result.write(this[i].toInt())
+ }
+ i++
+ }
+ return result.toByteArray()
+}
diff --git a/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt
new file mode 100644
index 000000000000..dc63a541518d
--- /dev/null
+++ b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt
@@ -0,0 +1,1162 @@
+package fleet.buildtool.fs
+
+import org.junit.Assume.assumeTrue
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.io.ByteArrayInputStream
+import java.io.IOException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+import java.nio.file.attribute.PosixFilePermission
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import java.util.zip.ZipOutputStream
+import kotlin.io.path.*
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlin.test.assertContains
+import kotlin.test.assertEquals
+import kotlin.test.assertFails
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class ArchiveUtilsTest {
+
+ private val logger: Logger = LoggerFactory.getLogger(ArchiveUtilsTest::class.java)
+
+ private lateinit var tempDir: Path
+
+ @BeforeTest
+ fun setUp() {
+ tempDir = Files.createTempDirectory("archive-utils-test")
+ }
+
+ @OptIn(ExperimentalPathApi::class)
+ @AfterTest
+ fun tearDown() {
+ tempDir.deleteRecursively()
+ }
+
+ // ========== Zip Tests ==========
+
+ @Test
+ fun `should create zip archive from directory without top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+ sourceDir.resolve("file2.txt").writeText("content2")
+ val subDir = sourceDir.resolve("subdir").createDirectories()
+ subDir.resolve("file3.txt").writeText("content3")
+
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ assertTrue(targetZip.exists())
+ val entriesMap = readZipEntries(targetZip)
+ assertEquals(3, entriesMap.size)
+ assertEquals("content1", entriesMap["file1.txt"])
+ assertEquals("content2", entriesMap["file2.txt"])
+ assertEquals("content3", entriesMap["subdir/file3.txt"])
+ }
+
+ @Test
+ fun `should create zip archive from directory with top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+ val subDir = sourceDir.resolve("subdir").createDirectories()
+ subDir.resolve("file2.txt").writeText("content2")
+
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = true)
+
+ // Then
+ assertTrue(targetZip.exists())
+ val entriesMap = readZipEntries(targetZip)
+ assertEquals(2, entriesMap.size)
+ assertEquals("content1", entriesMap["source/file1.txt"])
+ assertEquals("content2", entriesMap["source/subdir/file2.txt"])
+ }
+
+ @Test
+ fun `should create zip with sorted entries for reproducibility`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("zebra.txt").writeText("z")
+ sourceDir.resolve("alpha.txt").writeText("a")
+ sourceDir.resolve("beta.txt").writeText("b")
+
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ val entryNames = readZipEntryNames(targetZip)
+ assertEquals(listOf("alpha.txt", "beta.txt", "zebra.txt"), entryNames)
+ }
+
+ @Test
+ fun `should create zip with timestamp set to zero for reproducibility`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file.txt").writeText("content")
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ ZipInputStream(targetZip.inputStream()).use { zipIn ->
+ val entry = zipIn.nextEntry
+ assertEquals(0L, entry.time)
+ }
+ }
+
+ @Test
+ fun `should create zip from sequence of input streams`() {
+ // Given
+ val filesToZip = sequenceOf(
+ "path/to/file1.txt" to ByteArrayInputStream("content1".toByteArray()),
+ "path/to/file2.txt" to ByteArrayInputStream("content2".toByteArray()),
+ "another/file3.txt" to ByteArrayInputStream("content3".toByteArray())
+ )
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, filesToZip)
+
+ // Then
+ val entriesMap = readZipEntries(targetZip)
+ assertEquals(3, entriesMap.size)
+ assertEquals("content1", entriesMap["path/to/file1.txt"])
+ assertEquals("content2", entriesMap["path/to/file2.txt"])
+ assertEquals("content3", entriesMap["another/file3.txt"])
+ }
+
+ @Test
+ fun `should create empty zip when source directory is empty`() {
+ // Given
+ val sourceDir = tempDir.resolve("empty").createDirectories()
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ assertTrue(targetZip.exists())
+ val entriesMap = readZipEntries(targetZip)
+ assertEquals(0, entriesMap.size)
+ }
+
+ @Test
+ fun `should create zip from a single file without top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source.txt").apply { writeText("content") }
+ val tmpDir = tempDir.resolve("tmp")
+ val outputFile = tempDir.resolve("output.zip")
+
+ val extractDir = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ zip(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ extractZip(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(extractDir.resolve("source.txt").exists())
+ assertEquals(extractDir.resolve("source.txt").readText(), "content")
+ }
+
+ @Test
+ fun `should use invariant path separators for reproducibility`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ val subDir = sourceDir.resolve("sub1").resolve("sub2").createDirectories()
+ subDir.resolve("file.txt").writeText("content")
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ val entryNames = readZipEntryNames(targetZip)
+ // Should use forward slashes regardless of platform
+ assertTrue(entryNames.any { it.contains("sub1/sub2/file.txt") })
+ }
+
+ @Test
+ fun `should handle mixed content in zip`() {
+ assumePosixFileSystem()
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+
+ // Regular files
+ sourceDir.resolve("text.txt").writeText("text")
+ sourceDir.resolve("binary.bin").writeBytes(ByteArray(100) { it.toByte() })
+
+ // Directories
+ val nestedDir = sourceDir.resolve("nested/dir").createDirectories()
+ nestedDir.resolve("nested.txt").writeText("nested")
+
+ // Symlinks (if supported)
+ val target = sourceDir.resolve("target.txt").apply { writeText("target") }
+ sourceDir.resolve("link.txt").createSymbolicLinkPointingTo(target)
+
+ val outputFile = tempDir.resolve("output.zip")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ zip(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ val extractDir = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+ extractZip(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ assertTrue(extractDir.resolve("text.txt").exists())
+ assertTrue(extractDir.resolve("binary.bin").exists())
+ assertTrue(extractDir.resolve("nested/dir/nested.txt").exists())
+ assertEquals("text", extractDir.resolve("text.txt").readText())
+ assertEquals("nested", extractDir.resolve("nested/dir/nested.txt").readText())
+ val symlink = extractDir.resolve("link.txt")
+ assertTrue(symlink.exists())
+ assertTrue(symlink.isSymbolicLink())
+ assertEquals(symlink.readSymbolicLink(), target)
+ }
+
+ @Test
+ fun `should extract manually created symlink from zip`() {
+ val extractDir = tempDir.resolve("extracted")
+ val zipFile = getFileFromResources("archive/single_symlink.zip")
+ val tmpDir = tempDir.resolve("tmp")
+
+ extractZip(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ val symlink = extractDir.resolve("link.txt")
+ assertTrue(symlink.exists())
+ assertTrue(symlink.isSymbolicLink())
+ }
+
+ @Test
+ fun `should extract empty folder from assets zip`() {
+ val extractDir = tempDir.resolve("extracted")
+ val zipFile = getFileFromResources("archive/empty_folder.zip")
+ val tmpDir = tempDir.resolve("tmp")
+
+ extractZip(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ assertTrue(extractDir.resolve("archive").exists())
+ }
+
+ @Test
+ fun `should handle mixed content in assets zip`() {
+ // Given
+ val archiveFile = getFileFromResources("archive/mixed.zip")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // Then
+ val extractDir = tempDir.resolve("extracted")
+ extractZip(archiveFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ assertTrue(extractDir.resolve("text.txt").exists())
+ assertTrue(extractDir.resolve("binary.bin").exists())
+ assertTrue(extractDir.resolve("nested/nested.txt").exists())
+ assertEquals("abc", extractDir.resolve("text.txt").readText().trim())
+ assertEquals("nested", extractDir.resolve("nested/nested.txt").readText().trim())
+ val symlink = extractDir.resolve("link.txt")
+ assertTrue(symlink.exists())
+ assertTrue(symlink.isSymbolicLink())
+ }
+
+ // ========== Gzip Tests ==========
+
+ @Test
+ fun `should create gzip archive from single file`() {
+ // Given
+ val sourceFile = tempDir.resolve("source.txt").apply {
+ writeText("This is test content for gzip compression")
+ }
+ val targetGz = tempDir.resolve("output.txt.gz")
+
+ // When
+ gz(targetGz, sourceFile)
+
+ // Then
+ assertTrue(targetGz.exists())
+ assertTrue(targetGz.fileSize() > 0)
+ }
+
+ @Test
+ fun `should compress and decompress file correctly`() {
+ // Given
+ val content = "Test content for round-trip compression"
+ val sourceFile = tempDir.resolve("source.txt").apply { writeText(content) }
+ val gzFile = tempDir.resolve("compressed.gz")
+ val decompressedFile = tempDir.resolve("decompressed.txt")
+
+ // When
+ gz(gzFile, sourceFile)
+ extractGz(gzFile, decompressedFile, logger)
+
+ // Then
+ assertEquals(content, decompressedFile.readText())
+ }
+
+ @Test
+ fun `should handle empty file compression`() {
+ // Given
+ val sourceFile = tempDir.resolve("empty.txt").apply { writeText("") }
+ val targetGz = tempDir.resolve("empty.gz")
+
+ // When
+ gz(targetGz, sourceFile)
+
+ // Then
+ assertTrue(targetGz.exists())
+ assertTrue(targetGz.fileSize() > 0) // GZ header is still present
+ }
+
+ // ========== Extract Zip Tests ==========
+
+ @Test
+ fun `should extract zip archive to destination`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ createTestZip(zipFile, mapOf(
+ "file1.txt" to "content1",
+ "dir/file2.txt" to "content2"
+ ))
+ val destination = tempDir.resolve("extracted")
+
+ // When
+ extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger)
+
+ // Then
+ assertTrue(destination.resolve("file1.txt").exists())
+ assertEquals("content1", destination.resolve("file1.txt").readText())
+ assertTrue(destination.resolve("dir/file2.txt").exists())
+ assertEquals("content2", destination.resolve("dir/file2.txt").readText())
+ }
+
+ @Test
+ fun `should create destination directory if not exists`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ createTestZip(zipFile, mapOf("file.txt" to "content"))
+ val destination = tempDir.resolve("non-existent/extracted")
+
+ // When
+ extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger)
+
+ // Then
+ assertTrue(destination.exists())
+ assertTrue(destination.resolve("file.txt").exists())
+ }
+
+ @Test
+ fun `should extract directories from zip`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ ZipOutputStream(zipFile.outputStream()).use { zipOut ->
+ zipOut.putNextEntry(ZipEntry("emptydir/"))
+ zipOut.closeEntry()
+ zipOut.putNextEntry(ZipEntry("emptydir/file.txt"))
+ zipOut.write("content".toByteArray())
+ zipOut.closeEntry()
+ }
+ val destination = tempDir.resolve("extracted")
+
+ // When
+ extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger)
+
+ // Then
+ assertTrue(destination.resolve("emptydir").isDirectory())
+ assertTrue(destination.resolve("emptydir/file.txt").exists())
+ }
+
+ @Test
+ fun `should handle zip with nested directories`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ createTestZip(zipFile, mapOf(
+ "level1/level2/level3/file.txt" to "deep content"
+ ))
+ val destination = tempDir.resolve("extracted")
+
+ // When
+ extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger)
+
+ // Then
+ assertTrue(destination.resolve("level1/level2/level3/file.txt").exists())
+ assertEquals("deep content", destination.resolve("level1/level2/level3/file.txt").readText())
+ }
+
+ // ========== Extract Single File Zip Tests ==========
+
+ @Test
+ fun `should extract single file matching the predicate`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ createTestZip(zipFile, mapOf(
+ "file1.txt" to "content1",
+ "file2.txt" to "content2",
+ "target.txt" to "target content"
+ ))
+ val outputFile = tempDir.resolve("extracted.txt")
+
+ // When
+ extractSingleFileZip(zipFile, outputFile, logger) { entry ->
+ entry.name == "target.txt"
+ }
+
+ // Then
+ assertTrue(outputFile.exists())
+ assertEquals("target content", outputFile.readText())
+ }
+
+ @Test
+ fun `should create parent directories if not exist`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ createTestZip(zipFile, mapOf("file.txt" to "content"))
+ val outputFile = tempDir.resolve("non-existent/dir/output.txt")
+
+ // When
+ extractSingleFileZip(zipFile, outputFile, logger) { true }
+
+ // Then
+ assertTrue(outputFile.exists())
+ assertEquals("content", outputFile.readText())
+ }
+
+ @Test
+ fun `should throw error when singleZip is empty`() {
+ // Given
+ val zipFile = tempDir.resolve("empty.zip")
+ ZipOutputStream(zipFile.outputStream()).use { }
+ val outputFile = tempDir.resolve("output.txt")
+
+ // When/Then
+ val exception = assertFails {
+ extractSingleFileZip(zipFile, outputFile, logger) { true }
+ }
+ assertContains(exception.message ?: "", "No entry found")
+ }
+
+ @Test
+ fun `should throw error when singleZip contains directory`() {
+ // Given
+ val zipFile = tempDir.resolve("archive.zip")
+ ZipOutputStream(zipFile.outputStream()).use { zipOut ->
+ zipOut.putNextEntry(ZipEntry("dir/"))
+ zipOut.closeEntry()
+ }
+ val outputFile = tempDir.resolve("output.txt")
+
+ // When/Then
+ val exception = assertFails {
+ extractSingleFileZip(zipFile, outputFile, logger) { true }
+ }
+ assertContains(exception.message ?: "", "Directory found")
+ }
+
+ // ========== Extract Gzip Tests ==========
+
+ @Test
+ fun `should extract gzip archive`() {
+ // Given
+ val content = "Test content for extraction"
+ val sourceFile = tempDir.resolve("source.txt").apply { writeText(content) }
+ val gzFile = tempDir.resolve("archive.gz")
+ gz(gzFile, sourceFile)
+ val destination = tempDir.resolve("extracted.txt")
+
+ // When
+ extractGz(gzFile, destination, logger)
+
+ // Then
+ assertTrue(destination.exists())
+ assertEquals(content, destination.readText())
+ }
+
+ @Test
+ fun `should create parent directory if not exists for gzip extraction`() {
+ // Given
+ val sourceFile = tempDir.resolve("source.txt").apply { writeText("content") }
+ val gzFile = tempDir.resolve("archive.gz")
+ gz(gzFile, sourceFile)
+ val destination = tempDir.resolve("non-existent/dir/extracted.txt")
+
+ // When
+ extractGz(gzFile, destination, logger)
+
+ // Then
+ assertTrue(destination.exists())
+ assertEquals("content", destination.readText())
+ }
+
+ // ========== POSIX Permissions Tests ==========
+
+ @Test
+ fun `should convert all possible POSIX permissions`() {
+ assumePosixFileSystem()
+
+ // Given
+ val mode = 0b111111111 // rwxrwxrwx (777)
+
+ // When
+ val permissions = mode.toPosixPermissions()
+
+ // Then
+ assertEquals(9, permissions.size)
+ assertTrue(permissions.containsAll(PosixFilePermission.entries))
+ }
+
+ @Test
+ fun `should convert no permissions`() {
+ assumePosixFileSystem()
+
+ // Given
+ val mode = 0b000000000 // no permissions (000)
+
+ // When
+ val permissions = mode.toPosixPermissions()
+
+ // Then
+ assertEquals(0, permissions.size)
+ }
+
+ @Test
+ fun `should convert POSIX permissions to int correctly`() {
+ assumePosixFileSystem()
+
+ // Given
+ val permissions = setOf(
+ PosixFilePermission.OWNER_READ,
+ PosixFilePermission.OWNER_WRITE,
+ PosixFilePermission.OWNER_EXECUTE,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.GROUP_EXECUTE,
+ PosixFilePermission.OTHERS_READ,
+ PosixFilePermission.OTHERS_EXECUTE
+ )
+
+ // When
+ val mode = permissions.toInt()
+
+ // Then
+ assertEquals(0b111101101, mode) // 755
+ }
+
+ // ========== TarGz Tests ==========
+
+ @Test
+ fun `should create tar gz from directory without top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+ val subDir = sourceDir.resolve("subdir").createDirectories()
+ subDir.resolve("file2.txt").writeText("content2")
+
+ val outputFile = tempDir.resolve("output.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ assertTrue(outputFile.exists())
+ assertTrue(outputFile.fileSize() > 0)
+ }
+
+ @Test
+ fun `should create tar gz from directory with top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+
+ val outputFile = tempDir.resolve("output.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarGz(sourceDir, outputFile, withTopLevelFolder = true, tmpDir, logger)
+
+ // Then
+ assertTrue(outputFile.exists())
+ }
+
+ @Test
+ fun `should create tar gz from single file`() {
+ // Given
+ val sourceFile = tempDir.resolve("file.txt").apply { writeText("content") }
+ val outputFile = tempDir.resolve("output.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarGz(sourceFile, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ assertTrue(outputFile.exists())
+ }
+
+ @Test
+ fun `should return output file path from tarGz`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file.txt").writeText("content")
+ val outputFile = tempDir.resolve("output.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ val result = tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ assertEquals(outputFile, result)
+ }
+
+
+ @Test
+ fun `should handle symlink with spaces in name`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ val targetFile = sourceDir.resolve("target file.txt").apply { writeText("content") }
+ val symlink = sourceDir.resolve("link file.txt")
+
+ symlink.createSymbolicLinkPointingTo(targetFile)
+
+ val outputFile = tempDir.resolve("output.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ val extractDir = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+ extractTarGz(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ val extractedLink = extractDir.resolve("link file.txt")
+ assertTrue(extractedLink.exists())
+ assertTrue(extractedLink.isSymbolicLink())
+ }
+
+ // ========== TarZst Tests ==========
+
+ @Test
+ fun `should create tar zst from directory without top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+
+ val outputFile = tempDir.resolve("output.tar.zst")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarZst(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ assertTrue(outputFile.exists())
+ assertTrue(outputFile.fileSize() > 0)
+ }
+
+ @Test
+ fun `should create tar zst from directory with top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+
+ val outputFile = tempDir.resolve("output.tar.zst")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarZst(sourceDir, outputFile, withTopLevelFolder = true, tmpDir, logger)
+
+ // Then
+ assertTrue(outputFile.exists())
+ }
+
+ @Test
+ fun `should create tar zst from a single file without top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source.txt").apply { writeText("content") }
+ val tmpDir = tempDir.resolve("tmp")
+ val outputFile = tempDir.resolve("output.tar.zst")
+
+ val extractDir = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ tarZst(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ extractTarZst(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(extractDir.resolve("source.txt").exists())
+ assertEquals(extractDir.resolve("source.txt").readText(), "content")
+ }
+
+ // ========== ExtractTarGz Tests ==========
+
+ @Test
+ fun `should extract tar gz without stripping top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file.txt").writeText("content")
+ val tarGz = tempDir.resolve("archive.tar.gz")
+ val tmpDir = tempDir.resolve("tmp1")
+ tarGz(sourceDir, tarGz, withTopLevelFolder = true, tmpDir, logger)
+
+ val destination = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ extractTarGz(tarGz, destination, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(destination.resolve("source/file.txt").exists())
+ assertEquals("content", destination.resolve("source/file.txt").readText())
+ }
+
+ @Test
+ fun `should extract tar gz with stripping top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file.txt").writeText("content")
+ val tarGz = tempDir.resolve("archive.tar.gz")
+ val tmpDir = tempDir.resolve("tmp1")
+ tarGz(sourceDir, tarGz, withTopLevelFolder = true, tmpDir, logger)
+
+ val destination = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ extractTarGz(tarGz, destination, stripTopLevelFolder = true, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(destination.resolve("file.txt").exists())
+ assertEquals("content", destination.resolve("file.txt").readText())
+ }
+
+ @Test
+ fun `should clean destination when cleanDestination is true`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("new.txt").writeText("new")
+ val tarGz = tempDir.resolve("archive.tar.gz")
+ val tmpDir = tempDir.resolve("tmp1")
+ tarGz(sourceDir, tarGz, withTopLevelFolder = false, tmpDir, logger)
+
+ val destination = tempDir.resolve("extracted").createDirectories()
+ destination.resolve("old.txt").writeText("old")
+
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ extractTarGz(tarGz, destination, stripTopLevelFolder = false, cleanDestination = true, extractTmpDir, logger)
+
+ // Then
+ assertTrue(destination.resolve("new.txt").exists())
+ assertFalse(destination.resolve("old.txt").exists())
+ }
+
+ @Test
+ fun `should preserve file structure with nested directories in tar gz`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ val nested = sourceDir.resolve("level1/level2").createDirectories()
+ nested.resolve("deep.txt").writeText("deep content")
+ val tarGz = tempDir.resolve("single_symlink.tar.gz")
+ val tmpDir = tempDir.resolve("tmp1")
+ tarGz(sourceDir, tarGz, withTopLevelFolder = false, tmpDir, logger)
+
+ val destination = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ extractTarGz(tarGz, destination, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(destination.resolve("level1/level2/deep.txt").exists())
+ assertEquals("deep content", destination.resolve("level1/level2/deep.txt").readText())
+ }
+
+ @Test
+ fun `should extract symlink from assets tar gz`() {
+ val extractDir = tempDir.resolve("extracted")
+ val zipFile = getFileFromResources("archive/single_symlink.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ extractTarGz(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ val symlink = extractDir.resolve("link.txt")
+ assertTrue(symlink.exists())
+ assertTrue(symlink.isSymbolicLink())
+ }
+
+ @Test
+ fun `should extract empty folder from assets tar gz`() {
+ val extractDir = tempDir.resolve("extracted")
+ val zipFile = getFileFromResources("archive/empty_folder.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ extractTarGz(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ assertTrue(extractDir.resolve("archive").exists())
+ }
+
+
+ @Test
+ fun `should handle mixed content in tar gz`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+
+ // Regular files
+ sourceDir.resolve("text.txt").writeText("text")
+ sourceDir.resolve("binary.bin").writeBytes(ByteArray(100) { it.toByte() })
+
+ // Directories
+ val nestedDir = sourceDir.resolve("nested/dir").createDirectories()
+ nestedDir.resolve("nested.txt").writeText("nested")
+
+ val target = sourceDir.resolve("target.txt").apply { writeText("target") }
+ sourceDir.resolve("link.txt").createSymbolicLinkPointingTo(target)
+
+ val outputFile = tempDir.resolve("output.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // When
+ tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger)
+
+ // Then
+ val extractDir = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+ extractTarGz(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ assertTrue(extractDir.resolve("text.txt").exists())
+ assertTrue(extractDir.resolve("binary.bin").exists())
+ assertTrue(extractDir.resolve("nested/dir/nested.txt").exists())
+ assertEquals("text", extractDir.resolve("text.txt").readText())
+ assertEquals("nested", extractDir.resolve("nested/dir/nested.txt").readText())
+ val symlink = extractDir.resolve("link.txt")
+ assertTrue(symlink.exists())
+ assertTrue(symlink.isSymbolicLink())
+ }
+
+ @Test
+ fun `should handle mixed content in assets tar gz`() {
+ // Given
+ val archiveFile = getFileFromResources("archive/mixed.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ // Then
+ val extractDir = tempDir.resolve("extracted")
+ extractTarGz(archiveFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ assertTrue(extractDir.resolve("text.txt").exists())
+ assertTrue(extractDir.resolve("binary.bin").exists())
+ assertTrue(extractDir.resolve("nested/nested.txt").exists())
+ assertEquals("abc", extractDir.resolve("text.txt").readText().trim())
+ assertEquals("nested", extractDir.resolve("nested/nested.txt").readText().trim())
+ val symlink = extractDir.resolve("link.txt")
+ assertTrue(symlink.exists())
+ assertTrue(symlink.isSymbolicLink())
+ }
+ // ========== ExtractTarZst Tests ==========
+
+ @Test
+ fun `should extract tar zst without stripping top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file.txt").writeText("zst content")
+ val tarZst = tempDir.resolve("archive.tar.zst")
+ val tmpDir = tempDir.resolve("tmp1")
+ tarZst(sourceDir, tarZst, withTopLevelFolder = true, tmpDir, logger)
+
+ val destination = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ extractTarZst(tarZst, destination, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(destination.resolve("source/file.txt").exists())
+ assertEquals("zst content", destination.resolve("source/file.txt").readText())
+ }
+
+ @Test
+ fun `should extract tar zst with stripping top level folder`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file.txt").writeText("zst content")
+ val tarZst = tempDir.resolve("archive.tar.zst")
+ val tmpDir = tempDir.resolve("tmp1")
+ tarZst(sourceDir, tarZst, withTopLevelFolder = true, tmpDir, logger)
+
+ val destination = tempDir.resolve("extracted")
+ val extractTmpDir = tempDir.resolve("tmp2")
+
+ // When
+ extractTarZst(tarZst, destination, stripTopLevelFolder = true, cleanDestination = false, extractTmpDir, logger)
+
+ // Then
+ assertTrue(destination.resolve("file.txt").exists())
+ assertEquals("zst content", destination.resolve("file.txt").readText())
+ }
+
+ // ========== Edge Cases and Error Handling ==========
+
+ @Test
+ fun `should handle large files`() {
+ // Given
+ val sourceFile = tempDir.resolve("large.txt")
+ val largeContent = "x".repeat(1024 * 1024) // 1MB
+ sourceFile.writeText(largeContent)
+
+ val gzFile = tempDir.resolve("large.gz")
+
+ // When
+ gz(gzFile, sourceFile)
+
+ // Then
+ assertTrue(gzFile.exists())
+ assertTrue(gzFile.fileSize() > 0)
+ assertTrue(gzFile.fileSize() < largeContent.length) // Should be compressed
+ }
+
+ @Test
+ fun `should overwrite existing archive`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ sourceDir.resolve("file1.txt").writeText("content1")
+ val targetZip = tempDir.resolve("output.zip")
+
+ // Create first archive
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+ val firstSize = targetZip.fileSize()
+
+ // Modify source
+ sourceDir.resolve("file2.txt").writeText("content2")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ val secondSize = targetZip.fileSize()
+ assertTrue(secondSize > firstSize)
+ val entries = readZipEntryNames(targetZip)
+ assertEquals(2, entries.size)
+ }
+
+ @Test
+ fun `should handle deeply nested directory structures`() {
+ // Given
+ val sourceDir = tempDir.resolve("source").createDirectories()
+ val deepDir = sourceDir.resolve("a/b/c/d/e/f/g/h").createDirectories()
+ deepDir.resolve("deep.txt").writeText("deep")
+ val targetZip = tempDir.resolve("output.zip")
+
+ // When
+ zip(targetZip, sourceDir, withTopLevelFolder = false)
+
+ // Then
+ val destination = tempDir.resolve("extracted")
+ extractZip(targetZip, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger)
+ assertTrue(destination.resolve("a/b/c/d/e/f/g/h/deep.txt").exists())
+ assertEquals("deep", destination.resolve("a/b/c/d/e/f/g/h/deep.txt").readText())
+ }
+
+ @Test
+ fun `should handle empty sequence for zip`() {
+ // Given
+ val emptySequence = emptySequence>()
+ val targetZip = tempDir.resolve("empty.zip")
+
+ // When
+ zip(targetZip, emptySequence)
+
+ // Then
+ assertTrue(targetZip.exists())
+ val entries = readZipEntryNames(targetZip)
+ assertEquals(0, entries.size)
+ }
+
+ @Test
+ fun `tarGz and extractTarGz should preserve file permissions`() {
+ assumePosixFileSystem()
+
+ // Given
+ val sourceDir = tempDir.resolve("src").createDirectories()
+ val file = sourceDir.resolve("script.sh").apply { writeText("echo hi") }
+ val expectedPerms = setOf(
+ PosixFilePermission.OWNER_READ,
+ PosixFilePermission.OWNER_WRITE,
+ PosixFilePermission.OWNER_EXECUTE,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.GROUP_EXECUTE,
+ PosixFilePermission.OTHERS_READ,
+ PosixFilePermission.OTHERS_EXECUTE,
+ )
+
+ file.setPosixFilePermissions(expectedPerms)
+
+ val archive = tempDir.resolve("perm.tar.gz")
+ val tmp = tempDir.resolve("tmp")
+ tarGz(sourceDir, archive, withTopLevelFolder = false, tmp, logger)
+
+ val dest = tempDir.resolve("out-tar")
+ extractTarGz(archive, dest, stripTopLevelFolder = false, cleanDestination = false, tmp, logger)
+
+ // Then
+ val extracted = dest.resolve("script.sh")
+ assertTrue(extracted.exists())
+ val actualPerms = extracted.getPosixFilePermissions()
+ assertEquals(expectedPerms, actualPerms)
+ }
+
+ @Test
+ fun `zip and extractZip should preserve file permissions`() {
+ assumePosixFileSystem()
+
+ // Given
+ val sourceDir = tempDir.resolve("srcZip").createDirectories()
+ val file = sourceDir.resolve("script.sh").apply { writeText("echo hi") }
+ val expectedPerms = setOf(
+ PosixFilePermission.OWNER_READ,
+ PosixFilePermission.OWNER_WRITE,
+ PosixFilePermission.OWNER_EXECUTE,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.GROUP_EXECUTE,
+ PosixFilePermission.OTHERS_READ,
+ PosixFilePermission.OTHERS_EXECUTE,
+ )
+
+ file.setPosixFilePermissions(expectedPerms)
+
+ val archive = tempDir.resolve("script.sh")
+ val tmp = tempDir.resolve("tmpZip")
+ zip(sourceDir, archive, withTopLevelFolder = false, tmp, logger)
+
+ val dest = tempDir.resolve("out-zip")
+ extractZip(archive, dest, stripTopLevelFolder = false, cleanDestination = false, tmp, logger)
+
+ // Then
+ val extracted = dest.resolve("script.sh")
+ assertTrue(extracted.exists())
+ val actualPerms = extracted.getPosixFilePermissions()
+ assertEquals(expectedPerms, actualPerms)
+ }
+
+ @Test
+ fun `extractZip should preserve file permissions from assets`() {
+ assumePosixFileSystem()
+
+ val extractDir = tempDir.resolve("extracted")
+ val zipFile = getFileFromResources("archive/readonly.zip")
+ val tmpDir = tempDir.resolve("tmp")
+
+ val expectedPermissions = setOf(
+ PosixFilePermission.OWNER_READ,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.OTHERS_READ,
+ )
+
+ extractZip(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ val readonlyFile = extractDir.resolve("readonly.txt")
+ assertTrue(readonlyFile.exists())
+ val actualPerms = readonlyFile.getPosixFilePermissions()
+ assertEquals(expectedPermissions, actualPerms)
+ }
+
+ @Test
+ fun `extractTarGz should preserve file permissions from assets`() {
+ assumePosixFileSystem()
+
+ val extractDir = tempDir.resolve("extracted")
+ val zipFile = getFileFromResources("archive/readonly.tar.gz")
+ val tmpDir = tempDir.resolve("tmp")
+
+ val expectedPermissions = setOf(
+ PosixFilePermission.OWNER_READ,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.OTHERS_READ,
+ )
+
+ extractTarGz(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger)
+
+ val readonlyFile = extractDir.resolve("readonly.txt")
+ assertTrue(readonlyFile.exists())
+ val actualPerms = readonlyFile.getPosixFilePermissions()
+ assertEquals(expectedPermissions, actualPerms)
+ }
+
+ // ========== Helper Functions ==========
+
+ private fun readZipEntries(zipFile: Path): Map {
+ val result = mutableMapOf()
+ ZipInputStream(zipFile.inputStream()).use { zipIn ->
+ var entry = zipIn.nextEntry
+ while (entry != null) {
+ if (!entry.isDirectory) {
+ result[entry.name] = zipIn.readBytes().decodeToString()
+ }
+ zipIn.closeEntry()
+ entry = zipIn.nextEntry
+ }
+ }
+ return result
+ }
+
+ private fun readZipEntryNames(zipFile: Path): List {
+ val result = mutableListOf()
+ ZipInputStream(zipFile.inputStream()).use { zipIn ->
+ var entry = zipIn.nextEntry
+ while (entry != null) {
+ if (!entry.isDirectory) {
+ result.add(entry.name)
+ }
+ zipIn.closeEntry()
+ entry = zipIn.nextEntry
+ }
+ }
+ return result
+ }
+
+ private fun createTestZip(zipFile: Path, files: Map) {
+ ZipOutputStream(zipFile.outputStream()).use { zipOut ->
+ files.forEach { (name, content) ->
+ zipOut.putNextEntry(ZipEntry(name))
+ zipOut.write(content.toByteArray())
+ zipOut.closeEntry()
+ }
+ }
+ }
+
+ private fun getFileFromResources(path: String): Path {
+ return Paths.get(javaClass.classLoader.getResource(path)!!.toURI())
+ }
+
+
+ private fun assumePosixFileSystem() {
+ assumeTrue("Posix permissions are not supported on this system. Skipping test.", tempDir.isPosixSupported())
+ }
+
+ private fun Path.isPosixSupported(): Boolean {
+ return try {
+ Files.getFileStore(this).supportsFileAttributeView("posix")
+ }
+ catch (_: IOException) {
+ // Could not determine if a file system supports POSIX attributes, assume it does not
+ false
+ }
+ }
+}
\ No newline at end of file
diff --git a/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt
new file mode 100644
index 000000000000..942752efed02
--- /dev/null
+++ b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt
@@ -0,0 +1,48 @@
+package fleet.buildtool.fs
+
+import java.io.File
+import java.nio.ByteBuffer
+import kotlin.io.path.readBytes
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+
+class UtilsTest {
+ @Test
+ fun `sha256 should not be modified as it will break dockApiVersion generation`() {
+ assertEquals(
+ "3202d841030f0606d93ea9e89aa06261a784615c961c94bfd92286d30538e8c8",
+ sha256(resourceAsFile("/example-abi-lf.txt").toPath().readBytesForSha256())
+ )
+ }
+
+ @Test
+ fun `sha256 should be agnostic of line endings`() {
+ val expectedHash = "3202d841030f0606d93ea9e89aa06261a784615c961c94bfd92286d30538e8c8"
+ assertEquals(expectedHash, sha256(resourceAsFile("/example-abi-lf.txt").toPath().readBytesForSha256()), "LF line ending file must produce correct hash")
+ assertEquals(expectedHash, sha256(resourceAsFile("/example-abi-crlf.txt").toPath().readBytesForSha256()), "CRLF line ending file must produce correct hash")
+ assertEquals(expectedHash, sha256(resourceAsFile("/example-abi-cr.txt").toPath().readBytesForSha256()), "CR line ending file must produce correct hash")
+ }
+
+ @Test
+ fun `should have no action on LF`() {
+ val lfFile = resourceAsFile("/example-abi-lf.txt").toPath().readBytes()
+ assertContentEquals(lfFile, ByteBuffer.wrap(lfFile).normaliseLineSeparators().array(), "should not have modified the content")
+ }
+
+ @Test
+ fun `should normalize CRLF`() {
+ val lfFile = resourceAsFile("/example-abi-lf.txt").toPath().readBytes()
+ assertContentEquals(lfFile, ByteBuffer.wrap(resourceAsFile("/example-abi-crlf.txt").toPath().readBytes()).normaliseLineSeparators().array(), "should not contain LF")
+ }
+
+ @Test
+ fun `should normalize CR `() {
+ val lfFile = resourceAsFile("/example-abi-lf.txt").toPath().readBytes()
+ assertContentEquals(lfFile, ByteBuffer.wrap(resourceAsFile("/example-abi-cr.txt").toPath().readBytes()).normaliseLineSeparators().array(), "should not contain CR")
+ }
+}
+
+private fun resourceAsFile(filename: String) = object {}.javaClass.getResource(filename)?.let { File(it.toURI()) } ?: error(
+ "could not find test resource $filename"
+)
\ No newline at end of file
diff --git a/fleet/build/http/BUILD.bazel b/fleet/build/http/BUILD.bazel
new file mode 100644
index 000000000000..b1a0eaa35042
--- /dev/null
+++ b/fleet/build/http/BUILD.bazel
@@ -0,0 +1,69 @@
+### auto-generated section `build fleet.build.http` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_http",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "http",
+ module_name = "fleet.build.http",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_http",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:kotlinx-coroutines-core",
+ "//fleet/ktor/client/core",
+ "//fleet/build/fs",
+ "@lib//:fleet-io-ktor-client-logging",
+ "@lib//:slf4j-api",
+ "@lib//:fleet-io-ktor-client-mock",
+ ],
+ exports = [
+ "@lib//:kotlinx-coroutines-core",
+ "//fleet/ktor/client/core",
+ ]
+)
+
+jvm_library(
+ name = "http_test_lib",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmTest/**/*.kt", "srcJvmTest/**/*.java", "srcJvmTest/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_http",
+ associates = [":http"],
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:kotlinx-coroutines-core",
+ "//fleet/ktor/client/core",
+ "//fleet/build/fs",
+ "//fleet/build/fs:fs_test_lib",
+ "@lib//:fleet-io-ktor-client-logging",
+ "@lib//:slf4j-api",
+ "//fleet/junit4",
+ "@lib//:kotlin-test",
+ "@lib//:fleet-io-ktor-client-mock",
+ ],
+ exports = [
+ "@lib//:kotlinx-coroutines-core",
+ "//fleet/ktor/client/core",
+ ]
+)
+### auto-generated section `build fleet.build.http` end
+
+### auto-generated section `test fleet.build.http` start
+load("@community//build:tests-options.bzl", "jps_test")
+
+jps_test(
+ name = "http_test",
+ runtime_deps = [":http_test_lib"]
+)
+### auto-generated section `test fleet.build.http` end
\ No newline at end of file
diff --git a/fleet/build/http/fleet.build.http.iml b/fleet/build/http/fleet.build.http.iml
new file mode 100644
index 000000000000..a1da9435717c
--- /dev/null
+++ b/fleet/build/http/fleet.build.http.iml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/http/gradlebuild/build.gradle.kts b/fleet/build/http/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..6a650b00b6b6
--- /dev/null
+++ b/fleet/build/http/gradlebuild/build.gradle.kts
@@ -0,0 +1,76 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.http"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.commonMain.dependencies {
+ implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ exclude(group = "org.jetbrains", module = "annotations")
+ }
+ api(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.io.ktor.ktor.client.logging.jvm1780199912.get().let { "${it.group}:ktor-client-logging:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ exclude(group = "org.slf4j", module = "slf4j-jdk14")
+ }
+ implementation(jps.io.ktor.ktor.client.mock.jvm121671911.get().let { "${it.group}:ktor-client-mock:${it.version}" }) {
+ isTransitive = false
+ }
+ api(project(":fleet.ktor.client.core"))
+ implementation(project(":fleet.build.fs"))
+ }
+ sourceSets.commonTest.dependencies {
+ implementation(jps.org.jetbrains.kotlin.kotlin.test542871666.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
+ }
+ }
+ sourceSets.jvmTest.dependencies {
+ implementation(project(":fleet.junit4"))
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt b/fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt
new file mode 100644
index 000000000000..ca92611a37aa
--- /dev/null
+++ b/fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt
@@ -0,0 +1,177 @@
+package fleet.buildtool.http
+
+import fleet.buildtool.fs.*
+import io.ktor.client.*
+import io.ktor.client.call.*
+import io.ktor.client.plugins.*
+import io.ktor.client.plugins.logging.*
+import io.ktor.client.plugins.logging.Logger as KtorLogger
+import io.ktor.client.request.*
+import io.ktor.client.statement.*
+import io.ktor.http.*
+import io.ktor.utils.io.*
+import io.ktor.utils.io.jvm.javaio.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.StandardOpenOption
+import java.time.Duration
+import org.slf4j.Logger
+import kotlin.io.path.*
+
+fun fleetBuildHttpClient(logger: Logger, userAgent: String = "FleetBuildTooling/1.0"): HttpClient = HttpClient {
+ expectSuccess = true
+ followRedirects = true
+ install(HttpTimeout) {
+ requestTimeoutMillis = Duration.ofMinutes(5).toMillis()
+ }
+ install(HttpRequestRetry) {
+ retryOnServerErrors(maxRetries = 10)
+ exponentialDelay()
+ }
+ install(Logging) {
+ this.logger = object : KtorLogger {
+ override fun log(message: String) {
+ logger.info(message)
+ }
+ }
+ level = LogLevel.ALL
+ sanitizeHeader { header -> header == HttpHeaders.Authorization }
+ }
+ defaultRequest {
+ header("User-Agent", userAgent)
+ }
+}
+
+fun HttpClient.downloadFileBlocking(url: String, destination: Path, checksumValidation: ChecksumValidation = ChecksumValidation.Disabled, block: HttpRequestBuilder.() -> Unit = {}): Path = runBlocking {
+ downloadFile(url, destination, checksumValidation, block)
+}
+
+sealed class ChecksumValidation {
+ /**
+ * Download the file without checking its integrity once on disk.
+ *
+ * Always re-downloads the file, regardless of whether the target file is already present on disk.
+ */
+ object Disabled : ChecksumValidation()
+
+ /**
+ * Checks integrity of the downloaded file.
+ *
+ * Checks whether the target file is already on disk and matching the provided [expected] hash and if the hash matches the file won't be re-downloaded.
+ *
+ * @param algorithm of the [expected] hash string
+ * @param expected the expected hash of the downloaded file
+ */
+ data class UsingHash(val algorithm: ChecksumAlgorithm, val expected: String) : ChecksumValidation()
+
+ /**
+ * Checks integrity of the downloaded file by downloading first the file pointed by [url] which should contain the hash of the downloaded
+ * file.
+ *
+ * Checks whether the target file is already on disk and matching the hash contained in the file downloaded from [url] and if the hash
+ * matches the file won't be re-downloaded.
+ *
+ * @param algorithm of the hash present in the file downloaded from [url]
+ * @param url url to a remote checksum file to download
+ */
+ data class UsingHashDownloadedFile(val algorithm: ChecksumAlgorithm, val url: String) : ChecksumValidation()
+}
+
+enum class ChecksumAlgorithm {
+ SHA256,
+}
+
+suspend fun HttpClient.downloadFile(url: String, destination: Path, checksumValidation: ChecksumValidation, block: HttpRequestBuilder.() -> Unit = {}): Path {
+ return when (val hashResult = checkIfAlreadyDownloaded(destination, checksumValidation)) {
+ is HashResult.AlreadyDownloaded -> destination
+ HashResult.NotDownloaded, is HashResult.HashMismatched -> {
+ config {
+ install(Logging) {
+ level = LogLevel.HEADERS // a bug in Ktor Logging plugin leads to OOM if downloaded file is too big
+ }
+ }.prepareGet(url) {
+ expectSuccess = false
+ block()
+ }.execute { response ->
+ if (!response.status.isSuccess()) {
+ withContext(Dispatchers.IO) { Files.deleteIfExists(destination) }
+ error("failed to download $url: ${response.status} ${response.bodyAsText()}")
+ }
+
+ val channel: ByteReadChannel = response.body()
+ withContext(Dispatchers.IO) {
+ destination.parent.createDirectories()
+ destination.outputStream(StandardOpenOption.CREATE).buffered().use { fos ->
+ channel.copyTo(fos)
+ }
+ }
+ }
+
+ when (checksumValidation) {
+ ChecksumValidation.Disabled -> destination // no integrity validation
+ is ChecksumValidation.UsingHash,
+ is ChecksumValidation.UsingHashDownloadedFile,
+ -> {
+ val expected = when (hashResult) {
+ HashResult.NotDownloaded -> resolveChecksumHash(checksumValidation) ?: error("impossible to get an `null` hash here")
+ is HashResult.HashMismatched -> hashResult.expectedHash // do not re-resolve the expected hash, instead use the result of the previous hash check that happened at checkIfAlreadyDownloaded
+ is HashResult.AlreadyDownloaded -> error("impossible") // would have hit first when branch
+ }
+ val actual = sha256(destination.readBytes())
+ when (expected == actual) {
+ true -> destination
+ else -> error("downloaded file '$destination' failed integrity check, expected=$expected, actual=$actual")
+ }
+ }
+ }
+ }
+ }
+}
+
+private suspend fun HttpClient.resolveChecksumHash(checksumValidation: ChecksumValidation): String? = when (checksumValidation) {
+ ChecksumValidation.Disabled -> null
+ is ChecksumValidation.UsingHashDownloadedFile -> {
+ require(checksumValidation.algorithm == ChecksumAlgorithm.SHA256) { "Only SHA-256 algorithm is supported for now" }
+ val tmp = createTempFile(prefix = "hash", suffix = ".sha256")
+ val hashFile = downloadFile(checksumValidation.url, tmp, ChecksumValidation.Disabled)
+ val hash = hashFile.readText().split(" ", limit = 2).first()
+ tmp.deleteIfExists()
+ hash
+ }
+ is ChecksumValidation.UsingHash -> {
+ require(checksumValidation.algorithm == ChecksumAlgorithm.SHA256) { "Only SHA-256 algorithm is supported for now" }
+ checksumValidation.expected
+ }
+}
+
+private suspend fun HttpClient.checkIfAlreadyDownloaded(destination: Path, checksumValidation: ChecksumValidation): HashResult = when (destination.exists()) {
+ false -> HashResult.NotDownloaded
+ true -> when (checksumValidation) {
+ ChecksumValidation.Disabled -> {
+ destination.deleteIfExists()
+ HashResult.NotDownloaded
+ }
+ is ChecksumValidation.UsingHashDownloadedFile,
+ is ChecksumValidation.UsingHash,
+ -> {
+ val expected = resolveChecksumHash(checksumValidation) ?: error("impossible to get an `null` hash here")
+ val actual = sha256(destination.readBytes())
+ when (expected == actual) {
+ true -> HashResult.AlreadyDownloaded(hash = actual)
+ else -> {
+ withContext(Dispatchers.IO) { destination.deleteIfExists() }
+ HashResult.HashMismatched(expectedHash = expected)
+ }
+ }
+ }
+ }
+}
+
+private sealed class HashResult {
+ data class AlreadyDownloaded(val hash: String) : HashResult()
+ object NotDownloaded : HashResult()
+ data class HashMismatched(val expectedHash: String) : HashResult()
+}
diff --git a/fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt b/fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt
new file mode 100644
index 000000000000..6a421dd4216d
--- /dev/null
+++ b/fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt
@@ -0,0 +1,183 @@
+package fleet.buildtool.http
+
+import io.ktor.client.HttpClient
+import io.ktor.client.engine.mock.MockEngine
+import io.ktor.client.engine.mock.respond
+import io.ktor.http.HttpHeaders
+import io.ktor.http.HttpStatusCode
+import io.ktor.http.headersOf
+import io.ktor.utils.io.ByteReadChannel
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.io.path.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+
+class HttpClientTests {
+ @OptIn(ExperimentalPathApi::class)
+ @Test
+ fun `should always download file when checksum validation is disabled`() {
+ val resourceUrl = "https://example.com/some-resource.txt"
+ val requestCounter = mapOf(
+ resourceUrl to AtomicInteger(0),
+ )
+
+ val mockEngine = MockEngine { request ->
+ requestCounter.getValue(request.url.toString()).incrementAndGet()
+ respond(content = ByteReadChannel("sometestdata"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt"))
+ }
+
+ val httpClient = HttpClient(mockEngine) {}
+ val destination = createTempDirectory().resolve("some-resource.txt")
+ destination.parent.createDirectories()
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.deleteIfExists()
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.Disabled)
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file")
+ assertEquals("sometestdata", destination.readText())
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.parent.createDirectories()
+ destination.writeText("some other data but that will be overridden anyway")
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.Disabled)
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file")
+ assertEquals("sometestdata", destination.readText())
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.parent.createDirectories()
+ destination.writeText("sometestdata")
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.Disabled)
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file despite it already existing and matching checksum, because checksum validation is disabled")
+ assertEquals("sometestdata", destination.readText())
+
+ destination.parent.deleteRecursively()
+ }
+
+ @OptIn(ExperimentalPathApi::class)
+ @Test
+ fun `should not re-downloaded file if already on disk and matching the hash resolve by URL`() {
+ val resourceUrl = "https://example.com/some-resource.txt"
+ val resourceUrlSha = "$resourceUrl.sha256"
+ val requestCounter = mapOf(
+ resourceUrl to AtomicInteger(0),
+ resourceUrlSha to AtomicInteger(0),
+ )
+
+ val mockEngine = MockEngine { request ->
+ requestCounter.getValue(request.url.toString()).incrementAndGet()
+ when (request.url.toString()) {
+ resourceUrl -> respond(content = ByteReadChannel("sometestdata"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt"))
+ resourceUrlSha -> respond(content = ByteReadChannel("10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486 *some-resource.txt"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt"))
+ else -> error("unexpected request")
+ }
+ }
+
+ val httpClient = HttpClient(mockEngine) {}
+ val destination = createTempDirectory().resolve("some-resource.txt")
+ destination.parent.createDirectories()
+
+ requestCounter.getValue(resourceUrl).set(0)
+ requestCounter.getValue(resourceUrlSha).set(0)
+ destination.deleteIfExists()
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHashDownloadedFile(ChecksumAlgorithm.SHA256, resourceUrlSha))
+ assertEquals(1, requestCounter.getValue(resourceUrlSha).get(), "should have tried to download the checksum file once for integrity check")
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it did not exist")
+ assertEquals("sometestdata", destination.readText())
+
+ requestCounter.getValue(resourceUrl).set(0)
+ requestCounter.getValue(resourceUrlSha).set(0)
+ destination.parent.createDirectories()
+ destination.writeText("some other data but that will be overridden anyway")
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHashDownloadedFile(ChecksumAlgorithm.SHA256, resourceUrlSha))
+ assertEquals(1, requestCounter.getValue(resourceUrlSha).get(), "should have tried to download the checksum file once for integrity check, but not twice because it re-used the first download of the checksum file")
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it existed but did not matched checksum")
+ assertEquals("sometestdata", destination.readText())
+
+ requestCounter.getValue(resourceUrl).set(0)
+ requestCounter.getValue(resourceUrlSha).set(0)
+ destination.parent.createDirectories()
+ destination.writeText("sometestdata")
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHashDownloadedFile(ChecksumAlgorithm.SHA256, resourceUrlSha))
+ assertEquals(1, requestCounter.getValue(resourceUrlSha).get(), "should have tried to download the checksum file once for integrity check, but not twice because it re-used the first download of the checksum file")
+ assertEquals(0, requestCounter.getValue(resourceUrl).get(), "should not have tried to download the file because it exists and matched checksum")
+ assertEquals("sometestdata", destination.readText())
+
+ destination.parent.deleteRecursively()
+ }
+
+ @OptIn(ExperimentalPathApi::class)
+ @Test
+ fun `should not re-downloaded file if already on disk and matching the raw hash`() {
+ val resourceUrl = "https://example.com/some-resource.txt"
+ val requestCounter = mapOf(
+ resourceUrl to AtomicInteger(0),
+ )
+
+ val mockEngine = MockEngine { request ->
+ requestCounter.getValue(request.url.toString()).incrementAndGet()
+ when (request.url.toString()) {
+ resourceUrl -> respond(content = ByteReadChannel("sometestdata"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt"))
+ else -> error("unexpected request")
+ }
+ }
+
+ val httpClient = HttpClient(mockEngine) {}
+ val destination = createTempDirectory().resolve("some-resource.txt")
+ destination.parent.createDirectories()
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.deleteIfExists()
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486"))
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it did not exist")
+ assertEquals("sometestdata", destination.readText())
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.parent.createDirectories()
+ destination.writeText("some other data but that will be overridden anyway")
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486"))
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it existed but did not matched checksum")
+ assertEquals("sometestdata", destination.readText())
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.parent.createDirectories()
+ destination.writeText("sometestdata")
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486"))
+ assertEquals(0, requestCounter.getValue(resourceUrl).get(), "should not have tried to download the file because it exists and matched checksum")
+ assertEquals("sometestdata", destination.readText())
+
+ destination.parent.deleteRecursively()
+ }
+
+ @OptIn(ExperimentalPathApi::class)
+ @Test
+ fun `should fail when server returned a file not matching checksum`() {
+ val resourceUrl = "https://example.com/some-resource.txt"
+ val requestCounter = mapOf(
+ resourceUrl to AtomicInteger(0),
+ )
+
+ val mockEngine = MockEngine { request ->
+ requestCounter.getValue(request.url.toString()).incrementAndGet()
+ when (request.url.toString()) {
+ resourceUrl -> respond(content = ByteReadChannel("sometestdataDJFBSIDFUBSDIFUSDBFIUDB"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt"))
+ else -> error("unexpected request")
+ }
+ }
+
+ val httpClient = HttpClient(mockEngine) {}
+ val destination = createTempDirectory().resolve("some-resource.txt")
+ destination.parent.createDirectories()
+
+ requestCounter.getValue(resourceUrl).set(0)
+ destination.deleteIfExists()
+ val exception = assertFailsWith {
+ httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486"))
+ }
+ assertEquals("downloaded file '$destination' failed integrity check, expected=10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486, actual=5d2080c90b2d11ff9f17adb960c0f579931d94311e2c47cf5c2d97de4865d4f0", exception.message)
+ assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it did not exist")
+ assertEquals("sometestdataDJFBSIDFUBSDIFUSDBFIUDB", destination.readText())
+
+ destination.parent.deleteRecursively()
+ }
+}
diff --git a/fleet/build/license/BUILD.bazel b/fleet/build/license/BUILD.bazel
new file mode 100644
index 000000000000..34f6ce457f21
--- /dev/null
+++ b/fleet/build/license/BUILD.bazel
@@ -0,0 +1,28 @@
+### auto-generated section `build fleet.build.license` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_license",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "license",
+ module_name = "fleet.build.license",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcCommonMain/**/*.kt", "srcCommonMain/**/*.java", "srcCommonMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_license",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:kotlinx-serialization-core",
+ "@lib//:kotlinx-serialization-json",
+ ]
+)
+### auto-generated section `build fleet.build.license` end
\ No newline at end of file
diff --git a/fleet/build/license/fleet.build.license.iml b/fleet/build/license/fleet.build.license.iml
new file mode 100644
index 000000000000..6cbd1f6fb05b
--- /dev/null
+++ b/fleet/build/license/fleet.build.license.iml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/license/gradlebuild/build.gradle.kts b/fleet/build/license/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..d0397b043313
--- /dev/null
+++ b/fleet/build/license/gradlebuild/build.gradle.kts
@@ -0,0 +1,59 @@
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ alias(jps.plugins.kotlin.serialization)
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.license"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.serialization.core.jvm1739247612.get().let { "${it.group}:kotlinx-serialization-core:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.json.jvm231489733.get().let { "${it.group}:kotlinx-serialization-json:${it.version}" }) {
+ isTransitive = false
+ }
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/license/srcCommonMain/.pseudoCommonKotlinSourceSet b/fleet/build/license/srcCommonMain/.pseudoCommonKotlinSourceSet
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt b/fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt
new file mode 100644
index 000000000000..2cba291fdd16
--- /dev/null
+++ b/fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt
@@ -0,0 +1,18 @@
+package fleet.buildtool.license
+
+import kotlinx.serialization.Serializable
+
+fun thirdPartyLicenseFilename(
+ productName: String,
+ version: String,
+ extension: String,
+): String = "$productName-$version-third-party-libraries${extension}"
+
+@Serializable
+data class JetbrainsLicenceEntry(
+ val name: String,
+ val version: String?,
+ val url: String,
+ val license: String?,
+ val licenseUrl: String?,
+)
diff --git a/fleet/build/platform/BUILD.bazel b/fleet/build/platform/BUILD.bazel
new file mode 100644
index 000000000000..0bfcd2d1829d
--- /dev/null
+++ b/fleet/build/platform/BUILD.bazel
@@ -0,0 +1,29 @@
+### auto-generated section `build fleet.build.platform` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_platform",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "platform",
+ module_name = "fleet.build.platform",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_platform",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:kotlinx-serialization-core",
+ "@lib//:kotlinx-serialization-json",
+ "@lib//:kotlinx-coroutines-core",
+ ]
+)
+### auto-generated section `build fleet.build.platform` end
\ No newline at end of file
diff --git a/fleet/build/platform/fleet.build.platform.iml b/fleet/build/platform/fleet.build.platform.iml
new file mode 100644
index 000000000000..eaca7177a849
--- /dev/null
+++ b/fleet/build/platform/fleet.build.platform.iml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/platform/gradlebuild/build.gradle.kts b/fleet/build/platform/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..a7b14599d9bd
--- /dev/null
+++ b/fleet/build/platform/gradlebuild/build.gradle.kts
@@ -0,0 +1,63 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ alias(jps.plugins.kotlin.serialization)
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.platform"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.serialization.core.jvm1739247612.get().let { "${it.group}:kotlinx-serialization-core:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.json.jvm231489733.get().let { "${it.group}:kotlinx-serialization-json:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) {
+ isTransitive = false
+ }
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt
new file mode 100644
index 000000000000..b709c2e88d36
--- /dev/null
+++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt
@@ -0,0 +1,32 @@
+package fleet.buildtool.platform
+
+enum class DistributionChannel {
+ EAP, EAPCandidate, Nightly, Unspecified;
+
+ companion object {
+ fun fromString(s: String?): DistributionChannel = when (s) {
+ "nightly" -> Nightly
+ "stable",
+ "eap",
+ -> EAP
+ "eap_candidate" -> EAPCandidate
+ "", null -> Unspecified
+ else -> error("unsupported channel string '$s'")
+ }
+ }
+}
+
+fun DistributionChannel.toMarketplaceChannel(): MarketplaceChannel = when (this) {
+ DistributionChannel.EAP,
+ DistributionChannel.EAPCandidate,
+ -> MarketplaceChannel.Stable
+ DistributionChannel.Nightly -> MarketplaceChannel.Nightly
+ DistributionChannel.Unspecified -> MarketplaceChannel.Nightly // defaults to Nightly when channel of Fleet is unspecified
+}
+
+fun DistributionChannel.toFeedChannel(): String = when (this) {
+ DistributionChannel.EAP -> "eap"
+ DistributionChannel.EAPCandidate -> "eap_candidate"
+ DistributionChannel.Nightly -> "nightly"
+ DistributionChannel.Unspecified -> error("cannot resolve feed channel from an unspecified distribution channel")
+}
diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt
new file mode 100644
index 000000000000..73f9a796d63b
--- /dev/null
+++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt
@@ -0,0 +1,15 @@
+package fleet.buildtool.platform
+
+// TODO: maybe this needs its own module instead
+
+enum class MarketplaceChannel(val value: String) {
+ Stable("stable"),
+ Nightly("nightly");
+
+ override fun toString(): String = value
+
+ companion object {
+ private val byValues = MarketplaceChannel.values().associateBy { it.value }
+ fun fromString(s: String): MarketplaceChannel? = byValues[s]
+ }
+}
diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt
new file mode 100644
index 000000000000..dcbb9c6b40ad
--- /dev/null
+++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt
@@ -0,0 +1,196 @@
+package fleet.buildtool.platform
+
+import kotlinx.serialization.Serializable
+import java.nio.file.Path
+import java.io.Serializable as JavaSerializable
+
+val supportedPlatforms = listOf(
+ Platform.Windows.WindowsX64,
+ Platform.Windows.WindowsAarch64,
+ Platform.Linux.LinuxX64,
+ Platform.Linux.LinuxAarch64,
+ Platform.MacOs.MacOsX64,
+ Platform.MacOs.MacOsAarch64,
+)
+val supportedOses = supportedPlatforms.map { it.os }.toSet()
+
+@Serializable
+sealed class Platform(val os: OS) : JavaSerializable {
+ abstract val arch: Arch
+
+ @Serializable
+ sealed class Windows(override val arch: Arch) : Platform(OS.WINDOWS) {
+ @Serializable
+ object WindowsX64 : Windows(Arch.X64) {
+ private fun readResolve(): Any = WindowsX64
+ }
+
+ @Serializable
+ object WindowsAarch64 : Windows(Arch.AARCH64) {
+ private fun readResolve(): Any = WindowsAarch64
+ }
+ }
+
+ @Serializable
+ sealed class Linux(override val arch: Arch) : Platform(OS.LINUX) {
+ @Serializable
+ object LinuxX64 : Linux(Arch.X64) {
+ private fun readResolve(): Any = LinuxX64
+ }
+
+ @Serializable
+ object LinuxAarch64 : Linux(Arch.AARCH64) {
+ private fun readResolve(): Any = LinuxAarch64
+ }
+ }
+
+ @Serializable
+ sealed class MacOs(override val arch: Arch) : Platform(OS.MACOS) {
+ @Serializable
+ object MacOsX64 : MacOs(Arch.X64) {
+ private fun readResolve(): Any = MacOsX64
+ }
+
+ @Serializable
+ object MacOsAarch64 : MacOs(Arch.AARCH64) {
+ private fun readResolve(): Any = MacOsAarch64
+ }
+ }
+
+ companion object {
+ fun buildPlatform(): Platform {
+ val os = OS.currentOs()
+ val arch = Arch.currentArch()
+ return supportedPlatforms.findPlatform(os, arch)
+ }
+
+ fun windows(arch: Arch) = when (arch) {
+ Arch.AARCH64 -> Windows.WindowsAarch64
+ Arch.X64 -> Windows.WindowsX64
+ }
+
+ fun linux(arch: Arch) = when (arch) {
+ Arch.AARCH64 -> Linux.LinuxAarch64
+ Arch.X64 -> Linux.LinuxX64
+ }
+
+ fun macos(arch: Arch) = when (arch) {
+ Arch.AARCH64 -> MacOs.MacOsAarch64
+ Arch.X64 -> MacOs.MacOsX64
+ }
+
+ fun List.findPlatform(os: OS, arch: Arch) = singleOrNull { p ->
+ p.os == os && p.arch == arch
+ } ?: error("unsupported platform $os $arch (or more than one supported platform is matching this triple)")
+ }
+}
+
+enum class Arch {
+ AARCH64, X64;
+
+ companion object {
+ fun fromString(arch: String): Arch = when (arch.lowercase()) {
+ "x86_64", "amd64", "x64" -> X64
+ "arm64", "aarch64" -> AARCH64
+ else -> error("unsupported arch '$arch'")
+ }
+
+ fun currentArch(): Arch {
+ val arch = System.getProperty("os.arch") ?: error("failed to resolve system property 'os.arch'")
+ return fromString(arch)
+ }
+ }
+}
+
+enum class OS {
+ MACOS, WINDOWS, LINUX;
+
+ companion object {
+ fun fromString(os: String): OS = with(os.lowercase()) {
+ when {
+ contains("win") -> WINDOWS
+ contains("mac") -> MACOS
+ contains("nix") || contains("nux") -> LINUX
+ else -> error("unsuppported OS '$os'")
+ }
+ }
+
+ fun currentOs(): OS {
+ val os = System.getProperty("os.name") ?: error("failed to resolve system property 'os.name'")
+ return fromString(os)
+ }
+ }
+}
+
+enum class LibCImplementation {
+ GLIBC, MUSL;
+
+ companion object {
+ fun fromString(libc: String): LibCImplementation = when (libc.lowercase()) {
+ "musl" -> MUSL
+ "glibc" -> GLIBC
+ else -> error("unsupported libc implementation '$libc'")
+ }
+
+ // TODO: proper heuristic on libc detection for alpine linux support for example
+ fun currentLibc() = GLIBC
+ }
+}
+
+/**
+ * String representation of OS in Fleet parts (S3 storage, artefact names, etc.)
+ */
+fun OS.toFleetPartOsString() = when (this) {
+ OS.MACOS -> "macos"
+ OS.WINDOWS -> "windows"
+ OS.LINUX -> "linux"
+}
+
+/**
+ * String representation of Arch in Fleet parts (S3 storage, artefact names, etc.)
+ */
+fun Arch.toFleetPartArchString() = when (this) {
+ Arch.AARCH64 -> "aarch64"
+ Arch.X64 -> "x64"
+}
+
+fun Arch.distributionArchSuffix() = when (this) {
+ Arch.AARCH64 -> "-aarch64"
+ Arch.X64 -> ""
+}
+
+/**
+ * String representation of lib C implementation in Fleet parts (S3 storage, Gradle tooling, etc.)
+ */
+fun LibCImplementation.toFleetPartLibcString() = when (this) {
+ LibCImplementation.GLIBC -> null // we do not specify GLIBC in the distribution slug, as of legacy
+ LibCImplementation.MUSL -> "musl"
+}
+
+/**
+ * Converts FleetPlatform to the distribution slug used in our S3 for Fleet parts
+ *
+ * Beware, changing this code might result to a massive breaking change for customers of Fleet.
+ */
+fun Platform.toS3DistributionSlug(withLibC: LibCImplementation? = null): String {
+ val osName = os.toFleetPartOsString()
+ val archName = arch.toFleetPartArchString()
+ val libc = withLibC?.toFleetPartLibcString()
+
+ return when {
+ libc != null -> "${osName}_${libc}-$archName"
+ else -> "${osName}_$archName"
+ }
+}
+
+fun Path.withPlatformBinaryExtension(platform: Platform): Path = when (platform) {
+ is Platform.Windows -> resolveSibling("${fileName}.exe")
+ else -> this
+}
+
+fun Platform.binaryExtension() = when (this) {
+ is Platform.Windows -> ".exe"
+ else -> ""
+}
+
+fun String.withPlatformBinaryExtension(platform: Platform): String = "$this${platform.binaryExtension()}"
diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt
new file mode 100644
index 000000000000..c8c5ead4d273
--- /dev/null
+++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt
@@ -0,0 +1,28 @@
+package fleet.buildtool.platform
+
+import java.io.Serializable
+
+data class RustTarget(val platform: Platform, val libCImplementation: LibCImplementation? = null) : Serializable {
+
+ override fun toString(): String = when (platform) {
+ Platform.Linux.LinuxAarch64 -> when (libCImplementation) {
+ LibCImplementation.GLIBC -> "aarch64-unknown-linux-gnu"
+ LibCImplementation.MUSL, null -> "aarch64-unknown-linux-musl"
+ }
+
+ Platform.Linux.LinuxX64 -> when (libCImplementation) {
+ LibCImplementation.GLIBC -> "x86_64-unknown-linux-gnu"
+ LibCImplementation.MUSL, null -> "x86_64-unknown-linux-musl"
+ }
+
+ Platform.MacOs.MacOsAarch64 -> "aarch64-apple-darwin"
+ Platform.MacOs.MacOsX64 -> "x86_64-apple-darwin"
+ Platform.Windows.WindowsAarch64 -> "aarch64-pc-windows-msvc"
+ Platform.Windows.WindowsX64 -> "x86_64-pc-windows-msvc"
+ }
+}
+
+fun Platform.toFleetRustTarget(overrideLibCImplementation: LibCImplementation? = null): RustTarget =
+ RustTarget(this, overrideLibCImplementation)
+
+fun RustTarget.toS3DistributionSlug(): String = platform.toS3DistributionSlug(libCImplementation)
diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt
new file mode 100644
index 000000000000..a07e615b3a58
--- /dev/null
+++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt
@@ -0,0 +1,5 @@
+package fleet.buildtool.platform
+
+const val TEAMCITY_VERSION = "TEAMCITY_VERSION"
+
+fun isRunningOnTeamcity(): Boolean = System.getenv(TEAMCITY_VERSION)?.isNotBlank() ?: false
diff --git a/fleet/build/rules/fleet_module.bzl b/fleet/build/rules/fleet_module.bzl
new file mode 100644
index 000000000000..df99050e62fb
--- /dev/null
+++ b/fleet/build/rules/fleet_module.bzl
@@ -0,0 +1,31 @@
+visibility("private")
+
+def _fleet_module(ctx):
+ return [
+ DefaultInfo(
+ files = depset([]),
+ ),
+ ]
+
+fleet_module = rule(
+ attrs = {
+ "module_name": attr.string(
+ doc = """The Fleet module name""",
+ ),
+ "module_library": attr.label(
+ providers = [
+ [JavaInfo],
+ ],
+ allow_files = False,
+ ),
+ "_haven_cli": attr.label(
+ default = "//fleet/build/cli:haven",
+ executable = True,
+ # allow_single_file = True,
+ cfg = "exec",
+ # cfg = scrubbed_host_platform_transition,
+ ),
+ },
+ provides = [],
+ implementation = _fleet_module,
+)
diff --git a/fleet/build/s3/BUILD.bazel b/fleet/build/s3/BUILD.bazel
new file mode 100644
index 000000000000..e20a464fa77c
--- /dev/null
+++ b/fleet/build/s3/BUILD.bazel
@@ -0,0 +1,30 @@
+### auto-generated section `build fleet.build.s3` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_s3",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "s3",
+ module_name = "fleet.build.s3",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_s3",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "//fleet/build/platform",
+ "//fleet/build/fs",
+ "@lib//:kotlinx-coroutines-core",
+ "@lib//:fleet-aws-sdk-kotlin-s3-jvm",
+ ]
+)
+### auto-generated section `build fleet.build.s3` end
\ No newline at end of file
diff --git a/fleet/build/s3/fleet.build.s3.iml b/fleet/build/s3/fleet.build.s3.iml
new file mode 100644
index 000000000000..e40cd28fd132
--- /dev/null
+++ b/fleet/build/s3/fleet.build.s3.iml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/s3/gradlebuild/build.gradle.kts b/fleet/build/s3/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..063f1a296e6c
--- /dev/null
+++ b/fleet/build/s3/gradlebuild/build.gradle.kts
@@ -0,0 +1,65 @@
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.s3"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) {
+ isTransitive = false
+ }
+ implementation(jps.aws.sdk.kotlin.s3.jvm321782429.get().let { "${it.group}:s3:${it.version}" }) {
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
+ exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-jvm")
+ exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
+ exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
+ exclude(group = "org.slf4j", module = "slf4j-api")
+ }
+ implementation(project(":fleet.build.platform"))
+ implementation(project(":fleet.build.fs"))
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt
new file mode 100644
index 000000000000..5223f95c70ac
--- /dev/null
+++ b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt
@@ -0,0 +1,62 @@
+package fleet.buildtool.s3
+
+import fleet.buildtool.platform.*
+
+sealed class S3Version {
+ abstract override fun toString(): String
+
+ class Specific(val version: String) : S3Version() {
+ override fun toString() = version
+ }
+
+ /**
+ * Used for local testing only, artefacts using this version should never be used in production code.
+ * This is a mitigation until we have some of the logic described in https://youtrack.jetbrains.com/issue/FL-18599
+ */
+ object Latest : S3Version() {
+ override fun toString() = "latest"
+ }
+}
+
+private const val partsPrefix = "fleet-parts"
+
+fun Platform.fleetFleetDockS3Key(s3Version: S3Version): String = fleetPartS3Key(s3Version, "fleet-dock", { ".tar.zst" })
+fun Platform.fleetFleetJBRS3Key(s3Version: S3Version): String = fleetPartS3Key(s3Version, "fleet-jbr", { ".tar.zst" }, archiveName = "jbr")
+
+fun Platform.fleetPartS3Key(
+ s3Version: S3Version,
+ partName: String,
+ extension: () -> String,
+ archiveName: String = partName,
+) = fleetPartS3Key(
+ s3Version = s3Version,
+ partName = partName,
+ extension = extension,
+ distributionSlug = "${toS3DistributionSlug()}/",
+ archiveName = archiveName,
+)
+
+fun fleetPartS3Key(
+ s3Version: S3Version,
+ partName: String,
+ extension: () -> String,
+ archiveName: String = partName,
+): String = fleetPartS3Key(
+ s3Version,
+ partName,
+ extension,
+ distributionSlug = "",
+ archiveName = archiveName,
+)
+
+fun fleetPartS3Key(
+ s3Version: S3Version,
+ partName: String,
+ extension: () -> String,
+ distributionSlug: String,
+ archiveName: String = partName,
+): String = when (s3Version) {
+ S3Version.Latest -> "fleet-parts/latest/$partName/$distributionSlug$archiveName-latest${extension()}"
+ is S3Version.Specific -> "$partsPrefix/$partName/$distributionSlug$archiveName-${s3Version.version}${extension()}"
+}
+
diff --git a/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt
new file mode 100644
index 000000000000..2ab26010d1ab
--- /dev/null
+++ b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt
@@ -0,0 +1,20 @@
+package fleet.buildtool.s3
+
+import aws.sdk.kotlin.services.s3.S3Client
+import aws.sdk.kotlin.services.s3.headObject
+import aws.sdk.kotlin.services.s3.model.HeadObjectRequest
+import aws.sdk.kotlin.services.s3.model.NotFound
+import aws.sdk.kotlin.services.s3.model.S3Exception
+
+suspend fun S3Client.objectExists(block: HeadObjectRequest.Builder.() -> Unit): Boolean = try {
+ headObject(block)
+ true
+}
+catch (e: S3Exception) {
+ when {
+ e is NotFound -> false // object does not exist
+ e.sdkErrorMetadata.errorCode == "404" -> false // object does not exist
+ e.sdkErrorMetadata.errorCode == "403" -> false // object does not exist but credentials lacks `s3:ListBucket` permission
+ else -> throw e
+ }
+}
diff --git a/fleet/build/sign/BUILD.bazel b/fleet/build/sign/BUILD.bazel
new file mode 100644
index 000000000000..b8d0a636d954
--- /dev/null
+++ b/fleet/build/sign/BUILD.bazel
@@ -0,0 +1,27 @@
+### auto-generated section `build fleet.build.sign` start
+load("//build:compiler-options.bzl", "create_kotlinc_options")
+load("@rules_jvm//:jvm.bzl", "jvm_library")
+
+create_kotlinc_options(
+ name = "custom_sign",
+ opt_in = [
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlin.ExperimentalStdlibApi",
+ ],
+ x_consistent_data_class_copy_visibility = True,
+ x_jvm_default = "all-compatibility",
+ x_lambdas = "class"
+)
+
+jvm_library(
+ name = "sign",
+ module_name = "fleet.build.sign",
+ visibility = ["//visibility:public"],
+ srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]),
+ kotlinc_opts = ":custom_sign",
+ deps = [
+ "@lib//:kotlin-stdlib",
+ "@lib//:slf4j-api",
+ ]
+)
+### auto-generated section `build fleet.build.sign` end
\ No newline at end of file
diff --git a/fleet/build/sign/fleet.build.sign.iml b/fleet/build/sign/fleet.build.sign.iml
new file mode 100644
index 000000000000..35018d5e1dad
--- /dev/null
+++ b/fleet/build/sign/fleet.build.sign.iml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fleet/build/sign/gradlebuild/build.gradle.kts b/fleet/build/sign/gradlebuild/build.gradle.kts
new file mode 100644
index 000000000000..fc3d6d1bd271
--- /dev/null
+++ b/fleet/build/sign/gradlebuild/build.gradle.kts
@@ -0,0 +1,56 @@
+// 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")
+ alias(libs.plugins.dokka)
+ id("fleet.module-publishing-conventions")
+ id("fleet.sdk-repositories-publishing-conventions")
+ // GRADLE_PLUGINS__MARKER_START
+ id("fleet-module")
+ // GRADLE_PLUGINS__MARKER_END
+}
+
+fleetModule {
+ module {
+ name = "fleet.build.sign"
+ importedFromJps {}
+ }
+}
+
+@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
+kotlin {
+ // KOTLIN__MARKER_START
+ compilerOptions.freeCompilerArgs = listOf(
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.ExperimentalStdlibApi",
+ "-Xlambdas=class",
+ "-Xconsistent-data-class-copy-visibility",
+ "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks",
+ )
+ jvm {}
+ 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.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.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) {
+ isTransitive = false
+ exclude(group = "org.slf4j", module = "slf4j-jdk14")
+ }
+ }
+ // KOTLIN__MARKER_END
+}
\ No newline at end of file
diff --git a/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt
new file mode 100644
index 000000000000..32dbb14a4de4
--- /dev/null
+++ b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt
@@ -0,0 +1,11 @@
+package fleet.buildtool.sign
+
+import org.slf4j.Logger
+import java.nio.file.Path
+
+val jetSignJsonContentType: Pair = "contentType" to "text/plain"
+
+interface FleetSigner {
+ fun signFiles(files: Collection, options: Map = emptyMap(), logger: Logger)
+ fun gpgSign(data: Map, options: Map = emptyMap(), temporaryDirectory: Path, logger: Logger): Map
+}
diff --git a/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt
new file mode 100644
index 000000000000..2fc63d4e84cd
--- /dev/null
+++ b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt
@@ -0,0 +1,23 @@
+package fleet.buildtool.sign
+
+import org.slf4j.Logger
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.StandardOpenOption
+import kotlin.io.path.pathString
+import java.io.Serializable as JavaSerializable
+
+object MockSigner : FleetSigner, JavaSerializable {
+ override fun signFiles(files: Collection, options: Map, logger: Logger) {
+ files.forEach { file ->
+ Files.writeString(file.resolveSibling("${file.fileName.pathString}.asc"), "", StandardOpenOption.CREATE)
+ logger.info("[mockSign]: pretended to sign '${file.fileName.pathString}'")
+ }
+ }
+
+ override fun gpgSign(data: Map, options: Map, temporaryDirectory: Path, logger: Logger): Map =
+ data.mapValues { (key, _) ->
+ logger.info("[mockSign]: pretended to gpg sign '$key'")
+ byteArrayOf()
+ }
+}
diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel
index ee0b02b5f0bd..2f3eebd1146b 100644
--- a/lib/BUILD.bazel
+++ b/lib/BUILD.bazel
@@ -1458,6 +1458,300 @@ jvm_import(
visibility = ["//visibility:public"]
)
+java_library(
+ name = "fleet-aws-sdk-kotlin-s3-jvm",
+ exports = [
+ ":aws_sdk_kotlin-s3-jvm-1_0_69_http_import",
+ ":aws_sdk_kotlin-aws-config-jvm-1_0_69_http_import",
+ ":aws_smithy_kotlin-serde-json-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http_import",
+ ":aws_sdk_kotlin-aws-core-jvm-1_0_69_http_import",
+ ":aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http_import",
+ ":aws_smithy_kotlin-http-client-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-runtime-core-jvm-1_0_16_http_import",
+ ":com_squareup_okio-okio-jvm-3_6_0_http_import",
+ ":aws_smithy_kotlin-smithy-client-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http_import",
+ ":aws_sdk_kotlin-aws-http-jvm-1_0_69_http_import",
+ ":aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-http-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-http-auth-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http_import",
+ ":com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http_import",
+ ":com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http_import",
+ ":aws_smithy_kotlin-identity-api-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-serde-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-serde-xml-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http_import",
+ ":aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http_import",
+ ],
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "aws_sdk_kotlin-s3-jvm-1_0_69_http_import",
+ jar = "@aws_sdk_kotlin-s3-jvm-1_0_69_http//file",
+ source_jar = "@aws_sdk_kotlin-s3-jvm-1_0_69_http//file"
+)
+
+jvm_import(
+ name = "aws_sdk_kotlin-aws-config-jvm-1_0_69_http_import",
+ jar = "@aws_sdk_kotlin-aws-config-jvm-1_0_69_http//file",
+ source_jar = "@aws_sdk_kotlin-aws-config-jvm-1_0_69_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-serde-json-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-serde-json-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-serde-json-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_sdk_kotlin-aws-core-jvm-1_0_69_http_import",
+ jar = "@aws_sdk_kotlin-aws-core-jvm-1_0_69_http//file",
+ source_jar = "@aws_sdk_kotlin-aws-core-jvm-1_0_69_http//file"
+)
+
+jvm_import(
+ name = "aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http_import",
+ jar = "@aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http//file",
+ source_jar = "@aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-client-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-client-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-client-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-runtime-core-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-runtime-core-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-runtime-core-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "com_squareup_okio-okio-jvm-3_6_0_http_import",
+ jar = "@com_squareup_okio-okio-jvm-3_6_0_http//file",
+ source_jar = "@com_squareup_okio-okio-jvm-3_6_0_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-smithy-client-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-smithy-client-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-smithy-client-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_sdk_kotlin-aws-http-jvm-1_0_69_http_import",
+ jar = "@aws_sdk_kotlin-aws-http-jvm-1_0_69_http//file",
+ source_jar = "@aws_sdk_kotlin-aws-http-jvm-1_0_69_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-auth-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-auth-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-auth-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http_import",
+ jar = "@com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http//file",
+ source_jar = "@com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http//file"
+)
+
+jvm_import(
+ name = "com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http_import",
+ jar = "@com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http//file",
+ source_jar = "@com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-identity-api-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-identity-api-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-identity-api-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-serde-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-serde-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-serde-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-serde-xml-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-serde-xml-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-serde-xml-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http//file"
+)
+
+jvm_import(
+ name = "aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http_import",
+ jar = "@aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http//file",
+ source_jar = "@aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http//file"
+)
+
+java_library(
+ name = "fleet-clikt",
+ exports = [
+ ":com_github_ajalt_clikt-clikt-jvm-5_0_3_http_import",
+ ":com_github_ajalt_mordant-mordant-jvm-3_0_1_http_import",
+ ":com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http_import",
+ ":com_github_ajalt_colormath-colormath-jvm-3_6_0_http_import",
+ ],
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "com_github_ajalt_clikt-clikt-jvm-5_0_3_http_import",
+ jar = "@com_github_ajalt_clikt-clikt-jvm-5_0_3_http//file",
+ source_jar = "@com_github_ajalt_clikt-clikt-jvm-5_0_3_http//file"
+)
+
+jvm_import(
+ name = "com_github_ajalt_mordant-mordant-jvm-3_0_1_http_import",
+ jar = "@com_github_ajalt_mordant-mordant-jvm-3_0_1_http//file",
+ source_jar = "@com_github_ajalt_mordant-mordant-jvm-3_0_1_http//file"
+)
+
+jvm_import(
+ name = "com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http_import",
+ jar = "@com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http//file",
+ source_jar = "@com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http//file"
+)
+
+jvm_import(
+ name = "com_github_ajalt_colormath-colormath-jvm-3_6_0_http_import",
+ jar = "@com_github_ajalt_colormath-colormath-jvm-3_6_0_http//file",
+ source_jar = "@com_github_ajalt_colormath-colormath-jvm-3_6_0_http//file"
+)
+
+jvm_import(
+ name = "fleet-clikt-core",
+ jar = "@com_github_ajalt_clikt-clikt-core-jvm-5_0_3_http//file",
+ source_jar = "@com_github_ajalt_clikt-clikt-core-jvm-5_0_3_http//file",
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "fleet-github-luben-zstd-jni",
+ jar = "@com_github_luben-zstd-jni-1_5_5-11_http//file",
+ source_jar = "@com_github_luben-zstd-jni-1_5_5-11_http//file",
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "fleet-io-github-pdvrieze-xmlutil-core-jvm",
+ jar = "@io_github_pdvrieze_xmlutil-core-jvm-0_86_3_http//file",
+ source_jar = "@io_github_pdvrieze_xmlutil-core-jvm-0_86_3_http//file",
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "fleet-io-github-pdvrieze-xmlutil-serialization-jvm",
+ jar = "@io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3_http//file",
+ source_jar = "@io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3_http//file",
+ visibility = ["//visibility:public"]
+)
+
jvm_import(
name = "fleet-io-ktor-client-cio",
jar = "@io_ktor-ktor-client-cio-jvm-3_1_3_http//file",
@@ -1472,6 +1766,20 @@ jvm_import(
visibility = ["//visibility:public"]
)
+jvm_import(
+ name = "fleet-io-ktor-client-logging",
+ jar = "@io_ktor-ktor-client-logging-jvm-3_1_3_http//file",
+ source_jar = "@io_ktor-ktor-client-logging-jvm-3_1_3_http//file",
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "fleet-io-ktor-client-mock",
+ jar = "@io_ktor-ktor-client-mock-jvm-3_1_3_http//file",
+ source_jar = "@io_ktor-ktor-client-mock-jvm-3_1_3_http//file",
+ visibility = ["//visibility:public"]
+)
+
jvm_import(
name = "fleet-io-ktor-events",
jar = "@io_ktor-ktor-events-jvm-3_1_3_http//file",
@@ -1570,6 +1878,55 @@ jvm_import(
visibility = ["//visibility:public"]
)
+java_library(
+ name = "fleet-jetbrains-intellij-idea-community-build-zip",
+ exports = [
+ ":com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http_import",
+ ":io_netty-netty-buffer-4_2_0_RC2_http_import",
+ ":io_netty-netty-common-4_2_0_RC2_http_import",
+ ":org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http_import",
+ ":com_dynatrace_hash4j-hash4j-0_20_0_http_import",
+ ],
+ visibility = ["//visibility:public"]
+)
+
+jvm_import(
+ name = "com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http_import",
+ jar = "@com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http//file",
+ source_jar = "@com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http//file"
+)
+
+jvm_import(
+ name = "io_netty-netty-buffer-4_2_0_RC2_http_import",
+ jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file",
+ source_jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file"
+)
+
+jvm_import(
+ name = "io_netty-netty-common-4_2_0_RC2_http_import",
+ jar = "@io_netty-netty-common-4_2_0_RC2_http//file",
+ source_jar = "@io_netty-netty-common-4_2_0_RC2_http//file"
+)
+
+jvm_import(
+ name = "org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http_import",
+ jar = "@org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http//file",
+ source_jar = "@org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http//file"
+)
+
+jvm_import(
+ name = "com_dynatrace_hash4j-hash4j-0_20_0_http_import",
+ jar = "@com_dynatrace_hash4j-hash4j-0_20_0_http//file",
+ source_jar = "@com_dynatrace_hash4j-hash4j-0_20_0_http//file"
+)
+
+jvm_import(
+ name = "fleet-jetbrains-intellij-platform-util-zip-squashed",
+ jar = "@com_jetbrains_intellij_platform-util-zip-squashed-252_16432_http//file",
+ source_jar = "@com_jetbrains_intellij_platform-util-zip-squashed-252_16432_http//file",
+ visibility = ["//visibility:public"]
+)
+
jvm_import(
name = "fleet-org-fusesource-jansi",
jar = "@org_fusesource_jansi-jansi-2_4_1_http//file",
@@ -1577,6 +1934,13 @@ jvm_import(
visibility = ["//visibility:public"]
)
+jvm_import(
+ name = "fleet-tukaani-xz",
+ jar = "@org_tukaani-xz-1_9_http//file",
+ source_jar = "@org_tukaani-xz-1_9_http//file",
+ visibility = ["//visibility:public"]
+)
+
jvm_import(
name = "fleet-util-modules-jetbrains-intellij-platform-util-zip-squashed",
jar = "@com_jetbrains_intellij_platform-util-zip-squashed-252_16432_http//file",
@@ -8069,18 +8433,6 @@ java_library(
visibility = ["//visibility:public"]
)
-jvm_import(
- name = "io_netty-netty-buffer-4_2_0_RC2_http_import",
- jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file",
- source_jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file"
-)
-
-jvm_import(
- name = "io_netty-netty-common-4_2_0_RC2_http_import",
- jar = "@io_netty-netty-common-4_2_0_RC2_http//file",
- source_jar = "@io_netty-netty-common-4_2_0_RC2_http//file"
-)
-
jvm_import(
name = "netty-codec-compression",
jar = "@io_netty-netty-codec-compression-4_2_0_RC2_http//file",
diff --git a/lib/MODULE.bazel b/lib/MODULE.bazel
index 78d5bc139398..729c460690db 100644
--- a/lib/MODULE.bazel
+++ b/lib/MODULE.bazel
@@ -1784,6 +1784,566 @@ http_file(
downloaded_file_path = "file-prediction-model-0.3.0-sources.jar"
)
+http_file(
+ name = "aws_sdk_kotlin-s3-jvm-1_0_69_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/s3-jvm/1.0.69/s3-jvm-1.0.69.jar",
+ sha256 = "3d6cd0bf48c65e965903fa4720d713badeeaa5433bd7b278075c484e3a5709a8",
+ downloaded_file_path = "s3-jvm-1.0.69.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-config-jvm-1_0_69_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-config-jvm/1.0.69/aws-config-jvm-1.0.69.jar",
+ sha256 = "877ffeaa6fc96192da7383b3c3e16717cb52504f488b80937d796eb875e5565a",
+ downloaded_file_path = "aws-config-jvm-1.0.69.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-json-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-json-jvm/1.0.16/serde-json-jvm-1.0.16.jar",
+ sha256 = "2f12ed8fbd53e91cb3b2a163b5d06ae2c39f538bd74f23a678889f5be02e8675",
+ downloaded_file_path = "serde-json-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-form-url-jvm/1.0.16/serde-form-url-jvm-1.0.16.jar",
+ sha256 = "198b9b1ec6c33da8c460e6a2e0859d1308f1fcb3f7d4138100f7dd75207edd80",
+ downloaded_file_path = "serde-form-url-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-json-protocols-jvm/1.0.16/aws-json-protocols-jvm-1.0.16.jar",
+ sha256 = "633ccbae483c99b27e311a6a70157cfc0dfa19f8eb3ba263198f40f87266c09c",
+ downloaded_file_path = "aws-json-protocols-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-core-jvm-1_0_69_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-core-jvm/1.0.69/aws-core-jvm-1.0.69.jar",
+ sha256 = "e738ebef7be523c4e1940f5e5b2390ba853d4333f547038b2b31fb3964e99933",
+ downloaded_file_path = "aws-core-jvm-1.0.69.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-endpoint-jvm/1.0.69/aws-endpoint-jvm-1.0.69.jar",
+ sha256 = "d3f30ef5e3b11b358f28f42422226585b72e4a11bf13b1fc3411962dcfde64f3",
+ downloaded_file_path = "aws-endpoint-jvm-1.0.69.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-client-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-jvm/1.0.16/http-client-jvm-1.0.16.jar",
+ sha256 = "aa584dcd05862a8bbe7eb7db73959d93ca6b527cc44536b7d10fcda733411d4b",
+ downloaded_file_path = "http-client-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-runtime-core-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/runtime-core-jvm/1.0.16/runtime-core-jvm-1.0.16.jar",
+ sha256 = "8a2323e7d08ff07d2f869e4bcae834773ce0a0e8704cd7404c9625992a748662",
+ downloaded_file_path = "runtime-core-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "com_squareup_okio-okio-jvm-3_6_0_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okio/okio-jvm/3.6.0/okio-jvm-3.6.0.jar",
+ sha256 = "67543f0736fc422ae927ed0e504b98bc5e269fda0d3500579337cb713da28412",
+ downloaded_file_path = "okio-jvm-3.6.0.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-smithy-client-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/smithy-client-jvm/1.0.16/smithy-client-jvm-1.0.16.jar",
+ sha256 = "ce382d40f998d8dd11008967c3bf4cd51c47182512b84b5f1d48e48d2a3f9770",
+ downloaded_file_path = "smithy-client-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-api-jvm/1.0.16/telemetry-api-jvm-1.0.16.jar",
+ sha256 = "508bde368d4b174402aeeccec68c7e29ad9349a953b1efab75ebeaa6c0a21f0e",
+ downloaded_file_path = "telemetry-api-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-credentials-jvm/1.0.16/aws-credentials-jvm-1.0.16.jar",
+ sha256 = "01489854c4f9494de6cd5446df8e3c694ad3f6582c5369b9578eb18754f1720c",
+ downloaded_file_path = "aws-credentials-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-event-stream-jvm/1.0.16/aws-event-stream-jvm-1.0.16.jar",
+ sha256 = "71fe60ba15b5f9c43f5ee1da0edc13fd98f2a85dbf8c9dcafc666bc1c7e668e7",
+ downloaded_file_path = "aws-event-stream-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-http-jvm-1_0_69_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-http-jvm/1.0.69/aws-http-jvm-1.0.69.jar",
+ sha256 = "6b7584f233e0715f5394edfbfd5536a480113efe99f3d49bfd58d2395072177a",
+ downloaded_file_path = "aws-http-jvm-1.0.69.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-protocol-core-jvm/1.0.16/aws-protocol-core-jvm-1.0.16.jar",
+ sha256 = "6d7028c6e695b872e0ba91679af4dbbabf8e1bfbd6f7fb52be30bb1e398d595d",
+ downloaded_file_path = "aws-protocol-core-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-common-jvm/1.0.16/aws-signing-common-jvm-1.0.16.jar",
+ sha256 = "30cf05f9f7ec7d6676031ee2b0c8f4b33b107a93a179ca931d57baa983de70a3",
+ downloaded_file_path = "aws-signing-common-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-default-jvm/1.0.16/aws-signing-default-jvm-1.0.16.jar",
+ sha256 = "dd0616302ce4a3fc1c03326bad37e5cf3509377692b6aca5ea17d581ad582d62",
+ downloaded_file_path = "aws-signing-default-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-xml-protocols-jvm/1.0.16/aws-xml-protocols-jvm-1.0.16.jar",
+ sha256 = "94cc5ebcb2d819ebb8c628f64ffa352426f23b3b422834bfa8199cf497a1365d",
+ downloaded_file_path = "aws-xml-protocols-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-jvm/1.0.16/http-jvm-1.0.16.jar",
+ sha256 = "3f0d5d4e67a3097b56b32fa7b26589da9d6bd0968693b395ad1c8f49f60dbdfd",
+ downloaded_file_path = "http-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-auth-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-jvm/1.0.16/http-auth-jvm-1.0.16.jar",
+ sha256 = "79ed729b7e134609310de7d691b1b80870f467a95bc84f2f6bbf3e43ae83de85",
+ downloaded_file_path = "http-auth-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-api-jvm/1.0.16/http-auth-api-jvm-1.0.16.jar",
+ sha256 = "9664c8d36468c1f575b356f04025ce17417e584a0bb19b640b115b10a8dce72c",
+ downloaded_file_path = "http-auth-api-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-aws-jvm/1.0.16/http-auth-aws-jvm-1.0.16.jar",
+ sha256 = "31e3811fd85ba7fcba38a4067d63af8c13c1e05ae7062639fd8c954520315f51",
+ downloaded_file_path = "http-auth-aws-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-default-jvm/1.0.16/http-client-engine-default-jvm-1.0.16.jar",
+ sha256 = "bbbf92cbc8df78a8fa20f0b35613e8d41024ee6525b538ebda280917e1d7e812",
+ downloaded_file_path = "http-client-engine-default-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-okhttp-jvm/1.0.16/http-client-engine-okhttp-jvm-1.0.16.jar",
+ sha256 = "4bfb91b4c6373ac2b1d94a085c69935ca1b56b7b27b21c5228c5bc5da6f7e2dc",
+ downloaded_file_path = "http-client-engine-okhttp-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-jvm/5.0.0-alpha.11/okhttp-jvm-5.0.0-alpha.11.jar",
+ sha256 = "506a1d3aba9f99b55570c7f8813cd79a44946531f29050d5b938474efdb4cf80",
+ downloaded_file_path = "okhttp-jvm-5.0.0-alpha.11.jar"
+)
+
+http_file(
+ name = "com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-coroutines-jvm/5.0.0-alpha.11/okhttp-coroutines-jvm-5.0.0-alpha.11.jar",
+ sha256 = "1452873e0cc9637b7e1a717f2cca0cbccebd00c05aaaf67b8adb06777bf4f461",
+ downloaded_file_path = "okhttp-coroutines-jvm-5.0.0-alpha.11.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-identity-api-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/identity-api-jvm/1.0.16/identity-api-jvm-1.0.16.jar",
+ sha256 = "be3f9bd5ebfb83aa353f407a4389f7b3454a6945f82cea1d41e370aeedae71cd",
+ downloaded_file_path = "identity-api-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-jvm/1.0.16/serde-jvm-1.0.16.jar",
+ sha256 = "c017ee899656cf5abf22c3793603cb33ae35f0a805b9fa0707f8146a73a14a7f",
+ downloaded_file_path = "serde-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-xml-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-xml-jvm/1.0.16/serde-xml-jvm-1.0.16.jar",
+ sha256 = "72c8559a241af92ce122c0df0dc3749d638d616ebd5da3ba3749a95abca3b349",
+ downloaded_file_path = "serde-xml-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-defaults-jvm/1.0.16/telemetry-defaults-jvm-1.0.16.jar",
+ sha256 = "4343154bb1b0a282d86b627932a465722eeda3cccd6cc4986fc5e5c4013bfef4",
+ downloaded_file_path = "telemetry-defaults-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/logging-slf4j2-jvm/1.0.16/logging-slf4j2-jvm-1.0.16.jar",
+ sha256 = "66c0700ca0c27046b0b12e6cf4c4da423800b88c1c72b2709cd5cbc3e0f1eb64",
+ downloaded_file_path = "logging-slf4j2-jvm-1.0.16.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-s3-jvm-1_0_69-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/s3-jvm/1.0.69/s3-jvm-1.0.69-sources.jar",
+ sha256 = "c7df05b60aec94b092cff2959f1c19d649c169c79c59146c14c69ab6edd451b8",
+ downloaded_file_path = "s3-jvm-1.0.69-sources.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-config-jvm-1_0_69-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-config-jvm/1.0.69/aws-config-jvm-1.0.69-sources.jar",
+ sha256 = "1d4ab3c4eb73f800a059d65f7e7983969a1fbccc2ebc02f999aa41f54fdb314b",
+ downloaded_file_path = "aws-config-jvm-1.0.69-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-json-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-json-jvm/1.0.16/serde-json-jvm-1.0.16-sources.jar",
+ sha256 = "63312de11976e37ccd4a8ccc51c6db0a51220d548d8d25e2d568b2cd36d7b5b7",
+ downloaded_file_path = "serde-json-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-form-url-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-form-url-jvm/1.0.16/serde-form-url-jvm-1.0.16-sources.jar",
+ sha256 = "2fa020f36f3e20da2445a4fe9a7081da9fb9c5099781c9110de5b62bbe291422",
+ downloaded_file_path = "serde-form-url-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-json-protocols-jvm/1.0.16/aws-json-protocols-jvm-1.0.16-sources.jar",
+ sha256 = "82b5eb3de2edbc38c42558490b612b2c5c2e69ad47183a812c26f0423a6f7f6f",
+ downloaded_file_path = "aws-json-protocols-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-core-jvm-1_0_69-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-core-jvm/1.0.69/aws-core-jvm-1.0.69-sources.jar",
+ sha256 = "2a0fd31b290758e74e725b2d42b7c071cfa458aed45aee2a77ec8601cb434cca",
+ downloaded_file_path = "aws-core-jvm-1.0.69-sources.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-endpoint-jvm-1_0_69-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-endpoint-jvm/1.0.69/aws-endpoint-jvm-1.0.69-sources.jar",
+ sha256 = "1bf9476e19cf4f20f4ede1bb004a819825dbda051c6ac00b486720570b8b77e1",
+ downloaded_file_path = "aws-endpoint-jvm-1.0.69-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-client-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-jvm/1.0.16/http-client-jvm-1.0.16-sources.jar",
+ sha256 = "1a21fe3e336c009cb1334993eab2c0cc97c0412775df55a139c8a11c9ca7afcd",
+ downloaded_file_path = "http-client-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-runtime-core-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/runtime-core-jvm/1.0.16/runtime-core-jvm-1.0.16-sources.jar",
+ sha256 = "6db2e152b1b92fc66c381add6e3c378cdcc1bef41cc0c1d8eee78fecd01bba16",
+ downloaded_file_path = "runtime-core-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "com_squareup_okio-okio-jvm-3_6_0-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okio/okio-jvm/3.6.0/okio-jvm-3.6.0-sources.jar",
+ sha256 = "870a42a7b468af8fd24d67b7b62e5861aab398c56f15a80b10642e7001564e2f",
+ downloaded_file_path = "okio-jvm-3.6.0-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-smithy-client-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/smithy-client-jvm/1.0.16/smithy-client-jvm-1.0.16-sources.jar",
+ sha256 = "4f326ea673b6cd99b33739e6fb4df8ae80bd28def81cc3e1641f33c44c4d6f54",
+ downloaded_file_path = "smithy-client-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-telemetry-api-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-api-jvm/1.0.16/telemetry-api-jvm-1.0.16-sources.jar",
+ sha256 = "b5a2a420df517652ada3da3de5534e836d78633ae528d9b6aa3fd32b0bad4668",
+ downloaded_file_path = "telemetry-api-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-credentials-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-credentials-jvm/1.0.16/aws-credentials-jvm-1.0.16-sources.jar",
+ sha256 = "ec3a34bfb8b38e620233d7bf54e66e87e22644f74d884635aa2ed942442ea945",
+ downloaded_file_path = "aws-credentials-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-event-stream-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-event-stream-jvm/1.0.16/aws-event-stream-jvm-1.0.16-sources.jar",
+ sha256 = "2b732ea574cab4c437f717873193aeade7482ed7e6f024d1a368549cf8cb0ae1",
+ downloaded_file_path = "aws-event-stream-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_sdk_kotlin-aws-http-jvm-1_0_69-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-http-jvm/1.0.69/aws-http-jvm-1.0.69-sources.jar",
+ sha256 = "1c6858e4f5e036a118e5cf54d7b72990a968252141e65da489955cec0103919b",
+ downloaded_file_path = "aws-http-jvm-1.0.69-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-protocol-core-jvm/1.0.16/aws-protocol-core-jvm-1.0.16-sources.jar",
+ sha256 = "db32fffaf7bda060bde5623ac91da87888af287629c5a44b34026f86a4ed7a59",
+ downloaded_file_path = "aws-protocol-core-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-signing-common-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-common-jvm/1.0.16/aws-signing-common-jvm-1.0.16-sources.jar",
+ sha256 = "0e43d7624b1d6f9aa0700f4cbed81951002f53a5138d412e84aa373eb57750b5",
+ downloaded_file_path = "aws-signing-common-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-signing-default-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-default-jvm/1.0.16/aws-signing-default-jvm-1.0.16-sources.jar",
+ sha256 = "d8c2708cf555ce2925aa60a51540bf1480440e8f991bcedefd62fd6f07c48d23",
+ downloaded_file_path = "aws-signing-default-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-xml-protocols-jvm/1.0.16/aws-xml-protocols-jvm-1.0.16-sources.jar",
+ sha256 = "249a38bd14fae9257d1424311e94aaf01c552cee4bf3d15c482efb999d8f99b9",
+ downloaded_file_path = "aws-xml-protocols-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-jvm/1.0.16/http-jvm-1.0.16-sources.jar",
+ sha256 = "2c8e0881e79988310ad402a79ef9dcf604d68b675bdd9056343a5133e8edf191",
+ downloaded_file_path = "http-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-auth-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-jvm/1.0.16/http-auth-jvm-1.0.16-sources.jar",
+ sha256 = "78222f0442031fa2201d512885279b027385bd89e7760006ee27b7d825c2913c",
+ downloaded_file_path = "http-auth-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-auth-api-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-api-jvm/1.0.16/http-auth-api-jvm-1.0.16-sources.jar",
+ sha256 = "41f50535e53ddb3da31de70f65d80c8c3680055abc3de3336ff0ddbe9dab95ca",
+ downloaded_file_path = "http-auth-api-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-auth-aws-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-aws-jvm/1.0.16/http-auth-aws-jvm-1.0.16-sources.jar",
+ sha256 = "318701ebe5a4cb2b9ef4602ee940e1011e3385629a3ab2b96dfbce8fa2b9319a",
+ downloaded_file_path = "http-auth-aws-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-default-jvm/1.0.16/http-client-engine-default-jvm-1.0.16-sources.jar",
+ sha256 = "430597b6f8f43bdb1ae5987d564b156ea8a5e00658793db976e16c472f219320",
+ downloaded_file_path = "http-client-engine-default-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-okhttp-jvm/1.0.16/http-client-engine-okhttp-jvm-1.0.16-sources.jar",
+ sha256 = "bbbcd3c14a7d4939f270b68547d90755b56c9b66f6d715964ab3bdcb0f17d252",
+ downloaded_file_path = "http-client-engine-okhttp-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-jvm/5.0.0-alpha.11/okhttp-jvm-5.0.0-alpha.11-sources.jar",
+ sha256 = "93da6c712a017eb4625fd9093bb1305c670583cdaa2279cfe79f1e308b544e1e",
+ downloaded_file_path = "okhttp-jvm-5.0.0-alpha.11-sources.jar"
+)
+
+http_file(
+ name = "com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-coroutines-jvm/5.0.0-alpha.11/okhttp-coroutines-jvm-5.0.0-alpha.11-sources.jar",
+ sha256 = "a97b8fb6bc30483991426ec3ec223c8d0f8994fa10980448c404077a13be9ec4",
+ downloaded_file_path = "okhttp-coroutines-jvm-5.0.0-alpha.11-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-identity-api-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/identity-api-jvm/1.0.16/identity-api-jvm-1.0.16-sources.jar",
+ sha256 = "a2cafc146661a3f9dd887cdd03cd5b39acd8babd5dfcf7772e226b35ecd103f5",
+ downloaded_file_path = "identity-api-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-jvm/1.0.16/serde-jvm-1.0.16-sources.jar",
+ sha256 = "303ce8427e5b0b4037f7cfa6b0e75470cb6c8c46166bb73c0dc628a988acee18",
+ downloaded_file_path = "serde-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-serde-xml-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-xml-jvm/1.0.16/serde-xml-jvm-1.0.16-sources.jar",
+ sha256 = "fbd7cf3d16fe774af863d6ad9a460a2fef5c4c338b771f77e308aa50cba2df37",
+ downloaded_file_path = "serde-xml-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-defaults-jvm/1.0.16/telemetry-defaults-jvm-1.0.16-sources.jar",
+ sha256 = "900d1c6c0274a713fed0c03b02f278c32a4af049b6922f6d85db4cc82f5f2207",
+ downloaded_file_path = "telemetry-defaults-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/logging-slf4j2-jvm/1.0.16/logging-slf4j2-jvm-1.0.16-sources.jar",
+ sha256 = "0bac498fe98ab2e4f25af1551a0df700e03ff628d0e909e41ceedc935591bb57",
+ downloaded_file_path = "logging-slf4j2-jvm-1.0.16-sources.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_clikt-clikt-jvm-5_0_3_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-jvm/5.0.3/clikt-jvm-5.0.3.jar",
+ sha256 = "9cd19e04782f461dffeb183d17e576984febb7934932cb406646ac41e428863b",
+ downloaded_file_path = "clikt-jvm-5.0.3.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_mordant-mordant-jvm-3_0_1_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-jvm/3.0.1/mordant-jvm-3.0.1.jar",
+ sha256 = "9ed3b976fcccc78da746d49866fa8ebb8f10530a93c544ea0420259a607dd95e",
+ downloaded_file_path = "mordant-jvm-3.0.1.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-core-jvm/3.0.1/mordant-core-jvm-3.0.1.jar",
+ sha256 = "9cf9b46d1f49f2d6cf2635462b29dc59e0c29b1fb2f085b3312888bbe9c7cd31",
+ downloaded_file_path = "mordant-core-jvm-3.0.1.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_colormath-colormath-jvm-3_6_0_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/colormath/colormath-jvm/3.6.0/colormath-jvm-3.6.0.jar",
+ sha256 = "59f741adfe62053066782d8b1a45afd06685a4bc64b33277e54876b993ed885c",
+ downloaded_file_path = "colormath-jvm-3.6.0.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_clikt-clikt-jvm-5_0_3-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-jvm/5.0.3/clikt-jvm-5.0.3-sources.jar",
+ sha256 = "42e19164ac1058a8f86f5929bad928d749e96376c5e58499924f441fff67f714",
+ downloaded_file_path = "clikt-jvm-5.0.3-sources.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_mordant-mordant-jvm-3_0_1-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-jvm/3.0.1/mordant-jvm-3.0.1-sources.jar",
+ sha256 = "3aa71b91497d7a6e10947949ee91039186c181fda3cc5418e49ccd5cbee98b62",
+ downloaded_file_path = "mordant-jvm-3.0.1-sources.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_mordant-mordant-core-jvm-3_0_1-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-core-jvm/3.0.1/mordant-core-jvm-3.0.1-sources.jar",
+ sha256 = "96c7a7138bce85cd7aa4d3359b0febd4f907ab4692e9434ac2a9cf364c2f7b09",
+ downloaded_file_path = "mordant-core-jvm-3.0.1-sources.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_colormath-colormath-jvm-3_6_0-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/colormath/colormath-jvm/3.6.0/colormath-jvm-3.6.0-sources.jar",
+ sha256 = "cd4baaaa928f21ec45b0cbf78f7071a94be9830bf0e3ed8f320e851f47b32864",
+ downloaded_file_path = "colormath-jvm-3.6.0-sources.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_clikt-clikt-core-jvm-5_0_3_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-core-jvm/5.0.3/clikt-core-jvm-5.0.3.jar",
+ sha256 = "69cba94a2291f74b78c88239cd8b67eaf3f08e0dad297749abe6477b3e9b81b1",
+ downloaded_file_path = "clikt-core-jvm-5.0.3.jar"
+)
+
+http_file(
+ name = "com_github_ajalt_clikt-clikt-core-jvm-5_0_3-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-core-jvm/5.0.3/clikt-core-jvm-5.0.3-sources.jar",
+ sha256 = "517d028e3b038ae9fa2cac9d536d1ddc78c35a463f3c65f524503c6f6699e4a6",
+ downloaded_file_path = "clikt-core-jvm-5.0.3-sources.jar"
+)
+
+http_file(
+ name = "com_github_luben-zstd-jni-1_5_5-11_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/luben/zstd-jni/1.5.5-11/zstd-jni-1.5.5-11.jar",
+ sha256 = "d75b2ced6059f81ad23e021c554259b906b6c4f2991cb772409827569ead4c1a",
+ downloaded_file_path = "zstd-jni-1.5.5-11.jar"
+)
+
+http_file(
+ name = "com_github_luben-zstd-jni-1_5_5-11-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/luben/zstd-jni/1.5.5-11/zstd-jni-1.5.5-11-sources.jar",
+ sha256 = "658dc0dc9832114d6e8665eb297ae432ad90ec1419fadbc97f974179137bb8c2",
+ downloaded_file_path = "zstd-jni-1.5.5-11-sources.jar"
+)
+
+http_file(
+ name = "io_github_pdvrieze_xmlutil-core-jvm-0_86_3_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/core-jvm/0.86.3/core-jvm-0.86.3.jar",
+ sha256 = "91527d86fea04bd6183d140a09f10dab2dea727af12d27d9165ee342ea3d0ede",
+ downloaded_file_path = "core-jvm-0.86.3.jar"
+)
+
+http_file(
+ name = "io_github_pdvrieze_xmlutil-core-jvm-0_86_3-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/core-jvm/0.86.3/core-jvm-0.86.3-sources.jar",
+ sha256 = "ef68ec64bfe82cd284b634edd19ec3630746644af53184ba70cbb4878002fdcb",
+ downloaded_file_path = "core-jvm-0.86.3-sources.jar"
+)
+
+http_file(
+ name = "io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/serialization-jvm/0.86.3/serialization-jvm-0.86.3.jar",
+ sha256 = "9ce273dcb860b92a5bf2ec36ad1e2a11b41aed89c6c984ca73e87eff5eda1bd0",
+ downloaded_file_path = "serialization-jvm-0.86.3.jar"
+)
+
+http_file(
+ name = "io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/serialization-jvm/0.86.3/serialization-jvm-0.86.3-sources.jar",
+ sha256 = "6aa7410dead13532a0d254b5ce98d7e5db19f06743014ba76f084f86c2c0b561",
+ downloaded_file_path = "serialization-jvm-0.86.3-sources.jar"
+)
+
http_file(
name = "io_ktor-ktor-client-cio-jvm-3_1_3_http",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-cio-jvm/3.1.3/ktor-client-cio-jvm-3.1.3.jar",
@@ -1812,6 +2372,34 @@ http_file(
downloaded_file_path = "ktor-client-core-jvm-3.1.3-sources.jar"
)
+http_file(
+ name = "io_ktor-ktor-client-logging-jvm-3_1_3_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3.jar",
+ sha256 = "ecabf013b137f1a69c39cffeae1ba9e365659357b21688f1876ee6cf50fd00b9",
+ downloaded_file_path = "ktor-client-logging-jvm-3.1.3.jar"
+)
+
+http_file(
+ name = "io_ktor-ktor-client-logging-jvm-3_1_3-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3-sources.jar",
+ sha256 = "4ac316b70a1fa7dd06c17926de41340d24c045b3a96e87139f5a7debb6d78a9a",
+ downloaded_file_path = "ktor-client-logging-jvm-3.1.3-sources.jar"
+)
+
+http_file(
+ name = "io_ktor-ktor-client-mock-jvm-3_1_3_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3.jar",
+ sha256 = "2ae0d57bb9fceb8ee2aeda6536acc6f3e1de355984b4c9da720566b85f1525e8",
+ downloaded_file_path = "ktor-client-mock-jvm-3.1.3.jar"
+)
+
+http_file(
+ name = "io_ktor-ktor-client-mock-jvm-3_1_3-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3-sources.jar",
+ sha256 = "8c47ed0e7a044a368ee139db4d9e372ab295931b8091fcc6027c6124de8eb7ef",
+ downloaded_file_path = "ktor-client-mock-jvm-3.1.3-sources.jar"
+)
+
http_file(
name = "io_ktor-ktor-events-jvm-3_1_3_http",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-events-jvm/3.1.3/ktor-events-jvm-3.1.3.jar",
@@ -2009,17 +2597,73 @@ http_file(
)
http_file(
- name = "org_fusesource_jansi-jansi-2_4_1_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1.jar",
- sha256 = "2e5e775a9dc58ffa6bbd6aa6f099d62f8b62dcdeb4c3c3bbbe5cf2301bc2dcc1",
- downloaded_file_path = "jansi-2.4.1.jar"
+ name = "com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http",
+ url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/com/jetbrains/intellij/idea/idea-community-build-zip/252.11244/idea-community-build-zip-252.11244.jar",
+ sha256 = "c86d2bfa23d5d23f916d3574591a166b3df259bea6c23d39abea597b34ba022f",
+ downloaded_file_path = "idea-community-build-zip-252.11244.jar"
)
http_file(
- name = "org_fusesource_jansi-jansi-2_4_1-sources_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1-sources.jar",
- sha256 = "f707511567a13ebf8c51164133770eb5a8e023e1d391bfbc6e7a0591c71729b8",
- downloaded_file_path = "jansi-2.4.1-sources.jar"
+ name = "io_netty-netty-buffer-4_2_0_RC2_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2.jar",
+ sha256 = "74d953cec00048b892112f9602fc7b1113ea55337dc1ede32e449efcb2b231c7",
+ downloaded_file_path = "netty-buffer-4.2.0.RC2.jar"
+)
+
+http_file(
+ name = "io_netty-netty-common-4_2_0_RC2_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2.jar",
+ sha256 = "cc65afe17daf2f9380d3c64460f5b10ec279f98b9461fbf0ccf62b3cb3ee6c37",
+ downloaded_file_path = "netty-common-4.2.0.RC2.jar"
+)
+
+http_file(
+ name = "org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http",
+ url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/org/jetbrains/intellij/deps/fastutil/intellij-deps-fastutil/8.5.15-jb1/intellij-deps-fastutil-8.5.15-jb1.jar",
+ sha256 = "5a0d7557d494732a6165e4188eb11f10faca196350cad04d9950abd83e6dd148",
+ downloaded_file_path = "intellij-deps-fastutil-8.5.15-jb1.jar"
+)
+
+http_file(
+ name = "com_dynatrace_hash4j-hash4j-0_20_0_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/dynatrace/hash4j/hash4j/0.20.0/hash4j-0.20.0.jar",
+ sha256 = "0181873ba85684d119134f5598432f5b63109a03adc455767a1500b2145df8a2",
+ downloaded_file_path = "hash4j-0.20.0.jar"
+)
+
+http_file(
+ name = "com_jetbrains_intellij_idea-idea-community-build-zip-252_11244-sources_http",
+ url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/com/jetbrains/intellij/idea/idea-community-build-zip/252.11244/idea-community-build-zip-252.11244-sources.jar",
+ sha256 = "ca10b44853d641b0bfeb5ed57cca50d16892735821542bd7fd45e3b33314c566",
+ downloaded_file_path = "idea-community-build-zip-252.11244-sources.jar"
+)
+
+http_file(
+ name = "io_netty-netty-buffer-4_2_0_RC2-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2-sources.jar",
+ sha256 = "e483fa26b8035c8c2a2306371a19e821b1c63a08465c31b9711e6db79e691715",
+ downloaded_file_path = "netty-buffer-4.2.0.RC2-sources.jar"
+)
+
+http_file(
+ name = "io_netty-netty-common-4_2_0_RC2-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2-sources.jar",
+ sha256 = "5b3516f7d3f5d4884af89bd632713d779aba17b5a8eabc6366777201878d7e27",
+ downloaded_file_path = "netty-common-4.2.0.RC2-sources.jar"
+)
+
+http_file(
+ name = "org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1-sources_http",
+ url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/org/jetbrains/intellij/deps/fastutil/intellij-deps-fastutil/8.5.15-jb1/intellij-deps-fastutil-8.5.15-jb1-sources.jar",
+ sha256 = "57d0e59cd6f6bd931d893ab0089bdcb13aee0ed78f1f8e85d88bc55b89815211",
+ downloaded_file_path = "intellij-deps-fastutil-8.5.15-jb1-sources.jar"
+)
+
+http_file(
+ name = "com_dynatrace_hash4j-hash4j-0_20_0-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/dynatrace/hash4j/hash4j/0.20.0/hash4j-0.20.0-sources.jar",
+ sha256 = "63ddb785e1691849d571a78dfca9c311f2646d148fd7cbf1a60999e7e93a54bc",
+ downloaded_file_path = "hash4j-0.20.0-sources.jar"
)
http_file(
@@ -2036,6 +2680,34 @@ http_file(
downloaded_file_path = "util-zip-squashed-252.16432-sources.jar"
)
+http_file(
+ name = "org_fusesource_jansi-jansi-2_4_1_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1.jar",
+ sha256 = "2e5e775a9dc58ffa6bbd6aa6f099d62f8b62dcdeb4c3c3bbbe5cf2301bc2dcc1",
+ downloaded_file_path = "jansi-2.4.1.jar"
+)
+
+http_file(
+ name = "org_fusesource_jansi-jansi-2_4_1-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1-sources.jar",
+ sha256 = "f707511567a13ebf8c51164133770eb5a8e023e1d391bfbc6e7a0591c71729b8",
+ downloaded_file_path = "jansi-2.4.1-sources.jar"
+)
+
+http_file(
+ name = "org_tukaani-xz-1_9_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/tukaani/xz/1.9/xz-1.9.jar",
+ sha256 = "211b306cfc44f8f96df3a0a3ddaf75ba8c5289eed77d60d72f889bb855f535e5",
+ downloaded_file_path = "xz-1.9.jar"
+)
+
+http_file(
+ name = "org_tukaani-xz-1_9-sources_http",
+ url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/tukaani/xz/1.9/xz-1.9-sources.jar",
+ sha256 = "5befa47f06b90e752f035191dde7f2deb59f36000f1ca6cc77d2362a82b6f462",
+ downloaded_file_path = "xz-1.9-sources.jar"
+)
+
http_file(
name = "org_jetbrains_intellij_deps_ift-git-learning-project-212_0_2_http",
url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/org/jetbrains/intellij/deps/ift/git-learning-project/212.0.2/git-learning-project-212.0.2.jar",
@@ -6509,20 +7181,6 @@ http_file(
downloaded_file_path = "ktor-client-java-jvm-3.1.3-sources.jar"
)
-http_file(
- name = "io_ktor-ktor-client-mock-jvm-3_1_3_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3.jar",
- sha256 = "2ae0d57bb9fceb8ee2aeda6536acc6f3e1de355984b4c9da720566b85f1525e8",
- downloaded_file_path = "ktor-client-mock-jvm-3.1.3.jar"
-)
-
-http_file(
- name = "io_ktor-ktor-client-mock-jvm-3_1_3-sources_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3-sources.jar",
- sha256 = "8c47ed0e7a044a368ee139db4d9e372ab295931b8091fcc6027c6124de8eb7ef",
- downloaded_file_path = "ktor-client-mock-jvm-3.1.3-sources.jar"
-)
-
http_file(
name = "io_ktor-ktor-client-okhttp-jvm-3_1_3_http",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-okhttp-jvm/3.1.3/ktor-client-okhttp-jvm-3.1.3.jar",
@@ -8070,20 +8728,6 @@ http_file(
downloaded_file_path = "ktor-client-encoding-jvm-3.1.3-sources.jar"
)
-http_file(
- name = "io_ktor-ktor-client-logging-jvm-3_1_3_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3.jar",
- sha256 = "ecabf013b137f1a69c39cffeae1ba9e365659357b21688f1876ee6cf50fd00b9",
- downloaded_file_path = "ktor-client-logging-jvm-3.1.3.jar"
-)
-
-http_file(
- name = "io_ktor-ktor-client-logging-jvm-3_1_3-sources_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3-sources.jar",
- sha256 = "4ac316b70a1fa7dd06c17926de41340d24c045b3a96e87139f5a7debb6d78a9a",
- downloaded_file_path = "ktor-client-logging-jvm-3.1.3-sources.jar"
-)
-
http_file(
name = "com_miglayout-miglayout-swing-11_4_2_http",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/miglayout/miglayout-swing/11.4.2/miglayout-swing-11.4.2.jar",
@@ -11647,34 +12291,6 @@ http_file(
downloaded_file_path = "mockito-kotlin-6.0.0-sources.jar"
)
-http_file(
- name = "io_netty-netty-buffer-4_2_0_RC2_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2.jar",
- sha256 = "74d953cec00048b892112f9602fc7b1113ea55337dc1ede32e449efcb2b231c7",
- downloaded_file_path = "netty-buffer-4.2.0.RC2.jar"
-)
-
-http_file(
- name = "io_netty-netty-common-4_2_0_RC2_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2.jar",
- sha256 = "cc65afe17daf2f9380d3c64460f5b10ec279f98b9461fbf0ccf62b3cb3ee6c37",
- downloaded_file_path = "netty-common-4.2.0.RC2.jar"
-)
-
-http_file(
- name = "io_netty-netty-buffer-4_2_0_RC2-sources_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2-sources.jar",
- sha256 = "e483fa26b8035c8c2a2306371a19e821b1c63a08465c31b9711e6db79e691715",
- downloaded_file_path = "netty-buffer-4.2.0.RC2-sources.jar"
-)
-
-http_file(
- name = "io_netty-netty-common-4_2_0_RC2-sources_http",
- url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2-sources.jar",
- sha256 = "5b3516f7d3f5d4884af89bd632713d779aba17b5a8eabc6366777201878d7e27",
- downloaded_file_path = "netty-common-4.2.0.RC2-sources.jar"
-)
-
http_file(
name = "io_netty-netty-codec-compression-4_2_0_RC2_http",
url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-codec-compression/4.2.0.RC2/netty-codec-compression-4.2.0.RC2.jar",