diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index f082593..c3aac3c 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Run detekt checks diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b5154eb..8a3eb6b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,10 +15,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build plugin diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index bf01284..f25fb85 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -11,15 +11,15 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - kotlin-version: [1.4.21, 1.5.30, 1.6.10] + kotlin-version: [1.8.21, 1.9.21] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Run tests with Gradle diff --git a/CHANGELOG.md b/CHANGELOG.md index 697e4d2..e3aeed8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.3.2 + +##### Add +* [issue#5](https://github.com/cianru/rustore-publish-gradle-plugin/issues/5) Support of serviceType plugin param. + # 0.3.1 ##### Add diff --git a/README.md b/README.md index 3d2c428..046ad5e 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ [//]: # (

)

- +

RuStore Publishing

-![Version](https://img.shields.io/badge/GradlePortal-0.3.1-green.svg) +![Version](https://img.shields.io/badge/GradlePortal-0.3.2-green.svg) ![Version](https://img.shields.io/badge/Gradle-8.*-pink.svg) [![License](https://img.shields.io/github/license/srs/gradle-node-plugin.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) @@ -27,7 +27,6 @@ The plugin use [Rustore API](https://help.rustore.ru/rustore/for_developers/work - [Using the `apply` method](#using-the-apply-method) - [Configuring Plugin](#configuring-plugin) - [Plugin usage](#plugin-usage) - # Features @@ -58,10 +57,10 @@ The following features are not available on Rustore API side yet: The Android Gradle Plugin often changes the Variant API, so a different version of AGP corresponds to a specific version of the current plugin -| AGP | Plugin | -|-----|--------| -| 7.+ | 0.2.2 | -| 8.+ | 0.3.1 | +| AGP | Plugin | +|-----|----------------------------------------------------------------------------| +| 7.+ | 0.2.2 | +| 8.+ | [latest](https://github.com/cianru/rustore-publish-gradle-plugin/releases) | # Adding the plugin to your project @@ -76,6 +75,39 @@ plugins { } ``` +
+Snapshot builds are also available +___ + +You'll need to add the Sonatype snapshots repository. +Look for the actual version of the snapshot in the name of the opened `snapshot-` repository branch. + +For general integration add next snippet in `./settings.gradle` + +```kotlin +pluginManagement { + + resolutionStrategy { + eachPlugin { + if(requested.id.namespace == "ru.cian") { + useModule("ru.cian.rustore-plugin:rustore-publish-gradle-plugin:") + } + } + } + + plugins { + id("ru.cian.rustore-publish-gradle-plugin") version rustorePublish apply false + } + + repositories { + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + } +} +``` +___ + +
+ ## Using the `apply` method ```groovy @@ -93,7 +125,65 @@ apply plugin: 'com.android.application' apply plugin: 'ru.cian.rustore-publish-gradle-plugin' ``` -## Configuring Plugin +
+Snapshot builds are also available +___ + +You'll need to add the Sonatype snapshots repository. +Look for the actual version of the snapshot in the name of the opened `snapshot-` repository branch. + +```groovy +buildscript { + repositories { + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + } + + dependencies { + classpath "ru.cian.rustore-plugin:rustore-publish-gradle-plugin::-SNAPSHOT" + } +} + +apply plugin: 'com.android.application' +apply plugin: "ru.cian.rustore-publish-gradle-plugin" +``` +___ + +
+ +## Quickstart Plugin Configuration + +```kotlin +rustorePublish { + instances { + create("release") { + /** + * Path to json file with RuStore credentials params (`company_id` and `client_secret`). + * How to get credentials see [[RU] Rustore API Getting Started](https://help.rustore.ru/rustore/for_developers/work_with_RuStore_API/authorization_rustore_api_1). + * Plugin credential json example: + * { + * "company_id": "", + * "client_secret": "" + * } + * + * Type: String (Optional) + * Default value: `null` (but plugin wait that you provide credentials by CLI params) + * CLI: `--credentialsPath` + */ + credentialsPath = "$rootDir/rustore-credentials-release.json" + + /** + * Path to build file if you would like to change default path. "null" means use standard path for "apk" and "aab" files. + * Type: String (Optional) + * Default value: `null` + * CLI: `--buildFile` + */ + buildFile = "$rootDir/app/build/outputs/apk/release/app-release.apk" + } + } +} +``` + +## Full Plugin Configuration
Kotlin @@ -125,6 +215,16 @@ rustorePublish { */ buildFile = "$rootDir/app/build/outputs/apk/release/app-release.apk" + /** + * Type of mobile services used in application. Available values: [\"Unknown\", \"HMS\"]. + * For more details see param `servicesType` in documentation " + + * https://www.rustore.ru/help/work-with-rustore-api/api-upload-publication-app/apk-file-upload/file-upload-apk/ + * Type: ru.cian.rustore.publish.MobileServicesType (Optional) + * Default value: UNKNOWN + * CLI: `--mobileServicesType` + */ + mobileServicesType = ru.cian.rustore.publish.MobileServicesType.UNKNOWN + /** * Release Notes settings. For mote info see ReleaseNote param desc. * Type: List (Optional) @@ -171,6 +271,7 @@ rustorePublish { release { credentialsPath = "$rootDir/rustore-credentials-release.json" buildFile = "$rootDir/app/build/outputs/apk/release/app-release.apk" + mobileServicesType = "Unknown" releaseNotes = [ new ru.cian.rustore.publish.ReleaseNote( "ru-RU", @@ -191,6 +292,7 @@ the same by CLI ./gradlew assembleRelease publishRustoreRelease \ --credentialsPath="/sample-kotlin/rustore-credentials.json" \ --buildFile="/sample-kotlin/app/build/outputs/apk/release/app-release.apk" \ + --mobileServicesType="Unknown" \ --releaseNotes="ru_RU:/home//str/project/release_notes_ru.txt" ``` CLI params are more priority than Plugin configuration params. diff --git a/build.gradle.kts b/build.gradle.kts index 4cd1ac1..9874f44 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ buildscript { plugins { alias(libs.plugins.kotlinJvm) alias(libs.plugins.benManesVersions) - alias(libs.plugins.gradleDoctor) +// alias(libs.plugins.gradleDoctor) } allprojects { @@ -23,46 +23,46 @@ allprojects { } } -doctor { - /** - * Throw an exception when multiple Gradle Daemons are running. - * - * Windows is not supported yet, see https://github.com/runningcode/gradle-doctor/issues/84 - */ - disallowMultipleDaemons.set(true) - - /** - * Warn when not using parallel GC. Parallel GC is faster for build type tasks and is no longer the default in Java 9+. - */ - warnWhenNotUsingParallelGC.set(true) - - /** - * Warn if using the Kotlin Compiler Daemon Fallback. The fallback is incredibly slow and should be avoided. - * https://youtrack.jetbrains.com/issue/KT-48843 - */ - warnIfKotlinCompileDaemonFallback.set(true) - - /** Configuration properties relating to JAVA_HOME */ - javaHome { - /** - * Ensure that we are using JAVA_HOME to build with this Gradle. - */ - ensureJavaHomeMatches.set(true) - /** - * Ensure we have JAVA_HOME set. - */ - ensureJavaHomeIsSet.set(true) - /** - * Fail on any `JAVA_HOME` issues. - */ - failOnError.set(true) - /** - * Extra message text, if any, to show with the Gradle Doctor message. This is useful if you have a wiki page or - * other instructions that you want to link for developers on your team if they encounter an issue. - */ - extraMessage.set("Gradle Doctor Issue!") - } -} +//doctor { +// /** +// * Throw an exception when multiple Gradle Daemons are running. +// * +// * Windows is not supported yet, see https://github.com/runningcode/gradle-doctor/issues/84 +// */ +// disallowMultipleDaemons.set(true) +// +// /** +// * Warn when not using parallel GC. Parallel GC is faster for build type tasks and is no longer the default in Java 9+. +// */ +// warnWhenNotUsingParallelGC.set(true) +// +// /** +// * Warn if using the Kotlin Compiler Daemon Fallback. The fallback is incredibly slow and should be avoided. +// * https://youtrack.jetbrains.com/issue/KT-48843 +// */ +// warnIfKotlinCompileDaemonFallback.set(true) +// +// /** Configuration properties relating to JAVA_HOME */ +// javaHome { +// /** +// * Ensure that we are using JAVA_HOME to build with this Gradle. +// */ +// ensureJavaHomeMatches.set(true) +// /** +// * Ensure we have JAVA_HOME set. +// */ +// ensureJavaHomeIsSet.set(true) +// /** +// * Fail on any `JAVA_HOME` issues. +// */ +// failOnError.set(true) +// /** +// * Extra message text, if any, to show with the Gradle Doctor message. This is useful if you have a wiki page or +// * other instructions that you want to link for developers on your team if they encounter an issue. +// */ +// extraMessage.set("Gradle Doctor Issue!") +// } +//} configurations.all { resolutionStrategy { diff --git a/docs/screenshots/header_cian_rustore.png b/docs/screenshots/header_cian_rustore.png new file mode 100644 index 0000000..89bf6b3 Binary files /dev/null and b/docs/screenshots/header_cian_rustore.png differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 26d2320..c2690ac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,30 +11,32 @@ minSdkVersion = "21" jvm = "17" kotlin = "1.9.21" detekt = "1.23.4" -junitJupiter = "5.7.0" +junitJupiter = "5.9.3" androidGradlePlugin = "8.0.2" -sampleRustorePlugin = "0.3.1-SNAPSHOT" +sampleRustorePlugin = "0.3.2-SNAPSHOT" [libraries] appcompat = "androidx.appcompat:appcompat:1.6.1" kotlinBom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlin" } -kotlinDateTime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0" +kotlinDateTime = "org.jetbrains.kotlinx:kotlinx-datetime:0.5.0" gson = "com.google.code.gson:gson:2.8.6" -okHttp = "com.squareup.okhttp3:okhttp:4.9.1" +okHttp = "com.squareup.okhttp3:okhttp:4.12.0" androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } +detektFormating = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detektRules = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" } + test-assertk = "com.willowtreeapps.assertk:assertk-jvm:0.23" -test-hamcreast = "org.hamcrest:hamcrest:2.1" -test-mockk = "io.mockk:mockk:1.10.3-jdk8" +test-hamcreast = "org.hamcrest:hamcrest:2.2" +test-mockk = "io.mockk:mockk:1.13.5" test-mockito = "org.mockito:mockito-core:2.23.4" test-mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" -test-junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:5.7.0" -test-junitJupiterEngine = "org.junit.jupiter:junit-jupiter-engine:5.7.0" -test-junitJupiterParams = "org.junit.jupiter:junit-jupiter-params:5.7.0" +test-junitJupiterApi = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitJupiter" } +test-junitJupiterEngine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junitJupiter" } +test-junitJupiterParams = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junitJupiter" } [plugins] dokka = { id = "org.jetbrains.dokka", version = "1.8.10" } -dcendents = { id = "com.github.dcendents", version = "plugin:2.1" } bintray = { id = "com.jfrog.bintray", version = "1.8.5" } pluginPublish = { id = "com.gradle.plugin-publish", version = "0.15.0" } kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index f72a74e..be0cf8b 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -17,7 +17,11 @@ detekt { // The directories where detekt looks for source files. // Defaults to `files("src/main/java", "src/test/java", "src/main/kotlin", "src/test/kotlin")`. - source = files("src/main/java", "src/main/kotlin") + source.setFrom("src/main/java", "src/main/kotlin") + + // Specifying a baseline file. All findings stored in this file in subsequent runs of detekt. + // A way of suppressing issues before introducing detekt. + baseline = file("$projectDir/config/detekt/detekt-baseline.xml") // Builds the AST in parallel. Rules are always executed in parallel. // Can lead to speedups in larger projects. `false` by default. @@ -25,10 +29,7 @@ detekt { // Define the detekt configuration(s) you want to use. // Defaults to the default detekt configuration. - config = files("$projectDir/config/detekt/detekt-config.yml") - - // A way of suppressing issues before introducing detekt. - baseline = file("$projectDir/config/detekt/detekt-baseline.xml") + config.setFrom("$projectDir/config/detekt/detekt-config.yml") // Applies the config files on top of detekt's default config file. `false` by default. buildUponDefaultConfig = true @@ -36,9 +37,6 @@ detekt { // Turns on all the rules. `false` by default. allRules = false - // Specifying a baseline file. All findings stored in this file in subsequent runs of detekt. -// baseline = file("$projectDir/config/baseline.xml") - // Disables all default detekt rulesets and will only run detekt with custom rules // defined in plugins passed in with `detektPlugins` configuration. `false` by default. disableDefaultRuleSets = false @@ -90,6 +88,8 @@ dependencies { implementation(libs.gson) implementation(libs.okHttp) compileOnly(libs.androidGradlePlugin) + detektPlugins(libs.detektFormating) + detektPlugins(libs.detektRules) testImplementation(libs.test.junitJupiterApi) testImplementation(libs.test.junitJupiterEngine) diff --git a/plugin/config/detekt/detekt-config.yml b/plugin/config/detekt/detekt-config.yml index 2b514fd..c6c04ba 100644 --- a/plugin/config/detekt/detekt-config.yml +++ b/plugin/config/detekt/detekt-config.yml @@ -2,30 +2,46 @@ config: # is automatically ignored when custom-checks.jar is on the classpath # however other CI checks use the argsfile where our plugin is not applied # we need to care take of this by explicitly allowing this properties - excludes: 'custom-checks.*' + validation: true + excludes: 'custom-detekt-rules' -custom-checks: +custom-detekt-rules: + RxLazyMethodRule: + active: true + JupiterTestAnnotationRule: + active: true + UnintendedCyrillicSymbolsRule: + active: true + DoesNotExistOutFlakySafelyRule: + active: true + +libraries: active: true - SpekTestDiscovery: + ForbiddenPublicDataClass: + active: false + LibraryEntitiesShouldNotBePublic: + active: false + LibraryCodeMustSpecifyReturnType: active: true - includes: ['**/test/**/*Spec.kt'] + excludes: [ '**/*.kt' ] + includes: [ '**/detekt-api/src/main/**/api/*.kt' ] comments: CommentOverPrivateProperty: - active: true + active: false UndocumentedPublicClass: active: true - excludes: ['**/*.kt'] - includes: ['**/detekt-api/src/main/**/api/*.kt'] + excludes: [ '**/*.kt' ] + includes: [ '**/detekt-api/src/main/**/api/*.kt' ] UndocumentedPublicFunction: active: true - excludes: ['**/*.kt'] - includes: ['**/detekt-api/src/main/**/api/*.kt'] + excludes: [ '**/*.kt' ] + includes: [ '**/detekt-api/src/main/**/api/*.kt' ] complexity: StringLiteralDuplication: active: true - excludes: ['**/test/**', '**/*Test.kt', '**/*Spec.kt'] + excludes: [ '**/test/**', '**/*Test.kt' ] threshold: 5 ignoreAnnotation: true excludeStringsWithLessThan5Characters: true @@ -35,21 +51,33 @@ complexity: threshold: 10 includeStaticDeclarations: false includePrivateDeclarations: false - ComplexMethod: + excludes: [ '**/*View.*','**/*Binder.*','**/*Module.*','**/*Component.*','**/*Dependencies.*' ] + CyclomaticComplexMethod: active: true threshold: 15 ignoreSingleWhenExpression: true LargeClass: - active: true - excludes: ['**/test/**', '**/*.Test.kt', '**/*.Spec.kt'] + active: false MethodOverloading: active: true + threshold: 10 + excludes: [ '**/*Component.*' ] LongParameterList: active: true - functionThreshold: 7 - constructorThreshold: 7 + functionThreshold: 5 + constructorThreshold: 30 ignoreDefaultParameters: false ignoreDataClasses: true + excludes: [ '**/*Presenter.*', '**/*Analytics.*', '**/*Builder.*', '**/*StructureProvider.*', '**/*Module.*', '**/src/test/**', '**/*Mapper.*', '**/*Dependencies.*' ] + ignoreAnnotated: ['Composable'] + LongMethod: + active: false + excludes: [ '**/*StructureProvider.*' ] + TooManyFunctions: + active: false + NestedBlockDepth: + active: true + threshold: 5 coroutines: active: true @@ -81,6 +109,10 @@ exceptions: active: true ThrowingNewInstanceOfSameException: active: true + SwallowedException: + active: false + TooGenericExceptionCaught: + active: false formatting: active: true @@ -90,17 +122,34 @@ formatting: active: true EnumEntryNameCase: active: true + ChainWrapping: + active: false + CommentSpacing: + active: false + ImportOrdering: + active: false + Indentation: + active: true + FinalNewline: + active: false MaximumLineLength: active: true maxLineLength: 120 + excludes: [ '**/test/**', '**/*Test.kt' ] MultiLineIfElse: active: true + NoBlankLineBeforeRbrace: + active: true NoEmptyFirstLineInMethodBlock: active: false + NoSemicolons: + active: false NoTrailingSpaces: active: false PackageName: active: true + SpacingAroundColon: + active: false SpacingAroundAngleBrackets: active: true SpacingAroundDoubleColon: @@ -108,14 +157,20 @@ formatting: SpacingAroundUnaryOperator: active: true SpacingBetweenDeclarationsWithAnnotations: - active: true + active: false SpacingBetweenDeclarationsWithComments: - active: true + active: false + Wrapping: + active: false + ArgumentListWrapping: + active: false + Filename: + active: false naming: InvalidPackageDeclaration: active: true - excludes: ['**/build-logic/**/*.kt', '**/*.kts'] + excludes: [ '**/build-logic/**/*.kt', '**/*.kts' ] NoNameShadowing: active: true NonBooleanPropertyPrefixedWithIs: @@ -124,6 +179,12 @@ naming: active: true VariableMinLength: active: true + FunctionNaming: + ignoreAnnotated: ['Composable'] + +performance: + SpreadOperator: + active: false potential-bugs: AvoidReferentialEquality: @@ -140,6 +201,8 @@ potential-bugs: active: true ImplicitUnitReturnType: active: true + ImplicitDefaultLocale: + active: false MapGetWithNotNullAssertionOperator: active: true UnconditionalJumpStatementInLoop: @@ -148,17 +211,19 @@ potential-bugs: active: true UnsafeCast: active: true - excludes: ['**/test/**', '**/*.Test.kt', '**/*.Spec.kt'] + excludes: [ '**/test/**', '**/*Test.kt', ] UselessPostfixExpression: active: true style: ClassOrdering: active: true + excludes: [ '**/*Fragment.kt','**/*DialogFragment.kt','**/*Activity.kt' ] CollapsibleIfStatements: active: true DestructuringDeclarationWithTooManyEntries: active: true + maxDestructuringEntries: 5 EqualsOnSignatureLine: active: true ExplicitCollectionElementAccessMethod: @@ -167,22 +232,17 @@ style: active: true ForbiddenComment: active: true - values: - - 'TODO:' - - 'FIXME:' + comments: + - '^(?i)\b(todo)\b' - 'STOPSHIP:' - '@author' - '@requiresTypeResolution' - excludes: ['**/detekt-rules-style/**/ForbiddenComment.kt'] + excludes: [ '**/detekt-rules-style/**/ForbiddenComment.kt' ] ForbiddenVoid: active: true - LibraryCodeMustSpecifyReturnType: - active: true - excludes: ['**/*.kt'] - includes: ['**/detekt-api/src/main/**/api/*.kt'] MagicNumber: - excludes: ['**/test/**', '**/*Test.kt', '**/*Spec.kt'] - ignoreNumbers: ['-1', '0', '1', '2', '100', '100.0', '1000'] + excludes: [ '**/test/**', '**/*Test.kt', '**/*RealmMigrationProvider*' ] + ignoreNumbers: [ '-1', '0', '1', '2','3','4','5','6','7','8','9', '100', '100.0', '1000' ] ignorePropertyDeclaration: true ignoreAnnotation: true ignoreEnums: true @@ -193,11 +253,12 @@ style: ignoreNamedArgument: true ignoreRanges: false ignoreExtensionFunctions: true + ignoreAnnotated: ['Composable'] MandatoryBracesLoops: active: true MaxLineLength: active: true - excludes: ['**/test/**', '**/*Test.kt', '**/*Spec.kt'] + excludes: [ '**/test/**', '**/*Test.kt' ] excludeCommentStatements: true NestedClassesVisibility: active: true @@ -218,9 +279,11 @@ style: excludeGuardClauses: true SpacingBetweenPackageAndImports: active: true + SerialVersionUIDInSerializableClass: + active: false ThrowsCount: - active: true max: 3 + excludeGuardClauses: false UnderscoresInNumericLiterals: active: false UnnecessaryAnnotationUseSiteTarget: @@ -236,6 +299,15 @@ style: UnusedPrivateMember: active: true allowedNames: '(_|ignored|expected)' + excludes: [ '**/*Request.kt' ] + ignoreAnnotated: + - 'Preview' + UnusedPrivateProperty: + active: true + allowedNames: '(_|ignored|expected)' + excludes: [ '**/*Request.kt' ] + ignoreAnnotated: + - 'Preview' UseCheckOrError: active: false UseDataClass: @@ -253,6 +325,13 @@ style: UseRequireNotNull: active: true UtilityClassWithPublicConstructor: - active: false + active: true + UnnecessaryAbstractClass: + active: true WildcardImport: active: true + +empty-blocks: + EmptyFunctionBlock: + active: true + ignoreOverridden: true \ No newline at end of file diff --git a/plugin/gradle.properties b/plugin/gradle.properties new file mode 100644 index 0000000..b2331cb --- /dev/null +++ b/plugin/gradle.properties @@ -0,0 +1,47 @@ +org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 +org.gradle.workers.max=2 +kotlin.code.style=official +org.gradle.parallel=false + +android.useAndroidX=true +android.enableJetifier=true + +#################################################################################################### + +GROUP_ID=ru.cian.rustore-plugin +VERSION_NAME=0.3.2 +IS_SNAPSHOT=true + +POM_ARTIFACT_ID=rustore-publish-gradle-plugin +POM_NAME=Rustore Publish Gradle Plugin +POM_PACKAGING=jar + +POM_DESCRIPTION=A Gradle Plugin for publishing release built to Rustore +POM_INCEPTION_YEAR=2020 + +POM_URL=https://github.com/cianru/rustore-publish-gradle-plugin +POM_SCM_URL=https://github.com/cianru/rustore-publish-gradle-plugin +POM_SCM_GITHUB_URL=https://github.com/cianru/rustore-publish-gradle-plugin.git +POM_SCM_CONNECTION=scm:git:git://github.com/cianru/rustore-publish-gradle-plugin.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/cianru/rustore-publish-gradle-plugin.git + +POM_LICENSE_NAME=The Apache Software License, Version 2.0 +POM_LICENSE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENSE_DIST=repo + +POM_DEVELOPER_ID=cianru +POM_DEVELOPER_NAME=CIAN, Inc. +POM_DEVELOPER_EMAIL=opensource@cian.ru +POM_DEVELOPER_CITE=www.cian.ru + +BINTRAY_REPO=ru.cian +BINTRAY_PKG=rustore-publish-gradle-plugin + +GRADLE_PORTAL_URL=https://github.com/cianru/rustore-publish-gradle-plugin +GRADLE_PORTAL_SCM_URL=https://github.com/cianru/rustore-publish-gradle-plugin.git +GRADLE_PORTAL_ARTIFACT_ID=ru.cian.rustore-publish-gradle-plugin +GRADLE_PORTAL_NAME=Rustore Publish Gradle Plugin +GRADLE_PORTAL_DESCRIPTION=A Gradle Plugin for publishing release built to Rustore +GRADLE_PORTAL_IMPLEMENTATION_CLASS=ru.cian.rustore.publish.RustorePublishPlugin + +#################################################################################################### diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishConfig.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishConfig.kt index b36b9a9..209f5de 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishConfig.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishConfig.kt @@ -6,6 +6,7 @@ internal data class InputPluginConfig( val credentials: Credentials, val deployType: DeployType, val artifactFormat: BuildFormat, + val mobileServicesType: MobileServicesType, val artifactFile: File, val releaseTime: String?, val releasePhase: ReleasePhaseConfig?, @@ -27,6 +28,7 @@ internal data class InputPluginCliParam( val credentialsPath: String? = null, val companyId: String? = null, val clientSecret: String? = null, + val mobileServicesType: MobileServicesType? = null, val buildFormat: BuildFormat? = null, val buildFile: String? = null, val releaseTime: String? = null, diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishExtension.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishExtension.kt index 227f7fa..fdeacdc 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishExtension.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishExtension.kt @@ -28,6 +28,7 @@ class RustorePublishExtensionConfig( */ var credentialsPath: String? = null var deployType = DeployType.PUBLISH + var mobileServicesType: MobileServicesType = MobileServicesType.UNKNOWN var buildFormat: BuildFormat = BuildFormat.APK var buildFile: String? = null var releaseTime: String? = null @@ -35,8 +36,8 @@ class RustorePublishExtensionConfig( var releaseNotes: List? = null init { - if (name.isBlank()) { - throw IllegalArgumentException("Name must not be blank nor empty") + require(name.isNotBlank()) { + "Name must not be blank nor empty" } } @@ -51,6 +52,7 @@ class RustorePublishExtensionConfig( "name='$name', " + "credentialsPath='$credentialsPath', " + "deployType='$deployType', " + + "mobileServicesType='$mobileServicesType', " + "buildFormat='$buildFormat', " + "buildFile='$buildFile', " + "releaseTime='$releaseTime', " + @@ -91,9 +93,9 @@ open class ReleaseNote { override fun toString(): String { return "ReleaseNote(" + - "lang='$lang', " + - "filePath='$filePath'" + - ")" + "lang='$lang', " + + "filePath='$filePath'" + + ")" } } @@ -102,6 +104,11 @@ enum class BuildFormat(val fileExtension: String) { AAB("aab"), } +enum class MobileServicesType(val value: String) { + HMS("HMS"), + UNKNOWN("Unknown"), +} + enum class DeployType { /** * Deploy without draft saving and submit on users; diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishPlugin.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishPlugin.kt index 5a66bbf..ffd3d41 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishPlugin.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishPlugin.kt @@ -32,7 +32,7 @@ class RustorePublishPlugin : Plugin { } } - @Suppress("DefaultLocale") + @Suppress("DefaultLocale", "UnusedPrivateProperty") private fun createTask( project: Project, variant: ApplicationVariant, diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishTask.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishTask.kt index 53a3df9..982bfc7 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishTask.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/RustorePublishTask.kt @@ -74,6 +74,16 @@ open class RustorePublishTask ) var buildFormat: BuildFormat? = null + @Suppress("MaxLineLength") + @get:Internal + @set:Option( + option = "mobileServicesType", + description = "Type of mobile services used in application. Available values: [\"Unknown\", \"HMS\"]. " + + "For more details see param `servicesType` in documentation " + + "https://www.rustore.ru/help/work-with-rustore-api/api-upload-publication-app/apk-file-upload/file-upload-apk/" + ) + var mobileServicesType: MobileServicesType? = null + @get:Internal @set:Option( option = "buildFile", @@ -131,6 +141,7 @@ open class RustorePublishTask credentialsPath = credentialsPath, companyId = companyId, clientSecret = clientSecret, + mobileServicesType = mobileServicesType, buildFormat = buildFormat, buildFile = buildFile, releaseTime = releaseTime, @@ -156,8 +167,9 @@ open class RustorePublishTask logger.v("Found build file: `${config.artifactFile.name}`") logger.v("2/6. Create signature") - val timestamp = ZonedDateTime.now().format(DateTimeFormatter.ofPattern(DATETIME_FORMAT_ISO8601)) - val salt = "${config.credentials.companyId}${timestamp}" + val datetimeFormatPattern = DateTimeFormatter.ofPattern(DATETIME_FORMAT_ISO8601) + val timestamp = ZonedDateTime.now().format(datetimeFormatPattern) + val salt = "${config.credentials.companyId}$timestamp" val signatureTools: SignatureTools = if (apiStub != true) SignatureToolsImpl() else MockSignatureTools() val signature = signatureTools.signData(salt, config.credentials.clientSecret) @@ -179,6 +191,7 @@ open class RustorePublishTask rustoreService.uploadBuildFile( token = token, applicationId = config.applicationId, + mobileServicesType = config.mobileServicesType.value, versionId = appVersionId, buildFile = config.artifactFile ) diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/HttpClientHelper.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/HttpClientHelper.kt index 73cfd7f..b58df20 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/HttpClientHelper.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/HttpClientHelper.kt @@ -41,12 +41,6 @@ internal class HttpClientHelper constructor( .build() return client.newCall(request).execute().use { httpResponse -> - val statusCode = httpResponse.code - -// if (!httpResponse.isSuccessful) { // TODO handle and remove; -// throw IllegalStateException("Request failed. statusCode=$statusCode, httpResponse=$httpResponse") -// } - gson.fromJson(httpResponse.body?.charStream(), T::class.java) ?: throw IllegalStateException("http request result must not be null") } @@ -59,6 +53,6 @@ internal class HttpClientHelper constructor( companion object { val MEDIA_TYPE_JSON = "application/json;charset=utf-8".toMediaType() val MEDIA_TYPE_AAB = "application/octet-stream".toMediaType() - private val REQUEST_TIMEOUT = 60L + private const val REQUEST_TIMEOUT = 60L } } diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/MockRustoreService.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/MockRustoreService.kt index bde1bf7..18c16cf 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/MockRustoreService.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/MockRustoreService.kt @@ -2,8 +2,6 @@ package ru.cian.rustore.publish.service import java.io.File -private const val REQUEST_RETRIES = 5 - @SuppressWarnings("StringLiteralDuplication", "TooManyFunctions") internal class MockRustoreService : RustoreService { @@ -15,13 +13,14 @@ internal class MockRustoreService : RustoreService { token: String, applicationId: String, whatsNew: String, - ): Int { + ): Int { return -1 } override fun uploadBuildFile( token: String, applicationId: String, + mobileServicesType: String, versionId: Int, buildFile: File ) { diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreService.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreService.kt index 6d18ddf..9290212 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreService.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreService.kt @@ -20,9 +20,11 @@ internal interface RustoreService { whatsNew: String, ): Int + @Suppress("LongParameterList") fun uploadBuildFile( token: String, applicationId: String, + mobileServicesType: String, versionId: Int, buildFile: File, ) @@ -33,5 +35,4 @@ internal interface RustoreService { versionId: Int, priorityUpdate: Int, ): Boolean - } diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreServiceImpl.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreServiceImpl.kt index 93157e0..4ea1899 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreServiceImpl.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/service/RustoreServiceImpl.kt @@ -121,6 +121,7 @@ internal class RustoreServiceImpl constructor( override fun uploadBuildFile( token: String, applicationId: String, + mobileServicesType: String, versionId: Int, buildFile: File ) { @@ -129,7 +130,7 @@ internal class RustoreServiceImpl constructor( val requestBody = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", buildFile.name, fileBody) - .addFormDataPart("servicesType", "Unknown") + .addFormDataPart("servicesType", mobileServicesType) .addFormDataPart("isMainApk", "true") .build() @@ -138,6 +139,16 @@ internal class RustoreServiceImpl constructor( "Public-Token" to token, ) + logger.i(""" + curl --location --request POST \ + --header 'Content-Type: application/json' \ + --header 'Public-Token: $token' \ + --form servicesType=$mobileServicesType \ + --form isMainApk=true \ + --form file='@${buildFile.absolutePath}' \ + $DOMAIN_URL/public/v1/application/$applicationId/version/$versionId/apk + """.trimIndent()) + val response = httpClient.post( url = "$DOMAIN_URL/public/v1/application/$applicationId/version/$versionId/apk", body = requestBody, @@ -146,7 +157,7 @@ internal class RustoreServiceImpl constructor( logger.v("response=$response") - check (response.code == "OK") { + check(response.code == "OK") { "Build file uploading is failed! " + "Reason code: ${response.code}, " + "message: ${response.message}" @@ -166,7 +177,8 @@ internal class RustoreServiceImpl constructor( """.trimIndent()) val response = httpClient.post( - url = "$DOMAIN_URL/public/v1/application/$applicationId/version/$versionId/commit?priorityUpdate=$priorityUpdate", + url = "$DOMAIN_URL/public/v1/application/$applicationId/version/$versionId/commit" + + "?priorityUpdate=$priorityUpdate", body = "".toRequestBody(), headers = mapOf( "Content-Type" to "application/json", diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/BuildFileProvider.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/BuildFileProvider.kt index 2b2b549..314b126 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/BuildFileProvider.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/BuildFileProvider.kt @@ -5,7 +5,7 @@ import com.android.build.api.variant.ApplicationVariant import ru.cian.rustore.publish.BuildFormat import java.io.File -internal class BuildFileProvider constructor( +internal class BuildFileProvider( private val variant: ApplicationVariant, private val logger: Logger, ) { @@ -17,8 +17,8 @@ internal class BuildFileProvider constructor( } } - // TODO(a.mirko): Remove after https://github.com/gradle/gradle/issues/16777 - // TODO(a.mirko): Remove after https://github.com/gradle/gradle/issues/16775 + // FIXME(a.mirko): Remove after https://github.com/gradle/gradle/issues/16777 + // FIXME(a.mirko): Remove after https://github.com/gradle/gradle/issues/16775 private fun getFinalApkArtifactCompat(variant: ApplicationVariant): List { val apkDirectory = variant.artifacts.get(SingleArtifact.APK).get() logger.v("Build File Directory: $apkDirectory") diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ConfigProvider.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ConfigProvider.kt index f5a5632..811f081 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ConfigProvider.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ConfigProvider.kt @@ -20,6 +20,7 @@ internal class ConfigProvider( fun getConfig(): InputPluginConfig { + val mobileServicesType = cli.mobileServicesType ?: extension.mobileServicesType val deployType = cli.deployType ?: extension.deployType val artifactFormat = cli.buildFormat ?: extension.buildFormat val customBuildFilePath: String? = cli.buildFile ?: extension.buildFile @@ -43,6 +44,7 @@ internal class ConfigProvider( return InputPluginConfig( credentials = credentialsConfig, deployType = deployType, + mobileServicesType = mobileServicesType, artifactFormat = actualArtifactFormat, artifactFile = artifactFile, releaseTime = releaseTime, @@ -52,7 +54,7 @@ internal class ConfigProvider( ) } - fun getBuildFile( + private fun getBuildFile( customBuildFilePath: String?, artifactFormat: BuildFormat ): File { @@ -66,21 +68,21 @@ internal class ConfigProvider( if (artifactFile == null || !artifactFile.exists()) { throw FileNotFoundException( "$artifactFile (No such file or directory). Application build file is not found. " + - "Please run `assemble` or `bundle` task to build the application file before current task." + "Please run `assemble` or `bundle` task to build the application file before current task." ) } if (artifactFormat.fileExtension != artifactFile.extension) { throw IllegalArgumentException( "Build file ${artifactFile.absolutePath} has wrong file extension " + - "that doesn't match with announced buildFormat($artifactFormat) plugin extension param." + "that doesn't match with announced buildFormat($artifactFormat) plugin extension param." ) } return artifactFile } @Suppress("ThrowsCount") - fun getCredentialsConfig(): Credentials { + private fun getCredentialsConfig(): Credentials { val credentialsFilePath = cli.credentialsPath ?: extension.credentialsPath val companyIdPriority: String? = cli.companyId val clientSecretPriority: String? = cli.clientSecret @@ -88,33 +90,35 @@ internal class ConfigProvider( if (credentialsFilePath.isNullOrBlank()) { throw FileNotFoundException( "$extension (File path for credentials is null or empty. " + - "See the `credentialsPath` param description." + "See the `credentialsPath` param description." ) } val credentialsFile = File(credentialsFilePath) if (!credentialsFile.exists()) { throw FileNotFoundException( "$extension (File (${credentialsFile.absolutePath}) " + - "with 'company_id' and 'client_secret' for access to Rustore Publish API is not found)" + "with 'company_id' and 'client_secret' for access to Rustore Publish API is not found)" ) } CredentialHelper.getCredentials(credentialsFile) } - val companyId = companyIdPriority ?: credentials.value.companyId.nullIfBlank() - ?: throw IllegalArgumentException( - "(Rustore credential `companyId` param is null or empty). " + + val companyId = companyIdPriority + ?: credentials.value.companyId.nullIfBlank() + ?: throw IllegalArgumentException( + "(Rustore credential `companyId` param is null or empty). " + "Please check your credentials file content or as single parameter." - ) - val clientSecret = clientSecretPriority ?: credentials.value.clientSecret.nullIfBlank() - ?: throw IllegalArgumentException( - "(Rustore credential `clientSecret` param is null or empty). " + + ) + val clientSecret = clientSecretPriority + ?: credentials.value.clientSecret.nullIfBlank() + ?: throw IllegalArgumentException( + "(Rustore credential `clientSecret` param is null or empty). " + "Please check your credentials file content or as single parameter." - ) + ) return Credentials(companyId, clientSecret) } @Suppress("ThrowsCount") - fun getReleasePhaseConfig(): ReleasePhaseConfig? { + private fun getReleasePhaseConfig(): ReleasePhaseConfig? { val releasePhasePercent = cli.releasePhasePercent?.toDouble() ?: extension.releasePhase?.percent if (releasePhasePercent != null) { @@ -149,29 +153,20 @@ internal class ConfigProvider( return releaseNotePairs?.map { val lang = it.first - val filePath = it.second - - if (lang.isBlank()) { - throw IllegalArgumentException( - "'lang' param must not be empty." - ) + require(lang.isNotBlank()) { + "'lang' param must not be empty." } + val filePath = it.second val file = releaseNotesFileProvider.getFile(filePath) - - if (!file.exists()) { - throw IllegalArgumentException( - "File '$filePath' with Release Notes for '$lang' language is not exist." - ) + require(file.exists()) { + "File '$filePath' with Release Notes for '$lang' language is not exist." } val newFeatures = file.readText(Charsets.UTF_8) - - if (newFeatures.length > RELEASE_NOTES_MAX_LENGTH) { - throw IllegalArgumentException( - "Release notes from '$filePath' for '$lang' language " + - "must be less or equals to $RELEASE_NOTES_MAX_LENGTH sign." - ) + require(newFeatures.length <= RELEASE_NOTES_MAX_LENGTH) { + "Release notes from '$filePath' for '$lang' language " + + "must be less or equals to $RELEASE_NOTES_MAX_LENGTH sign." } ReleaseNotesConfig( diff --git a/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ServerPollingExecutor.kt b/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ServerPollingExecutor.kt index 0a5f7c7..e33e563 100644 --- a/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ServerPollingExecutor.kt +++ b/plugin/src/main/kotlin/ru/cian/rustore/publish/utils/ServerPollingExecutor.kt @@ -4,7 +4,7 @@ import java.util.concurrent.TimeoutException internal class ServerPollingExecutor { - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "LongParameterList") fun run( periodTimeInMs: Long, timeoutInMs: Long, diff --git a/plugin/src/test/kotlin/ru/cian/rustore/publish/utils/ConfigProviderTest.kt b/plugin/src/test/kotlin/ru/cian/rustore/publish/utils/ConfigProviderTest.kt index bb179ba..5103803 100644 --- a/plugin/src/test/kotlin/ru/cian/rustore/publish/utils/ConfigProviderTest.kt +++ b/plugin/src/test/kotlin/ru/cian/rustore/publish/utils/ConfigProviderTest.kt @@ -13,6 +13,17 @@ import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance +import ru.cian.rustore.publish.BuildFormat +import ru.cian.rustore.publish.Credentials +import ru.cian.rustore.publish.DeployType +import ru.cian.rustore.publish.InputPluginCliParam +import ru.cian.rustore.publish.InputPluginConfig +import ru.cian.rustore.publish.MobileServicesType +import ru.cian.rustore.publish.ReleaseNote +import ru.cian.rustore.publish.ReleaseNotesConfig +import ru.cian.rustore.publish.ReleasePhaseConfig +import ru.cian.rustore.publish.ReleasePhaseExtension +import ru.cian.rustore.publish.RustorePublishExtensionConfig import ru.cian.rustore.publish.models.Credential import java.io.File @@ -38,14 +49,14 @@ private const val APP_BASIC_INFO_FILE_SECOND_PATH = "$BUILD_DIRECTORY_PATH/app_i @TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class ConfigProviderTest { - private val buildFileProvider = mockk() private val project = mockk() + private val buildFileProvider = mockk() private val releaseNotesFileProvider = mockk() - - private val emptyCliConfig = ru.cian.rustore.publish.InputPluginCliParam() + private val applicationId = "applicationId_1234567890" + private val emptyCliConfig = InputPluginCliParam() private fun extensionConfigInstance() = run { - ru.cian.rustore.publish.RustorePublishExtensionConfig("any", project).apply { + RustorePublishExtensionConfig("any", project).apply { credentialsPath = CREDENTIALS_FILE_PATH } } @@ -96,15 +107,15 @@ internal class ConfigProviderTest { @BeforeEach fun beforeEach() { - every { buildFileProvider.getBuildFile(ru.cian.rustore.publish.BuildFormat.APK) } returns File(ARTIFACT_APK_FILE_PATH) - every { buildFileProvider.getBuildFile(ru.cian.rustore.publish.BuildFormat.AAB) } returns File(ARTIFACT_AAB_FILE_PATH) + every { buildFileProvider.getBuildFile(BuildFormat.APK) } returns File(ARTIFACT_APK_FILE_PATH) + every { buildFileProvider.getBuildFile(BuildFormat.AAB) } returns File(ARTIFACT_AAB_FILE_PATH) } @Test fun `get error to build config for wrong artifact file`() = mockkObject(CredentialHelper) { - val cliConfig = ru.cian.rustore.publish.InputPluginCliParam( - buildFormat = ru.cian.rustore.publish.BuildFormat.APK, + val cliConfig = InputPluginCliParam( + buildFormat = BuildFormat.APK, buildFile = WRONG_ARTIFACT_FILE_PATH ) val configProvider = ConfigProvider( @@ -112,6 +123,7 @@ internal class ConfigProviderTest { cli = cliConfig, buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) assertThat { configProvider.getConfig() }.hasException(IllegalArgumentException::class) @@ -120,14 +132,16 @@ internal class ConfigProviderTest { @Test fun `correct config for default params`() = mockkObject(CredentialHelper) { - val expectedConfig = ru.cian.rustore.publish.InputPluginConfig( - credentials = ru.cian.rustore.publish.Credentials("id", "secret"), - deployType = ru.cian.rustore.publish.DeployType.PUBLISH, - artifactFormat = ru.cian.rustore.publish.BuildFormat.APK, + val expectedConfig = InputPluginConfig( + credentials = Credentials("id", "secret"), + deployType = DeployType.PUBLISH, + artifactFormat = BuildFormat.APK, + mobileServicesType = MobileServicesType.UNKNOWN, artifactFile = File(ARTIFACT_APK_FILE_PATH), releaseTime = null, releasePhase = null, releaseNotes = null, + applicationId = applicationId, ) every { @@ -142,17 +156,19 @@ internal class ConfigProviderTest { cli = emptyCliConfig, buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( expectedConfig, ConfigProvider( extension = extensionConfigInstance(), - cli = ru.cian.rustore.publish.InputPluginCliParam( + cli = InputPluginCliParam( credentialsPath = CREDENTIALS_FILE_PATH ), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .forAll { expectedValue, actualValue -> @@ -163,34 +179,36 @@ internal class ConfigProviderTest { @Test fun `correct config with overriding common values at cli params`() { - val expectedConfig = ru.cian.rustore.publish.InputPluginConfig( - credentials = ru.cian.rustore.publish.Credentials("id123", "secret123"), - deployType = ru.cian.rustore.publish.DeployType.DRAFT, - artifactFormat = ru.cian.rustore.publish.BuildFormat.AAB, + val expectedConfig = InputPluginConfig( + credentials = Credentials("id123", "secret123"), + deployType = DeployType.DRAFT, + artifactFormat = BuildFormat.AAB, artifactFile = File(ARTIFACT_AAB_FILE_SECOND_PATH), + mobileServicesType = MobileServicesType.UNKNOWN, releaseTime = "2019-10-18T21:00:00+0300", - releasePhase = ru.cian.rustore.publish.ReleasePhaseConfig( + releasePhase = ReleasePhaseConfig( percent = 10.05 ), releaseNotes = null, + applicationId = applicationId, ) val inputExtensionConfig = extensionConfigInstance().apply { credentialsPath = CREDENTIALS_FILE_PATH - deployType = ru.cian.rustore.publish.DeployType.PUBLISH - buildFormat = ru.cian.rustore.publish.BuildFormat.APK + deployType = DeployType.PUBLISH + buildFormat = BuildFormat.APK buildFile = ARTIFACT_APK_FILE_PATH releaseTime = "2000-10-18T21:00:00+0300" - releasePhase = ru.cian.rustore.publish.ReleasePhaseExtension().apply { + releasePhase = ReleasePhaseExtension().apply { percent = 99.7 } } - val inputCliConfig = ru.cian.rustore.publish.InputPluginCliParam( - deployType = ru.cian.rustore.publish.DeployType.DRAFT, + val inputCliConfig = InputPluginCliParam( + deployType = DeployType.DRAFT, credentialsPath = CREDENTIALS_FILE_SECOND_PATH, companyId = "id123", clientSecret = "secret123", - buildFormat = ru.cian.rustore.publish.BuildFormat.AAB, + buildFormat = BuildFormat.AAB, buildFile = ARTIFACT_AAB_FILE_SECOND_PATH, releaseTime = "2019-10-18T21:00:00+0300", releasePhasePercent = "10.05", @@ -200,6 +218,7 @@ internal class ConfigProviderTest { cli = inputCliConfig, buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) val actualValue = configProvider.getConfig() @@ -211,72 +230,79 @@ internal class ConfigProviderTest { @Test fun `correct config with overriding of publish param`() { - val expectedConfig = ru.cian.rustore.publish.InputPluginConfig( - credentials = ru.cian.rustore.publish.Credentials("id", "secret"), - deployType = ru.cian.rustore.publish.DeployType.PUBLISH, - artifactFormat = ru.cian.rustore.publish.BuildFormat.APK, + val expectedConfig = InputPluginConfig( + credentials = Credentials("id", "secret"), + deployType = DeployType.PUBLISH, + artifactFormat = BuildFormat.APK, + mobileServicesType = MobileServicesType.UNKNOWN, artifactFile = File(ARTIFACT_APK_FILE_PATH), releaseTime = null, releasePhase = null, releaseNotes = null, + applicationId = applicationId, ) tableOf("expectedValue", "actualValue") .row( - expectedConfig.copy(deployType = ru.cian.rustore.publish.DeployType.PUBLISH), + expectedConfig.copy(deployType = DeployType.PUBLISH), ConfigProvider( extension = extensionConfigInstance(), - cli = ru.cian.rustore.publish.InputPluginCliParam(), + cli = InputPluginCliParam(), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( - expectedConfig.copy(deployType = ru.cian.rustore.publish.DeployType.DRAFT), + expectedConfig.copy(deployType = DeployType.DRAFT), ConfigProvider( extension = extensionConfigInstance().apply { - deployType = ru.cian.rustore.publish.DeployType.DRAFT + deployType = DeployType.DRAFT }, - cli = ru.cian.rustore.publish.InputPluginCliParam(), + cli = InputPluginCliParam(), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( - expectedConfig.copy(deployType = ru.cian.rustore.publish.DeployType.UPLOAD_ONLY), + expectedConfig.copy(deployType = DeployType.UPLOAD_ONLY), ConfigProvider( extension = extensionConfigInstance().apply { - deployType = ru.cian.rustore.publish.DeployType.UPLOAD_ONLY + deployType = DeployType.UPLOAD_ONLY }, - cli = ru.cian.rustore.publish.InputPluginCliParam(), + cli = InputPluginCliParam(), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( - expectedConfig.copy(deployType = ru.cian.rustore.publish.DeployType.DRAFT), + expectedConfig.copy(deployType = DeployType.DRAFT), ConfigProvider( extension = extensionConfigInstance().apply { - deployType = ru.cian.rustore.publish.DeployType.DRAFT + deployType = DeployType.DRAFT }, - cli = ru.cian.rustore.publish.InputPluginCliParam( + cli = InputPluginCliParam( deployType = null ), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( - expectedConfig.copy(deployType = ru.cian.rustore.publish.DeployType.UPLOAD_ONLY), + expectedConfig.copy(deployType = DeployType.UPLOAD_ONLY), ConfigProvider( extension = extensionConfigInstance().apply { - deployType = ru.cian.rustore.publish.DeployType.DRAFT + deployType = DeployType.DRAFT }, - cli = ru.cian.rustore.publish.InputPluginCliParam( - deployType = ru.cian.rustore.publish.DeployType.UPLOAD_ONLY + cli = InputPluginCliParam( + deployType = DeployType.UPLOAD_ONLY ), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .forAll { expectedValue, actualValue -> @@ -287,14 +313,16 @@ internal class ConfigProviderTest { @Suppress("LongMethod") @Test fun `correct config with overriding release notes`() { - val expectedConfig = ru.cian.rustore.publish.InputPluginConfig( - credentials = ru.cian.rustore.publish.Credentials("id", "secret"), - deployType = ru.cian.rustore.publish.DeployType.PUBLISH, - artifactFormat = ru.cian.rustore.publish.BuildFormat.APK, + val expectedConfig = InputPluginConfig( + credentials = Credentials("id", "secret"), + deployType = DeployType.PUBLISH, + artifactFormat = BuildFormat.APK, + mobileServicesType = MobileServicesType.UNKNOWN, artifactFile = File(ARTIFACT_APK_FILE_PATH), releaseTime = null, releasePhase = null, releaseNotes = null, + applicationId = applicationId, ) val langRu = "lang_ru_RU" val releaseNotesRu = "Some release notes for ru_RU" @@ -318,7 +346,7 @@ internal class ConfigProviderTest { tableOf("expectedValue", "actualValue") .row( expectedConfig.copy(releaseNotes = listOf( - ru.cian.rustore.publish.ReleaseNotesConfig( + ReleaseNotesConfig( lang = langRu, newFeatures = releaseNotesRu ) @@ -326,36 +354,38 @@ internal class ConfigProviderTest { ConfigProvider( extension = extensionConfigInstance().apply { releaseNotes = listOf( - ru.cian.rustore.publish.ReleaseNote( + ReleaseNote( lang = langRu, filePath = releaseNotesRuFilePath ) ) }, - cli = ru.cian.rustore.publish.InputPluginCliParam(), + cli = InputPluginCliParam(), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( expectedConfig.copy(releaseNotes = listOf( - ru.cian.rustore.publish.ReleaseNotesConfig( + ReleaseNotesConfig( lang = langRu, newFeatures = releaseNotesRu ) )), ConfigProvider( extension = extensionConfigInstance(), - cli = ru.cian.rustore.publish.InputPluginCliParam( + cli = InputPluginCliParam( releaseNotes = "$langRu:$releaseNotesRuFilePath" ), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .row( expectedConfig.copy(releaseNotes = listOf( - ru.cian.rustore.publish.ReleaseNotesConfig( + ReleaseNotesConfig( lang = langEn, newFeatures = releaseNotesEn ) @@ -363,17 +393,18 @@ internal class ConfigProviderTest { ConfigProvider( extension = extensionConfigInstance().apply { releaseNotes = listOf( - ru.cian.rustore.publish.ReleaseNote( + ReleaseNote( lang = langRu, filePath = releaseNotesRuFilePath ) ) }, - cli = ru.cian.rustore.publish.InputPluginCliParam( + cli = InputPluginCliParam( releaseNotes = "$langEn:$releaseNotesEnFilePath" ), buildFileProvider = buildFileProvider, releaseNotesFileProvider = releaseNotesFileProvider, + applicationId = applicationId, ) ) .forAll { expectedValue, actualValue -> diff --git a/sample-groovy/build.gradle b/sample-groovy/build.gradle index abb2172..00c2ecc 100644 --- a/sample-groovy/build.gradle +++ b/sample-groovy/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath "ru.cian.rustore-plugin:plugin:" + libs.versions.sampleRustorePlugin.get() + classpath "ru.cian.rustore-plugin:rustore-publish-gradle-plugin:" + libs.versions.sampleRustorePlugin.get() } } diff --git a/sample-kotlin/build.gradle.kts b/sample-kotlin/build.gradle.kts index 3ec204a..6256cdd 100644 --- a/sample-kotlin/build.gradle.kts +++ b/sample-kotlin/build.gradle.kts @@ -8,6 +8,7 @@ rustorePublish { instances { create("release") { credentialsPath = "$projectDir/rustore-credentials.json" + mobileServicesType = ru.cian.rustore.publish.MobileServicesType.HMS releaseNotes = listOf( ru.cian.rustore.publish.ReleaseNote( lang = "ru-RU", diff --git a/settings.gradle.kts b/settings.gradle.kts index d05910c..2b013f5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,16 +8,20 @@ include( pluginManagement { - val rustorePublish = "0.3.1-SNAPSHOT" + val rustorePublish = "0.3.2-SNAPSHOT" resolutionStrategy { eachPlugin { if(requested.id.namespace == "ru.cian") { - useModule("ru.cian.rustore-plugin:plugin:${rustorePublish}") + useModule("ru.cian.rustore-plugin:rustore-publish-gradle-plugin:${rustorePublish}") } } } + plugins { + id("ru.cian.rustore-publish-gradle-plugin") version rustorePublish apply false + } + repositories { mavenLocal() maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") }