diff --git a/build.gradle.kts b/build.gradle.kts index d7c19b5..b45668b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,16 +7,16 @@ buildscript { } plugins { - kotlin("jvm") version "1.5.31" + kotlin("jvm") version "1.9.25" id("java-gradle-plugin") id("maven-publish") - id("com.github.ben-manes.versions") version "0.42.0" - id("io.gitlab.arturbosch.detekt") version "1.20.0" - id("com.gradle.plugin-publish") version "1.0.0-rc-1" + id("com.github.ben-manes.versions") version "0.51.0" + id("io.gitlab.arturbosch.detekt") version "1.23.8" + id("com.gradle.plugin-publish") version "2.0.0" } -group = "com.cherryperry.gfe" -version = "2.0.3" +group = "io.openremote.com.cherryperry.gfe" +version = "2.1.0" java { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -26,7 +26,7 @@ java { tasks.withType { kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() - apiVersion = "1.3" + apiVersion = "1.9" } } @@ -36,20 +36,17 @@ detekt { } gradlePlugin { + website = "https://github.com/CherryPerry/GradleFileEncrypt" + vcsUrl = "https://github.com/CherryPerry/GradleFileEncrypt.git" plugins.register("gradleFileEncryptPlugin") { - id = "com.cherryperry.gradle-file-encrypt" + id = "io.openremote.com.cherryperry.gradle-file-encrypt" displayName = "Gradle file encrypt" description = "Simply encrypt your sensitive data in repository with password" + tags = listOf("encryption", "cryptography") implementationClass = "com.cherryperry.gfe.FileEncryptPlugin" } } -pluginBundle { - website = "https://github.com/CherryPerry/GradleFileEncrypt" - vcsUrl = "https://github.com/CherryPerry/GradleFileEncrypt.git" - tags = listOf("encryption", "cryptography") -} - repositories { mavenCentral() } @@ -59,5 +56,6 @@ dependencies { compileOnly(gradleApi()) testImplementation("junit", "junit", "4.13.2") testImplementation(gradleTestKit()) - detektPlugins("io.gitlab.arturbosch.detekt", "detekt-formatting", "1.20.0") + detektPlugins("io.gitlab.arturbosch.detekt", "detekt-formatting", "1.23.8") + detektPlugins("io.gitlab.arturbosch.detekt", "detekt-rules-libraries", "1.23.8") } diff --git a/detekt.yml b/detekt.yml index 9879910..f02903f 100644 --- a/detekt.yml +++ b/detekt.yml @@ -91,7 +91,7 @@ complexity: threshold: 10 includeStaticDeclarations: false includePrivateDeclarations: false - ComplexMethod: + CyclomaticComplexMethod: active: true threshold: 15 ignoreSingleWhenExpression: false @@ -279,12 +279,26 @@ exceptions: - 'RuntimeException' - 'Throwable' +libraries: + active: true + ForbiddenPublicDataClass: + active: true + excludes: [ '**' ] + ignorePackages: + - '*.internal' + - '*.internal.*' + LibraryCodeMustSpecifyReturnType: + active: true + excludes: ['**'] + LibraryEntitiesShouldNotBePublic: + active: true + excludes: ['**'] + naming: active: true BooleanPropertyNaming: active: false allowedPattern: '^(is|has|are)' - ignoreOverridden: true ClassNaming: active: true classPattern: '[A-Z][a-zA-Z0-9]*' @@ -293,7 +307,6 @@ naming: parameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true EnumNaming: active: true enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' @@ -311,12 +324,10 @@ naming: excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] functionPattern: '[a-z][a-zA-Z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true FunctionParameterNaming: active: true parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true InvalidPackageDeclaration: active: false rootPackage: '' @@ -358,7 +369,6 @@ naming: variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true performance: active: true @@ -396,8 +406,6 @@ potential-bugs: - 'java.util.HashSet' - 'java.util.LinkedHashMap' - 'java.util.HashMap' - DuplicateCaseInWhenExpression: - active: true ElseCaseInsteadOfExhaustiveWhen: active: false EqualsAlwaysReturnsTrueOrFalse: @@ -412,7 +420,7 @@ potential-bugs: active: false IgnoredReturnValue: active: false - restrictToAnnotatedMethods: true + restrictToConfig: true returnValueAnnotations: - '*.CheckResult' - '*.CheckReturnValue' @@ -439,15 +447,10 @@ potential-bugs: MissingPackageDeclaration: active: false excludes: ['**/*.kts'] - MissingWhenCase: - active: true - allowElseExpression: true NullCheckOnMutableProperty: active: false NullableToStringCall: active: false - RedundantElseInWhen: - active: true UnconditionalJumpStatementInLoop: active: false UnnecessaryNotNullOperator: @@ -472,6 +475,8 @@ potential-bugs: style: active: true + BracesOnWhenStatements: + active: false CanBeNonNullable: active: false ClassOrdering: @@ -480,7 +485,8 @@ style: active: false DataClassContainsFunctions: active: false - conversionFunctionPrefix: 'to' + conversionFunctionPrefix: + - 'to' DataClassShouldBeImmutable: active: false DestructuringDeclarationWithTooManyEntries: @@ -499,12 +505,14 @@ style: includeLineWrapping: false ForbiddenComment: active: true - values: - - 'FIXME:' - - 'STOPSHIP:' - - 'TODO:' + comments: + - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' + value: 'FIXME:' + - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' + value: 'STOPSHIP:' + - reason: 'Forbidden TODO todo marker in comment, please do the changes.' + value: 'TODO:' allowedPatterns: '' - customMessage: '' ForbiddenImport: active: false imports: [] @@ -514,12 +522,6 @@ style: methods: - 'kotlin.io.print' - 'kotlin.io.println' - ForbiddenPublicDataClass: - active: true - excludes: ['**'] - ignorePackages: - - '*.internal' - - '*.internal.*' ForbiddenVoid: active: false ignoreOverridden: false @@ -528,13 +530,8 @@ style: active: true ignoreOverridableFunction: true ignoreActualFunction: true - excludedFunctions: '' - LibraryCodeMustSpecifyReturnType: - active: true - excludes: ['**'] - LibraryEntitiesShouldNotBePublic: - active: true - excludes: ['**'] + excludedFunctions: + - '' LoopWithTooManyJumpStatements: active: true maxJumpCount: 1 @@ -556,8 +553,6 @@ style: ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false MandatoryBracesLoops: active: false MaxLineLength: @@ -584,8 +579,6 @@ style: active: true OptionalUnit: active: false - OptionalWhenBraces: - active: false PreferToOverPairSyntax: active: false ProtectedMemberInFinalClass: @@ -599,7 +592,8 @@ style: ReturnCount: active: true max: 2 - excludedFunctions: 'equals' + excludedFunctions: + - 'equals' excludeLabeled: false excludeReturnFromLambda: true excludeGuardClauses: false @@ -679,4 +673,4 @@ style: active: true excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] excludeImports: - - 'java.util.*' + - 'java.util.*' \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3ad1194..ef31b4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Xmx512m -XX:MaxPermSize=128m +org.gradle.jvmargs=-Xmx512m org.gradle.caching=true org.gradle.parallel=true org.gradle.vfs.watch=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..afba109 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92f06b5..aa02b02 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..65dcd68 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd3..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/kotlin/com/cherryperry/gfe/FileEncryptExtensionExt.kt b/src/main/kotlin/com/cherryperry/gfe/FileEncryptExtensionExt.kt index 42667f7..1e360f4 100644 --- a/src/main/kotlin/com/cherryperry/gfe/FileEncryptExtensionExt.kt +++ b/src/main/kotlin/com/cherryperry/gfe/FileEncryptExtensionExt.kt @@ -43,6 +43,5 @@ internal fun FileEncryptExtension.secretKey(project: Project): Provider + .provider { password } as Provider } diff --git a/src/main/kotlin/com/cherryperry/gfe/Utils.kt b/src/main/kotlin/com/cherryperry/gfe/Utils.kt index e8be28a..1ddbc02 100644 --- a/src/main/kotlin/com/cherryperry/gfe/Utils.kt +++ b/src/main/kotlin/com/cherryperry/gfe/Utils.kt @@ -13,7 +13,7 @@ const val ITERATIONS = 10_000 const val KEY_LENGTH_BYTES = 16 const val KEY_LENGTH = KEY_LENGTH_BYTES * 8 const val GROUP_NAME = "encryption" -const val PLUGIN_NAME = "com.cherryperry.gradle-file-encrypt" +const val PLUGIN_NAME = "io.openremote.com.cherryperry.gradle-file-encrypt" fun generateKey(password: CharArray): SecretKey { val keySpec = PBEKeySpec(password, "salt".toByteArray(Charsets.UTF_8), ITERATIONS, KEY_LENGTH) diff --git a/src/main/kotlin/com/cherryperry/gfe/providers/DelegatePasswordProvider.kt b/src/main/kotlin/com/cherryperry/gfe/providers/DelegatePasswordProvider.kt index fc4edf7..16e98bc 100644 --- a/src/main/kotlin/com/cherryperry/gfe/providers/DelegatePasswordProvider.kt +++ b/src/main/kotlin/com/cherryperry/gfe/providers/DelegatePasswordProvider.kt @@ -6,5 +6,5 @@ internal class DelegatePasswordProvider( private val fileEncryptExtension: FileEncryptExtension, ) : PasswordProvider { override val password: CharArray? - get() = fileEncryptExtension.passwordProvider.forUseAtConfigurationTime().orNull?.call() + get() = fileEncryptExtension.passwordProvider.orNull?.call() } diff --git a/src/main/kotlin/com/cherryperry/gfe/providers/EnvironmentPasswordProvider.kt b/src/main/kotlin/com/cherryperry/gfe/providers/EnvironmentPasswordProvider.kt index 4a6420c..0ffc93f 100644 --- a/src/main/kotlin/com/cherryperry/gfe/providers/EnvironmentPasswordProvider.kt +++ b/src/main/kotlin/com/cherryperry/gfe/providers/EnvironmentPasswordProvider.kt @@ -6,7 +6,7 @@ internal class EnvironmentPasswordProvider( private val providersFactory: ProviderFactory, ) : PasswordProvider { override val password: CharArray? - get() = providersFactory.environmentVariable(ENVIRONMENT_KEY).forUseAtConfigurationTime().orNull?.toCharArray() + get() = providersFactory.environmentVariable(ENVIRONMENT_KEY).orNull?.toCharArray() companion object { const val ENVIRONMENT_KEY = "GFE_PASSWORD" diff --git a/src/main/kotlin/com/cherryperry/gfe/providers/PropertiesPasswordProvider.kt b/src/main/kotlin/com/cherryperry/gfe/providers/PropertiesPasswordProvider.kt index ffbeadd..bad9521 100644 --- a/src/main/kotlin/com/cherryperry/gfe/providers/PropertiesPasswordProvider.kt +++ b/src/main/kotlin/com/cherryperry/gfe/providers/PropertiesPasswordProvider.kt @@ -15,7 +15,6 @@ internal class PropertiesPasswordProvider( providers .fileContents(layout.projectDirectory.file(LOCAL_PROPERTIES_FILE)) .asBytes - .forUseAtConfigurationTime() .orNull if (propertiesFile != null) { Properties().apply { diff --git a/src/main/kotlin/com/cherryperry/gfe/providers/SystemPropertyPasswordProvider.kt b/src/main/kotlin/com/cherryperry/gfe/providers/SystemPropertyPasswordProvider.kt index e80bd81..e877c8d 100644 --- a/src/main/kotlin/com/cherryperry/gfe/providers/SystemPropertyPasswordProvider.kt +++ b/src/main/kotlin/com/cherryperry/gfe/providers/SystemPropertyPasswordProvider.kt @@ -6,7 +6,7 @@ internal class SystemPropertyPasswordProvider( private val providersFactory: ProviderFactory, ) : PasswordProvider { override val password: CharArray? - get() = providersFactory.systemProperty(SYSTEM_PROPERTY_KEY).forUseAtConfigurationTime().orNull?.toCharArray() + get() = providersFactory.systemProperty(SYSTEM_PROPERTY_KEY).orNull?.toCharArray() companion object { const val SYSTEM_PROPERTY_KEY = "GFE_PASSWORD" diff --git a/src/test/kotlin/com/cherryperry/gfe/FileEncryptPluginFunctionalTest.kt b/src/test/kotlin/com/cherryperry/gfe/FileEncryptPluginFunctionalTest.kt index 3574cb8..c242d66 100644 --- a/src/test/kotlin/com/cherryperry/gfe/FileEncryptPluginFunctionalTest.kt +++ b/src/test/kotlin/com/cherryperry/gfe/FileEncryptPluginFunctionalTest.kt @@ -24,7 +24,7 @@ class FileEncryptPluginFunctionalTest( ) { companion object { - const val EMPTY_BUILD_GRADLE = "plugins { id 'com.cherryperry.gradle-file-encrypt' }" + const val EMPTY_BUILD_GRADLE = "plugins { id 'io.openremote.com.cherryperry.gradle-file-encrypt' }" const val CONTENT_1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." @@ -47,6 +47,22 @@ class FileEncryptPluginFunctionalTest( arrayOf("7.2"), arrayOf("7.3.3"), arrayOf("7.4.2"), + arrayOf("7.5.1"), + arrayOf("7.6.6"), + arrayOf("8.0.2"), + arrayOf("8.1.1"), + arrayOf("8.2.1"), + arrayOf("8.3"), + arrayOf("8.4"), + arrayOf("8.5"), + arrayOf("8.6"), + arrayOf("8.7"), + arrayOf("8.9"), + arrayOf("8.10.2"), + arrayOf("8.11.1"), + arrayOf("8.12.1"), + arrayOf("8.13"), + arrayOf("8.14.3"), ) }