├── .DS_Store ├── .gitattributes ├── .github └── workflows │ └── publisher.yml ├── .gitignore ├── LICENSE ├── README.md ├── android-library-sample ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── dorck │ │ └── android │ │ └── library │ │ └── sample │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── dorck │ │ └── android │ │ └── library │ │ └── sample │ │ └── SimpleFragment.kt │ └── test │ └── java │ └── com │ └── dorck │ └── android │ └── library │ └── sample │ └── ExampleUnitTest.kt ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── dorck │ │ └── android │ │ └── publish │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── dorck │ │ │ └── android │ │ │ └── publish │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── dorck │ └── android │ └── publish │ └── ExampleUnitTest.kt ├── arts └── component_output.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── java-library-sample ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── dorck │ └── java │ └── library │ └── sample │ └── MyJavaClass.java ├── kotlin-library-sample ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── dorck │ └── kotlin │ └── library │ └── sample │ └── MyKotlinClass.kt ├── publish-plugin ├── .gitignore ├── build.gradle.kts └── src │ ├── functionalTest │ └── kotlin │ │ └── cn │ │ └── dorck │ │ └── publisher │ │ └── test │ │ ├── ComponentsPublicationFunctionalTest.kt │ │ └── extensions │ │ └── Extensions.kt │ ├── main │ └── java │ │ └── com │ │ └── dorck │ │ └── android │ │ └── upload │ │ ├── ComponentUploadPlugin.kt │ │ ├── PublishOptionsExtension.kt │ │ ├── config │ │ ├── Constants.kt │ │ └── JavadocJarOptions.kt │ │ ├── extensions │ │ ├── ProjectExtensions.kt │ │ ├── PublicationExtensions.kt │ │ └── StringExtensions.kt │ │ ├── publication │ │ ├── AndroidLibraryMultiVariantPublication.kt │ │ ├── AndroidLibrarySingleVariantPublication.kt │ │ ├── GradlePluginPublication.kt │ │ ├── JavaLibraryPublication.kt │ │ ├── KotlinJsPublication.kt │ │ ├── KotlinLibraryPublication.kt │ │ ├── KotlinMultiplatformPublication.kt │ │ ├── NoneSupportedPublication.kt │ │ └── PlatformPublication.kt │ │ └── task │ │ ├── JavaDocJarTask.kt │ │ └── SourceJarTask.kt │ └── test │ └── kotlin │ └── cn │ └── dorck │ └── publisher │ └── test │ └── ComponentPublisherPluginTest.kt └── settings.gradle /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/publisher.yml: -------------------------------------------------------------------------------- 1 | # A workflow service to setup and publish my plugin. 2 | name: Plugin Publisher 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | # Allows you to run this workflow manually from the Actions tab. 8 | workflow_dispatch: 9 | # Custom webhook event provided to trigger action execution by network request. 10 | repository_dispatch: 11 | types: [publish] 12 | jobs: 13 | publish: 14 | name: Publication Job 15 | runs-on: ubuntu-latest 16 | if: github.repository == 'Moosphan/component-publisher' 17 | env: 18 | GPG_SECRET_KEY_RING_FILE: ${{ github.workspace }}/secring.gpg 19 | steps: 20 | # Checkout to source code repo. 21 | - name: Checkout 22 | uses: actions/checkout@v2 23 | # Set up java env with specific version and distribution. 24 | - name: Set up JDK-8 25 | uses: actions/setup-java@v2 26 | with: 27 | distribution: 'zulu' 28 | java-version: '11' 29 | # Prepare signing for publication. 30 | - name: Set up signing 31 | run: | 32 | echo "Generate GPG private key file in $GPG_SECRET_KEY_RING_FILE" 33 | echo $GPG_SECRET_KEY_RING_FILE_CONTENT | base64 --decode > $GPG_SECRET_KEY_RING_FILE 34 | echo "GPG private key created succeed." 35 | # Publish plugin into Portal. 36 | - name: Plugin publication 37 | env: 38 | GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} 39 | GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} 40 | GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} 41 | GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} 42 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 43 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 44 | run: | 45 | echo "Start publish plugin on Maven Plugin Portal." 46 | echo "Load secret gpg file: ${{ env.GPG_SECRET_KEY_RING_FILE }}" 47 | echo "The release version is: ${GITHUB_REF_NAME}" 48 | ./gradlew clean :publish-plugin:publishPlugins -S --no-daemon 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | 22 | # Keystore files 23 | *.jks 24 | *.keystore 25 | 26 | # Google Services (e.g. APIs or Firebase) 27 | google-services.json 28 | 29 | # Android Profiling 30 | *.hprof 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Component Publisher 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/cn.dorck.android/component-publisher?style=flat-square)](https://search.maven.org/artifact/cn.dorck.android/component-publisher) [![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/cn.dorck.component.publisher?style=flat-square)](https://plugins.gradle.org/plugin/cn.dorck.component.publisher) [![License](https://img.shields.io/badge/license-apache2.0-blue?style=flat-square.svg)](https://opensource.org/licenses/Apache-2.0) 4 | 5 | A Gradle plugin to easily publish components based on maven-publish. 6 | 7 | > *You can find the latest released plugin on [Gradle Plugin Portal](https://plugins.gradle.org/plugin/cn.dorck.component.publisher).* 8 | 9 | ## Todo & Progress 10 | 11 | - [x] Publish this plugin onto gradle plugin portal. 12 | - [x] Improvement of usage doc. 13 | - [ ] Run the publication task with options by project properties. 14 | - [ ] Support signing artifacts before publishing. 15 | - [x] Import GitHub Actions for automatically publishing. 16 | - [ ] Use TestKit to test functions of Gradle plugin. 17 | 18 | ## Features 19 | 20 | - Publish components easily without any complex configs. 21 | - Support multi type components. e.g, Java library, Android library, Kotlin lib, Gradle lib etc. 22 | - Support publishing libraries into local repos, [Sonatype](https://central.sonatype.org) release/snapshot repos or [Github packages](https://docs.github.com/en/packages). 23 | - Safely configure your sensitive credentials in `local.properties`. 24 | 25 | ## Getting Started 26 | 27 | You can quickly publish your own components by following steps with this plugin. 28 | #### 1. Apply plugin 29 | 30 | You need apply `cn.dorck.component.publisher` in `build.gradle`: 31 | 32 | ```kotlin 33 | plugins { 34 | id("cn.dorck.component.publisher") version "1.0.2" 35 | } 36 | ``` 37 | 38 | #### 2. Configure publish options 39 | 40 | You can customize your own publication options like this in your module `build.gradle` to be released: 41 | 42 | ```kotlin 43 | publishOptions { 44 | group = "com.dorck.android" 45 | version = "0.1.0-LOCAL" 46 | artifactId = "sample-library" 47 | } 48 | ``` 49 | 50 | As shown above, you will publish `sample-library` component into `mavenLocal()` which stores at `/Users//.m2/repository`. 51 | 52 | Currently, it supports the following more configuration properties: 53 | 54 | | Option | Description | 55 | | :------------------- | :----------------------------------------------------------- | 56 | | group | The group of maven publication. If empty, use project's group. | 57 | | artifactId | The artifactId of maven publication. If empty, use project's name. | 58 | | version | The version of maven publication. If empty, use project's version. | 59 | | userName | The username of credentials for maven repo. | 60 | | password | The password of credentials for maven repo. | 61 | | releaseRepoUrl | Url for maven release repository. | 62 | | snapshotRepoUrl | Url for maven snapshot repository. | 63 | | description | Library description written in pom file. | 64 | | packSourceCode | Whether to package the source code into jar/aar. | 65 | | transitiveDependency | Will transitive dependencies be required. | 66 | 67 | > If you want to publish component onto remote repositories, you need to specify the `userName` , `password` and `releaseRepoUrl` or `snapshotRepoUrl`. 68 | 69 | #### 3. Execute publication task 70 | 71 | Now, you can easily publish your component by gradle command: 72 | 73 | ``` 74 | $ ./gradlew ::publishComponent 75 | ``` 76 | 77 | Next you will see the following log output from the terminal: 78 | 79 | component_output 80 | 81 | #### 4. Configure stable options 82 | 83 | Normally, our repository urls and credentials information are the same for different components. To prevent repeated configuration, a global default options properties file is supported here. You can just put your global properties in root project `local.properties` like this: 84 | 85 | ```properties 86 | REPO_USER=Moosphan 87 | REPO_PASSWORD=***** 88 | REPO_RELEASE_URL=https://maven.xx.xx/repository/releases/ 89 | REPO_SNAPSHOT_URL=https://maven.xx.xx/repository/snapshots/ 90 | ``` 91 | 92 | > **Note**: If you want to specify repo options for a component, just add repo info in its `build.gradle` >> `publishOptions` DSL. 93 | 94 | ### Article Reference 95 | 96 | - [*Gradle 发布插件的前世今生*](https://dorck.cn/gradle/2022/08/20/component-publication-plugin/) 97 | -------------------------------------------------------------------------------- /android-library-sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-library-sample/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("org.jetbrains.kotlin.android") 4 | id("cn.dorck.component.publisher") version "1.0.4" 5 | } 6 | 7 | android { 8 | compileSdk = 32 9 | 10 | defaultConfig { 11 | minSdk = 21 12 | targetSdk = 32 13 | 14 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles("consumer-rules.pro") 16 | } 17 | 18 | buildTypes { 19 | release { 20 | isMinifyEnabled = false 21 | proguardFiles( 22 | getDefaultProguardFile("proguard-android-optimize.txt"), 23 | "proguard-rules.pro" 24 | ) 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility = JavaVersion.VERSION_1_8 29 | targetCompatibility = JavaVersion.VERSION_1_8 30 | } 31 | kotlinOptions { 32 | jvmTarget = "1.8" 33 | } 34 | } 35 | 36 | publishOptions { 37 | group = "com.dorck.android" 38 | version = "0.1.0-LOCAL" 39 | } 40 | 41 | dependencies { 42 | 43 | implementation("androidx.core:core-ktx:1.7.0") 44 | implementation("androidx.appcompat:appcompat:1.3.0") 45 | implementation("com.google.android.material:material:1.4.0") 46 | testImplementation("junit:junit:4.13.2") 47 | androidTestImplementation("androidx.test.ext:junit:1.1.3") 48 | androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") 49 | } -------------------------------------------------------------------------------- /android-library-sample/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/android-library-sample/consumer-rules.pro -------------------------------------------------------------------------------- /android-library-sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /android-library-sample/src/androidTest/java/com/dorck/android/library/sample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.library.sample 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.dorck.android.library.sample.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /android-library-sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /android-library-sample/src/main/kotlin/com/dorck/android/library/sample/SimpleFragment.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.library.sample 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | class SimpleFragment : Fragment() { 6 | 7 | fun getName() = "SimpleFragment" 8 | } -------------------------------------------------------------------------------- /android-library-sample/src/test/java/com/dorck/android/library/sample/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.library.sample 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 32 8 | 9 | defaultConfig { 10 | applicationId "com.dorck.android.publish" 11 | minSdk 21 12 | targetSdk 32 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | dependencies { 35 | 36 | implementation 'androidx.core:core-ktx:1.7.0' 37 | implementation 'androidx.appcompat:appcompat:1.3.0' 38 | implementation 'com.google.android.material:material:1.4.0' 39 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 40 | testImplementation 'junit:junit:4.13.2' 41 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 43 | 44 | implementation 'com.dorck.kotlin:kotlin-library-sample:1.0.0-LOCAL' 45 | implementation 'com.dorck.android:android-library-sample:0.1.0' 46 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/dorck/android/publish/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.publish 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.dorck.android.publish", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/dorck/android/publish/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.publish 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import com.dorck.android.library.sample.SimpleFragment 6 | import com.dorck.kotlin.library.sample.MyKotlinClass 7 | 8 | class MainActivity : AppCompatActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | setContentView(R.layout.activity_main) 12 | MyKotlinClass().doSomething() 13 | SimpleFragment().activity 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | component-publisher 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/dorck/android/publish/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.publish 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /arts/component_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/arts/component_output.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | plugins { 4 | id 'com.android.application' version '7.2.1' apply false 5 | id 'com.android.library' version '7.2.1' apply false 6 | id 'org.jetbrains.kotlin.android' version '1.5.31' apply false 7 | id 'org.jetbrains.kotlin.jvm' version '1.5.31' apply false 8 | } 9 | 10 | task clean(type: Delete) { 11 | delete rootProject.buildDir 12 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moosphan/component-publisher/8cd5aa7ab73269203e2e16e7d89594bea410f2e7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 11 22:45:36 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /java-library-sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /java-library-sample/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-library") 3 | id("cn.dorck.component.publisher") version "1.0.4" 4 | // id ("component-publisher") version "1.0.0" 5 | } 6 | 7 | publishOptions { 8 | group = "com.dorck.java" 9 | version = "1.0.1-LOCAL" 10 | } 11 | 12 | java { 13 | sourceCompatibility = JavaVersion.VERSION_1_7 14 | targetCompatibility = JavaVersion.VERSION_1_7 15 | } -------------------------------------------------------------------------------- /java-library-sample/src/main/java/com/dorck/java/library/sample/MyJavaClass.java: -------------------------------------------------------------------------------- 1 | package com.dorck.java.library.sample; 2 | 3 | public class MyJavaClass { 4 | void doSomething() { 5 | System.out.println("java"); 6 | } 7 | } -------------------------------------------------------------------------------- /kotlin-library-sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /kotlin-library-sample/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-library") 3 | id("org.jetbrains.kotlin.jvm") 4 | id("cn.dorck.component.publisher") version "1.0.4" 5 | } 6 | 7 | publishOptions { 8 | group = "com.dorck.kotlin" 9 | version = "1.0.0-LOCAL" 10 | } 11 | 12 | java { 13 | sourceCompatibility = JavaVersion.VERSION_1_7 14 | targetCompatibility = JavaVersion.VERSION_1_7 15 | } -------------------------------------------------------------------------------- /kotlin-library-sample/src/main/java/com/dorck/kotlin/library/sample/MyKotlinClass.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.kotlin.library.sample 2 | 3 | class MyKotlinClass { 4 | fun doSomething() { 5 | println("kotlin ...") 6 | } 7 | } -------------------------------------------------------------------------------- /publish-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /publish-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties 2 | 3 | plugins { 4 | `java-gradle-plugin` 5 | `kotlin-dsl` 6 | `maven-publish` 7 | // signing 8 | id("com.gradle.plugin-publish") version "1.0.0-rc-1" 9 | } 10 | 11 | // Load and configure secrets of publication. 12 | ext["signing.keyId"] = null 13 | ext["signing.password"] = null 14 | ext["signing.secretKeyRingFile"] = null 15 | ext["gradle.publish.key"] = null 16 | ext["gradle.publish.secret"] = null 17 | ext["ossrh.username"] = null 18 | ext["ossrh.password"] = null 19 | 20 | // We can get secrets from local.properties or system env. 21 | val localPropsFile = project.rootProject.file(com.android.SdkConstants.FN_LOCAL_PROPERTIES) 22 | if (localPropsFile.exists()) { 23 | val properties = gradleLocalProperties(rootDir) 24 | if (properties.isNotEmpty()) { 25 | properties.onEach { (key, value) -> 26 | ext[key.toString()] = value 27 | } 28 | } 29 | } else { 30 | ext["signing.keyId"] = System.getenv("GPG_KEY_ID") 31 | ext["signing.password"] = System.getenv("GPG_PASSWORD") 32 | ext["signing.secretKeyRingFile"] = System.getenv("GPG_SECRET_KEY_RING_FILE") 33 | ext["gradle.publish.key"] = System.getenv("GRADLE_PUBLISH_KEY") 34 | ext["gradle.publish.secret"] = System.getenv("GRADLE_PUBLISH_SECRET") 35 | ext["ossrh.username"] = System.getenv("OSSRH_USERNAME") 36 | ext["ossrh.password"] = System.getenv("OSSRH_PASSWORD") 37 | } 38 | 39 | java { 40 | sourceCompatibility = JavaVersion.VERSION_1_8 41 | targetCompatibility = JavaVersion.VERSION_1_8 42 | withJavadocJar() 43 | withSourcesJar() 44 | } 45 | 46 | group = PluginInfo.group 47 | version = PluginInfo.version 48 | 49 | // Create a source set for functional test. 50 | val functionalTest: SourceSet by sourceSets.creating 51 | // Make `functionalTestImplementation` extends from `testImplementation` to use test dependencies directly. 52 | configurations["functionalTestImplementation"].extendsFrom(configurations["testImplementation"]) 53 | 54 | pluginBundle { 55 | website = "https://github.com/Moosphan/component-publisher" 56 | vcsUrl = "https://github.com/Moosphan/component-publisher.git" 57 | tags = listOf("component publication", "publish library", "maven-publish", "android-library", "kotlin-library") 58 | } 59 | 60 | gradlePlugin { 61 | plugins { 62 | create(PluginInfo.name) { 63 | // Note: We need to make here id as same as module name, or 64 | // it will publish two different plugins. 65 | id = PluginInfo.id 66 | implementationClass = PluginInfo.implementationClass 67 | displayName = PluginInfo.displayName 68 | description = PluginInfo.description 69 | } 70 | } 71 | // We can use a custom source set for functional tests. 72 | testSourceSets(functionalTest) 73 | } 74 | 75 | dependencies { 76 | // Use gradle api 77 | implementation(gradleApi()) 78 | compileOnly("com.android.tools.build:gradle:4.0.0") 79 | compileOnly(kotlin("stdlib")) 80 | // Use dokka for java docs. 81 | compileOnly("org.jetbrains.dokka:dokka-gradle-plugin:1.6.10") 82 | // Use JUnit test framework for unit tests 83 | testImplementation(kotlin("test")) 84 | // testImplementation(kotlin("test-junit")) 85 | testImplementation(gradleTestKit()) 86 | testImplementation("com.google.truth:truth:1.1.3") 87 | "functionalTestImplementation"("org.junit.jupiter:junit-jupiter:5.7.2") 88 | } 89 | 90 | afterEvaluate { 91 | publishing { 92 | publications { 93 | create("mavenPub") { 94 | group = PluginInfo.group 95 | artifactId = PluginInfo.artifactId 96 | version = PluginInfo.version 97 | from(components["java"]) 98 | 99 | pom { 100 | name.set(PluginInfo.artifactId) 101 | description.set(PluginInfo.description) 102 | url.set(PluginInfo.url) 103 | 104 | scm { 105 | connection.set(PluginInfo.scmConnection) 106 | developerConnection.set(PluginInfo.scmConnection) 107 | url.set(PluginInfo.url) 108 | } 109 | 110 | licenses { 111 | license { 112 | name.set("The Apache Software License, Version 2.0") 113 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 114 | distribution.set("repo") 115 | } 116 | } 117 | 118 | developers { 119 | developer { 120 | name.set(PluginInfo.developer) 121 | email.set(PluginInfo.email) 122 | } 123 | } 124 | } 125 | } 126 | } 127 | repositories { 128 | maven { 129 | val isLocal = version.toString().endsWith("LOCAL") 130 | val repoUrl = if (version.toString().endsWith("SNAPSHOT")) { 131 | "https://s01.oss.sonatype.org/content/repositories/snapshots/" 132 | } else if (isLocal) { 133 | layout.buildDirectory.dir("repos/locals").get().asFile.path 134 | } else { 135 | "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 136 | } 137 | url = uri(repoUrl) 138 | if (!isLocal) { 139 | credentials { 140 | username = this@afterEvaluate.ext["ossrh.username"].toString() 141 | password = this@afterEvaluate.ext["ossrh.password"].toString() 142 | } 143 | } 144 | } 145 | } 146 | } 147 | } 148 | 149 | // If apply plugin `com.gradle.plugin-publish`, no need apply `signing` plugin. 150 | //signing { 151 | // sign(publishing.publications) 152 | //} 153 | 154 | // Reconfigure the conventions for functional tests. 155 | // Refer to: https://docs.gradle.org/current/userguide/test_kit.html 156 | val functionalTestTask by tasks.registering(Test::class) { 157 | group = "verification" 158 | description = "Runs the functional tests." 159 | mustRunAfter(tasks.named("test")) 160 | testClassesDirs = functionalTest.output.classesDirs 161 | classpath = functionalTest.runtimeClasspath 162 | useJUnitPlatform() 163 | beforeTest( 164 | closureOf { 165 | logger.lifecycle("Running test: $this") 166 | } 167 | ) 168 | } 169 | 170 | tasks.withType().configureEach { 171 | useJUnitPlatform() 172 | } 173 | 174 | tasks.check { 175 | dependsOn(functionalTestTask) 176 | } 177 | 178 | // Configure test suites for functional tests (An @Incubating feature since 7.3). 179 | // Refer to: https://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html 180 | /*testing { 181 | suites { 182 | val test by getting(JvmTestSuite::class) { 183 | useJUnitJupiter() 184 | } 185 | @Suppress("UnstableApiUsage") 186 | register(JvmTestSuite::class) { 187 | useKotlinTest() 188 | useSpock() 189 | dependencies { 190 | implementation(project) 191 | } 192 | 193 | targets { 194 | all { 195 | testTask.configure { 196 | shouldRunAfter(test) 197 | } 198 | } 199 | } 200 | } 201 | } 202 | }*/ 203 | 204 | object PluginInfo { 205 | const val id = "cn.dorck.component.publisher" 206 | const val name = "componentPublishPlugin" 207 | const val group = "cn.dorck" 208 | const val artifactId = "component-publisher" 209 | const val implementationClass = "com.dorck.android.upload.ComponentUploadPlugin" 210 | const val version = "1.0.4" 211 | const val displayName = "Upload library for Android" 212 | const val description = "Gradle plugin to publish library component quickly." 213 | const val url = "https://github.com/Moosphan/component-publisher.git" 214 | const val scmConnection = "scm:git@github.com:Moosphan/component-publisher.git" 215 | const val developer = "Dorck" 216 | const val email = "moosphon@gmail.com" 217 | } -------------------------------------------------------------------------------- /publish-plugin/src/functionalTest/kotlin/cn/dorck/publisher/test/ComponentsPublicationFunctionalTest.kt: -------------------------------------------------------------------------------- 1 | package cn.dorck.publisher.test 2 | 3 | import cn.dorck.publisher.test.extensions.AgpVersionMapping 4 | import cn.dorck.publisher.test.extensions.LibraryType 5 | import cn.dorck.publisher.test.extensions.createIfNotExist 6 | import org.gradle.testkit.runner.BuildResult 7 | import org.gradle.testkit.runner.GradleRunner 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.BeforeEach 10 | import org.junit.jupiter.api.io.TempDir 11 | import java.io.File 12 | import java.io.FileNotFoundException 13 | import kotlin.test.Test 14 | import kotlin.test.assertTrue 15 | 16 | /** 17 | * Functional test cases for publishing components with different gradle versions. 18 | * Reference see: https://docs.gradle.org/current/userguide/test_kit.html 19 | * Gradle release histories refer to: https://gradle.org/releases/ 20 | * AGP and Gradle version chart refer to: https://developer.android.com/studio/releases/gradle-plugin 21 | * @author Dorck 22 | * @since 2022/09/07 23 | */ 24 | class ComponentsPublicationFunctionalTest { 25 | @field:TempDir 26 | lateinit var testProjectDir: File 27 | private lateinit var settingsFile: File 28 | private lateinit var buildFile: File 29 | 30 | @BeforeEach 31 | fun setup() { 32 | testProjectDir.also { 33 | settingsFile = File(it, "settings.gradle.kts") 34 | buildFile = File(it, "build.gradle.kts") 35 | } 36 | } 37 | 38 | @Test 39 | fun `publish Java component to maven local with AGP 3_6`() { 40 | val buildResult = publishLibraryToMavenLocal(LibraryType.Java, AgpVersionMapping.AGP_3_6_0.gradleVersion) 41 | assertPublishSucceed(buildResult) 42 | } 43 | 44 | @Test 45 | fun `publish Java component to maven local with AGP 4_0_0`() { 46 | val buildResult = publishLibraryToMavenLocal(LibraryType.Java, AgpVersionMapping.AGP_4_0_0.gradleVersion) 47 | assertPublishSucceed(buildResult) 48 | } 49 | 50 | @Test 51 | fun `publish Java component to maven local with AGP 4_2_0`() { 52 | val buildResult = publishLibraryToMavenLocal(LibraryType.Java, AgpVersionMapping.AGP_4_2_0.gradleVersion) 53 | assertPublishSucceed(buildResult) 54 | } 55 | 56 | @Test 57 | fun `publish Java component to maven local with AGP 7_1_0`() { 58 | val buildResult = publishLibraryToMavenLocal(LibraryType.Java, AgpVersionMapping.AGP_7_1_0.gradleVersion) 59 | assertPublishSucceed(buildResult) 60 | } 61 | 62 | @Test 63 | fun `publish Kotlin component to maven local with AGP 7_2_0`() { 64 | val buildResult = publishLibraryToMavenLocal(LibraryType.Kotlin, AgpVersionMapping.AGP_7_2_0.gradleVersion) 65 | assertPublishSucceed(buildResult) 66 | } 67 | 68 | @Test 69 | fun `publish Android component to maven local with AGP 7_2_0`() { 70 | val buildResult = publishLibraryToMavenLocal(LibraryType.Android, AgpVersionMapping.AGP_7_2_0.gradleVersion) 71 | assertPublishSucceed(buildResult) 72 | } 73 | 74 | private fun assertPublishSucceed(result: BuildResult) { 75 | assertTrue(result.tasks.none { it.outcome == TaskOutcome.FAILED }) 76 | assertTrue(result.output.contains(SUCCEED_MSG)) 77 | } 78 | 79 | private fun publishLibraryToMavenLocal(libraryType: LibraryType, gradleVersion: String): BuildResult { 80 | if (!this::settingsFile.isInitialized || !this::buildFile.isInitialized) { 81 | throw FileNotFoundException("Cannot find `build.gradle` and `settings.gradle` file.") 82 | } 83 | settingsFile.writeText("") 84 | buildFile.writeText(libraryType.buildTestContent) 85 | testProjectDir.run { 86 | generateSourceFile(this, libraryType) 87 | } 88 | println("==> The temp project dir: ${testProjectDir.path}") 89 | return GradleRunner.create() 90 | .withProjectDir(testProjectDir) 91 | .withPluginClasspath() 92 | .withDebug(true) 93 | .withGradleVersion(gradleVersion) 94 | .withArguments("publishComponent", "-S") 95 | .forwardOutput() 96 | .build() 97 | } 98 | 99 | private fun generateSourceFile(parent: File, libraryType: LibraryType): File = 100 | when(libraryType) { 101 | LibraryType.Java -> File( 102 | parent, 103 | "src/main/java/cn/dorck/test/TestJavaClass.java" 104 | ).apply { 105 | createIfNotExist().writeText(""" 106 | public class TestJavaClass { 107 | void doSomething() { 108 | System.out.println("java"); 109 | } 110 | } 111 | """.trimIndent()) 112 | } 113 | LibraryType.Kotlin -> File( 114 | parent, 115 | "src/main/kotlin/cn/dorck/test/TestKotlinClass.kt" 116 | ).apply { 117 | createIfNotExist().writeText(""" 118 | class TestKotlinClass { 119 | fun doSomething() { 120 | println("kotlin ...") 121 | } 122 | } 123 | """.trimIndent()) 124 | } 125 | 126 | LibraryType.Android -> File( 127 | parent, 128 | "src/main/kotlin/cn/dorck/test/SimpleFragment.kt" 129 | ).apply { 130 | createIfNotExist().writeText(""" 131 | package com.dorck.android.library.sample 132 | 133 | import androidx.fragment.app.Fragment 134 | 135 | class SimpleFragment : Fragment() { 136 | 137 | fun getName() = "SimpleFragment" 138 | } 139 | """.trimIndent()) 140 | } 141 | 142 | else -> throw UnsupportedOperationException("Not supported yet") 143 | } 144 | 145 | companion object { 146 | private const val SUCCEED_MSG = "Your component published succeed!" 147 | } 148 | } -------------------------------------------------------------------------------- /publish-plugin/src/functionalTest/kotlin/cn/dorck/publisher/test/extensions/Extensions.kt: -------------------------------------------------------------------------------- 1 | package cn.dorck.publisher.test.extensions 2 | 3 | import java.io.File 4 | 5 | enum class AgpVersionMapping(val gradleVersion: String) { 6 | AGP_3_6_0("5.6.4"), 7 | AGP_4_0_0("6.1.1"), 8 | AGP_4_1_0("6.5.1"), 9 | AGP_4_2_0("6.7.1"), 10 | AGP_7_0_0("7.0.1"), 11 | AGP_7_1_0("7.2"), 12 | AGP_7_2_0("7.3.3"), 13 | } 14 | 15 | enum class LibraryType(val buildTestContent: String) { 16 | Android( 17 | """ 18 | plugins { 19 | id("com.android.library") 20 | id("org.jetbrains.kotlin.android") 21 | id("cn.dorck.component.publisher") version "1.0.4" 22 | } 23 | 24 | android { 25 | compileSdk = 32 26 | 27 | defaultConfig { 28 | minSdk = 21 29 | targetSdk = 32 30 | 31 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 32 | consumerProguardFiles("consumer-rules.pro") 33 | } 34 | 35 | buildTypes { 36 | release { 37 | isMinifyEnabled = false 38 | proguardFiles( 39 | getDefaultProguardFile("proguard-android-optimize.txt"), 40 | "proguard-rules.pro" 41 | ) 42 | } 43 | } 44 | compileOptions { 45 | sourceCompatibility = JavaVersion.VERSION_1_8 46 | targetCompatibility = JavaVersion.VERSION_1_8 47 | } 48 | kotlinOptions { 49 | jvmTarget = "1.8" 50 | } 51 | } 52 | 53 | publishOptions { 54 | group = "com.dorck.android" 55 | version = "0.1.0-LOCAL" 56 | artifactId = "android-sample-library" 57 | } 58 | 59 | """.trimIndent() 60 | ), 61 | Java( 62 | """ 63 | plugins { 64 | id("java-library") 65 | id("cn.dorck.component.publisher") version "1.0.4" 66 | } 67 | 68 | publishOptions { 69 | group = "com.dorck.java" 70 | version = "1.0.1-LOCAL" 71 | artifactId = "java-sample-library" 72 | } 73 | 74 | repositories { 75 | mavenCentral() 76 | google() 77 | } 78 | """.trimIndent() 79 | ), 80 | Kotlin( 81 | """ 82 | plugins { 83 | id("java-library") 84 | id("org.jetbrains.kotlin.jvm") 85 | id("cn.dorck.component.publisher") version "1.0.4" 86 | } 87 | 88 | publishOptions { 89 | group = "com.dorck.kotlin" 90 | version = "1.0.0-LOCAL" 91 | artifactId = "kotlin-sample-library" 92 | } 93 | 94 | java { 95 | sourceCompatibility = JavaVersion.VERSION_1_7 96 | targetCompatibility = JavaVersion.VERSION_1_7 97 | } 98 | """.trimIndent() 99 | ), 100 | Gradle(""), 101 | KotlinJs(""), 102 | Other("") 103 | } 104 | 105 | fun File.createIfNotExist(): File { 106 | if (!this.exists()) { 107 | this.parentFile?.mkdirs() 108 | this.createNewFile() 109 | } 110 | return this 111 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/ComponentUploadPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload 2 | 3 | import com.dorck.android.upload.config.Constants 4 | import com.dorck.android.upload.extensions.* 5 | import groovy.util.Node 6 | import groovy.util.NodeList 7 | import org.gradle.api.Plugin 8 | import org.gradle.api.Project 9 | import org.gradle.api.Project.DEFAULT_VERSION 10 | import org.gradle.api.artifacts.repositories.MavenArtifactRepository 11 | import org.gradle.api.publish.maven.MavenPom 12 | import java.net.URI 13 | 14 | /** 15 | * Plugin to publish libraries quickly based on `maven-publish`. 16 | * @author Dorck 17 | * @since 2022/08/13 18 | * Reference doc: https://docs.gradle.org/current/userguide/publishing_maven.html 19 | */ 20 | class ComponentUploadPlugin : Plugin { 21 | override fun apply(target: Project) { 22 | val moduleType = target.platformModule 23 | //println("===> start apply upload plugin with module type: $moduleType.") 24 | // Skip application module. 25 | if (target.isAndroidApp()) { 26 | return 27 | } 28 | // Check and make depends on `maven-publish`. 29 | if (!target.plugins.hasPlugin("maven-publish")) { 30 | target.plugins.apply("maven-publish") 31 | } 32 | // Obtain plugin extension. 33 | val publishOptionsExtension = target.extensions 34 | .create("publishOptions", PublishOptionsExtension::class.java) 35 | // Begin to config publish dsl. 36 | target.afterEvaluate { 37 | //println("===> publishOptions config: $publishOptionsExtension") 38 | setupLibraryMavenPublishing(this, publishOptionsExtension) 39 | } 40 | } 41 | 42 | private fun setupLibraryMavenPublishing(project: Project, publishOptions: PublishOptionsExtension) { 43 | publishOptions.checkOrSupplementOptions(project) 44 | //println("===> final publishOptions: $publishOptions") 45 | project.mavenPublishingDsl { 46 | // Configure maven repositories. 47 | repositories.maven { 48 | val isLocalPublish = publishOptions.version.endsWith("-LOCAL") 49 | val repoUri: URI = if (publishOptions.version.endsWith("-SNAPSHOT")) { 50 | URI.create(publishOptions.snapshotRepoUrl) 51 | } else if (isLocalPublish) { 52 | project.repositories.mavenLocal().url 53 | } else { 54 | URI.create(publishOptions.releaseRepoUrl) 55 | } 56 | url = repoUri 57 | if (!isLocalPublish) { 58 | credentials.username = publishOptions.userName 59 | credentials.password = publishOptions.password 60 | } 61 | } 62 | // Set up publication info and custom artifacts. 63 | project.setupPlatformPublication(publishOptions.packSourceCode) { 64 | groupId = publishOptions.group 65 | artifactId = publishOptions.artifactId 66 | version = publishOptions.version 67 | // Write component info into pom file. 68 | configurePomXml(pom, publishOptions) 69 | } 70 | // Configure custom task to publish component quickly. 71 | project.createCustomPublishingTask { 72 | printFinalPublication(publishOptions, repositories.named(defaultMavenPublication, MavenArtifactRepository::class.java).get()) 73 | } 74 | } 75 | } 76 | 77 | private fun configurePomXml(pom: MavenPom, publishOptions: PublishOptionsExtension) { 78 | // Set brief info, ignore scm, developer and license info configs etc. 79 | pom.name.set(publishOptions.artifactId) 80 | pom.description.set(publishOptions.description) 81 | // Configure dependencies relationship in pom. 82 | makeUpComponentDependencies(pom, publishOptions.transitiveDependency) 83 | } 84 | 85 | private fun makeUpComponentDependencies(pom: MavenPom, transitiveDependency: Boolean) { 86 | if (!transitiveDependency) { 87 | pom.withXml { 88 | val rootNode = asNode() 89 | val dependencyNodes = rootNode.get("dependencies") as NodeList 90 | if (dependencyNodes.isNotEmpty()) { 91 | rootNode.remove(dependencyNodes.first() as Node) 92 | } 93 | } 94 | } 95 | } 96 | 97 | private fun printFinalPublication(publishOptions: PublishOptionsExtension, mavenArtifactRepository: MavenArtifactRepository) { 98 | println(""" 99 | ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 100 | | Your component published succeed! 101 | | Component artifactId: ${publishOptions.artifactId} 102 | | Component dependency url: implementation '${publishOptions.group}:${publishOptions.artifactId}:${publishOptions.version}' 103 | | Published at: ${mavenArtifactRepository.url} 104 | └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 105 | """.trimIndent()) 106 | } 107 | 108 | private fun PublishOptionsExtension.checkOrSupplementOptions(project: Project) { 109 | group = group.takeIfBlank { project.defaultGroupId } 110 | artifactId = artifactId.takeIfBlank { project.defaultArtifactId } 111 | if (version.isEmpty()) { 112 | if (project.version.toString().isEmpty() || DEFAULT_VERSION == project.version.toString()) { 113 | throw IllegalArgumentException("You must specified version in ${project.name} publishOptions.") 114 | } 115 | version = project.version.toString() 116 | } 117 | // Select publish options first, if parameter is empty, use `local.properties` instead. 118 | val defaultProperties = project.rootProject.loadPropertiesFile() 119 | defaultProperties?.run { 120 | userName = userName.takeIfBlank { getProperty(Constants.REPOSITORY_USERNAME_KEY) } 121 | password = password.takeIfBlank { getProperty(Constants.REPOSITORY_PASSWORD_KEY) } 122 | releaseRepoUrl = releaseRepoUrl.takeIfBlank { getProperty(Constants.REPOSITORY_RELEASE_URL) } 123 | snapshotRepoUrl = snapshotRepoUrl.takeIfBlank { getProperty(Constants.REPOSITORY_SNAPSHOT_URL) } 124 | } 125 | if (version.checkIfLocalVersion()) { 126 | return 127 | } 128 | // If not local version, you must specify the required repo info. 129 | check(userName.isNotEmpty() && password.isNotEmpty() 130 | && (releaseRepoUrl.isNotEmpty() || snapshotRepoUrl.isNotEmpty())) { 131 | "If not publish a LOCAL version, you must specify repo info, e.g, username|password|repoUrls." 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/PublishOptionsExtension.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload 2 | 3 | /** 4 | * Component upload config options extension. 5 | * Note: This class must be open with default constructor. 6 | * @author Dorck 7 | * @since 2022/08/13 8 | */ 9 | open class PublishOptionsExtension { 10 | // If empty, use project's group. 11 | var group: String = "" 12 | // If empty, use project's name. 13 | var artifactId: String = "" 14 | // If empty, use project's version. 15 | var version: String = "" 16 | // If empty, use `local.properties` options. 17 | var userName: String = "" 18 | var password: String = "" 19 | var releaseRepoUrl: String = "" 20 | var snapshotRepoUrl: String = "" 21 | // Description written in pom file. 22 | var description: String = "" 23 | // Whether to package the source code. 24 | var packSourceCode: Boolean = true 25 | // Will transitive dependencies be required. 26 | var transitiveDependency: Boolean = false 27 | 28 | override fun toString(): String { 29 | return "{\n" + 30 | "\tgroup: $group,\n" + 31 | "\tartifactId: $artifactId,\n" + 32 | "\tversion: $version,\n" + 33 | "\tuserName: $userName,\n" + 34 | "\tpassword: $password,\n" + 35 | "\treleaseRepoUrl: $releaseRepoUrl,\n" + 36 | "\tpackSourceCode: $packSourceCode,\n" + 37 | "\ttransitiveDependency: $transitiveDependency,\n" + 38 | "}" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/config/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.config 2 | 3 | /** 4 | * Consts used for repos. 5 | * @author Dorck 6 | * @since 2022/08/17 7 | */ 8 | object Constants { 9 | const val REPOSITORY_USERNAME_KEY = "REPO_USER" 10 | const val REPOSITORY_PASSWORD_KEY = "REPO_PASSWORD" 11 | const val REPOSITORY_RELEASE_URL = "REPO_RELEASE_URL" 12 | const val REPOSITORY_SNAPSHOT_URL = "REPO_SNAPSHOT_URL" 13 | 14 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/config/JavadocJarOptions.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.config 2 | 3 | /** 4 | * Specifies how the javadoc jar should be created. 5 | */ 6 | sealed class JavadocJarOptions { 7 | /** 8 | * Do not create a javadoc jar. This option is not compatible with Maven Central. 9 | */ 10 | class None : JavadocJarOptions() { 11 | override fun equals(other: Any?): Boolean = other is None 12 | override fun hashCode(): Int = this::class.hashCode() 13 | } 14 | 15 | /** 16 | * Creates an empty javadoc jar to satisfy maven central requirements. 17 | */ 18 | class Empty : JavadocJarOptions() { 19 | override fun equals(other: Any?): Boolean = other is Empty 20 | override fun hashCode(): Int = this::class.hashCode() 21 | } 22 | 23 | /** 24 | * Creates a regular javadoc jar using Gradle's default `javadoc` task. 25 | */ 26 | class Javadoc : JavadocJarOptions() { 27 | override fun equals(other: Any?): Boolean = other is Javadoc 28 | override fun hashCode(): Int = this::class.hashCode() 29 | } 30 | 31 | /** 32 | * Creates a javadoc jar using Dokka's output. The argument is the name of the dokka task that should be used 33 | * for that purpose. 34 | */ 35 | data class Dokka(val taskName: String) : JavadocJarOptions() 36 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/extensions/ProjectExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.extensions 2 | 3 | import com.android.build.gradle.AppExtension 4 | import com.android.build.gradle.LibraryExtension 5 | import com.dorck.android.upload.config.JavadocJarOptions 6 | import com.dorck.android.upload.publication.* 7 | import org.gradle.api.Action 8 | import org.gradle.api.GradleException 9 | import org.gradle.api.JavaVersion 10 | import org.gradle.api.Project 11 | import org.gradle.api.plugins.JavaPluginExtension 12 | import org.gradle.api.publish.PublicationContainer 13 | import org.gradle.api.publish.PublishingExtension 14 | import org.gradle.api.publish.maven.MavenPublication 15 | import org.gradle.api.tasks.javadoc.Javadoc 16 | import org.gradle.external.javadoc.StandardJavadocDocletOptions 17 | import org.jetbrains.dokka.gradle.DokkaTask 18 | import java.util.* 19 | 20 | internal const val defaultMavenPublication = "maven" 21 | 22 | val Project.publications: PublicationContainer? 23 | get() = extensions.findByType(PublishingExtension::class.java)?.publications 24 | 25 | val Project.defaultGroupId: String 26 | // TODO: split package name from path 27 | get() = group.toString().takeIf(String::isNotBlank)?.takeUnless { 28 | it == rootProject.name || it.startsWith("${rootProject.name}.") 29 | } ?: parent?.defaultGroupId /*?: layout.projectDirectory.asFileTree.asPath */ ?: throw GradleException("unspecified project group") 30 | 31 | val Project.defaultArtifactId: String 32 | get() = name 33 | 34 | fun Project.isAndroidApp(): Boolean = 35 | platformModule == PlatformModule.ANDROID_APPLICATION 36 | 37 | fun Project.isAndroidLibrary(): Boolean = 38 | platformModule == PlatformModule.ANDROID_LIBRARY 39 | 40 | val Project.platformModule: PlatformModule? 41 | get() = when { 42 | plugins.hasPlugin("com.android.application") -> 43 | PlatformModule.ANDROID_APPLICATION 44 | plugins.hasPlugin("com.android.library") -> 45 | PlatformModule.ANDROID_LIBRARY 46 | plugins.hasPlugin("java-library") || plugins.hasPlugin("java") -> 47 | PlatformModule.JAVA_LIBRARY 48 | plugins.hasPlugin("org.jetbrains.kotlin.jvm") -> 49 | PlatformModule.KOTLIN_LIBRARY 50 | plugins.hasPlugin("java-gradle-plugin") -> // FIXME 2023/12/12 Publish gradle plugin cannot execute here. 51 | PlatformModule.GRADLE_PLUGIN 52 | plugins.hasPlugin("org.jetbrains.kotlin.js") -> 53 | PlatformModule.KOTLIN_JS_LIBRARY 54 | plugins.hasPlugin("org.jetbrains.kotlin.multiplatform") -> 55 | PlatformModule.KOTLIN_MULTI_PLATFORM 56 | else -> null 57 | } 58 | 59 | /** 60 | * Set up custom artifacts to a publication based on platforms. 61 | * Reference: 62 | * - Android artifact: https://developer.android.google.cn/studio/build/maven-publish-plugin 63 | * - Gradle doc: 64 | * - https://docs.gradle.org/current/userguide/publishing_customization.html 65 | * - https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html 66 | */ 67 | fun Project.setupPlatformPublication( 68 | packSourceCode: Boolean, 69 | configure: MavenPublication.() -> Unit 70 | ) { 71 | val platformPublication: PlatformPublication = when(platformModule) { 72 | PlatformModule.JAVA_LIBRARY -> JavaLibraryPublication(defaultJavaDocOptions(),packSourceCode) 73 | PlatformModule.KOTLIN_LIBRARY -> KotlinLibraryPublication(defaultJavaDocOptions(), packSourceCode) 74 | PlatformModule.GRADLE_PLUGIN -> GradlePluginPublication(defaultJavaDocOptions(), packSourceCode) 75 | PlatformModule.ANDROID_LIBRARY -> AndroidLibrarySingleVariantPublication("release", false) 76 | PlatformModule.KOTLIN_JS_LIBRARY -> KotlinJsPublication(defaultJavaDocOptions(), packSourceCode) 77 | PlatformModule.KOTLIN_MULTI_PLATFORM -> KotlinMultiplatformPublication(defaultJavaDocOptions()) 78 | else -> NoneSupportedPublication("Other platform libraries not supported yet.") 79 | } 80 | platformPublication.setupPublication(this, configure) 81 | } 82 | 83 | fun Project.createCustomPublishingTask(onFinish: () -> Unit) { 84 | tasks.register("publishComponent") { 85 | group = "publisher" 86 | if (isAndroidLibrary()) { 87 | dependsOn(tasks.named("assemble${getDefaultBuildType().formatCapitalize()}")) 88 | } 89 | // Finally execute maven publish task depends on custom task. 90 | val publishTaskName = "publish" + defaultMavenPublication.formatCapitalize() + 91 | "PublicationTo" + defaultMavenPublication.formatCapitalize() + "Repository" 92 | val realMavenPublishTask = tasks.named(publishTaskName) 93 | realMavenPublishTask.get().also { 94 | finalizedBy(it) 95 | }.doLast { 96 | onFinish() 97 | } 98 | } 99 | } 100 | 101 | // Config info within maven `publishing` dsl. 102 | fun Project.mavenPublishingDsl(action: Action) { 103 | extensions.configure(PublishingExtension::class.java, action) 104 | } 105 | 106 | fun Project.javaLibraryComponent(): JavaPluginExtension? = 107 | extensions.findByType(JavaPluginExtension::class.java) 108 | 109 | /** 110 | * Obtain app module(com.android.application) like this: 111 | * ``` 112 | * android { 113 | * buildTypes {} 114 | * defaultConfig {} 115 | * } 116 | * ``` 117 | */ 118 | fun Project.androidProject(): AppExtension = 119 | extensions.getByType(AppExtension::class.java) 120 | 121 | /** 122 | * Obtain library module(com.android.library). 123 | */ 124 | fun Project.libraryProject(): LibraryExtension? = 125 | extensions.findByType(LibraryExtension::class.java) 126 | 127 | // Get property from `gradle.properties`. 128 | internal fun Project.findOptionalProperty(key: String): String? = findProperty(key)?.toString() 129 | 130 | // Get properties from `local.properties` or other custom `xx.properties` file. 131 | internal fun Project.loadPropertiesFile(fileName: String = "local.properties"): Properties? { 132 | // Load file 133 | val propertiesFile = file(fileName) 134 | if (!propertiesFile.exists()) { 135 | return null 136 | } 137 | // Load contents into properties object 138 | val properties = Properties() 139 | properties.load(propertiesFile.inputStream()) 140 | return properties 141 | } 142 | 143 | enum class PlatformModule { 144 | ANDROID_APPLICATION, 145 | ANDROID_LIBRARY, 146 | JAVA_LIBRARY, 147 | KOTLIN_LIBRARY, 148 | GRADLE_PLUGIN, 149 | KOTLIN_JS_LIBRARY, 150 | KOTLIN_MULTI_PLATFORM 151 | // Others see here: 152 | // https://docs.gradle.org/current/userguide/build_init_plugin.html#supported_gradle_build_types 153 | } 154 | 155 | // Obtain default [JavadocJarOptions] by env. 156 | private fun Project.defaultJavaDocOptions(): JavadocJarOptions { 157 | return if (plugins.hasPlugin("org.jetbrains.dokka") || plugins.hasPlugin("org.jetbrains.dokka-android")) { 158 | JavadocJarOptions.Dokka(findDokkaTask()) 159 | } else { 160 | tasks.withType(Javadoc::class.java).configureEach { 161 | val options = options as StandardJavadocDocletOptions 162 | val javaVersion = javaVersion() 163 | if (javaVersion.isJava9Compatible) { 164 | options.addBooleanOption("html5", true) 165 | } 166 | if (javaVersion.isJava8Compatible) { 167 | options.addStringOption("Xdoclint:none", "-quiet") 168 | } 169 | } 170 | JavadocJarOptions.Javadoc() 171 | } 172 | } 173 | 174 | private fun Project.javaVersion(): JavaVersion { 175 | try { 176 | val extension = project.extensions.findByType(JavaPluginExtension::class.java) 177 | if (extension != null) { 178 | val toolchain = extension.toolchain 179 | val version = toolchain.languageVersion.get().asInt() 180 | return JavaVersion.toVersion(version) 181 | } 182 | } catch (t: Throwable) { 183 | // ignore failures and fallback to java version in which Gradle is running 184 | } 185 | return JavaVersion.current() 186 | } 187 | 188 | private fun Project.findDokkaTask(): String { 189 | val tasks = tasks.withType(DokkaTask::class.java) 190 | return if (tasks.size == 1) { 191 | tasks.first().name 192 | } else { 193 | tasks.findByName("dokkaHtml")?.name ?: "dokka" 194 | } 195 | } 196 | 197 | private fun Project.getDefaultBuildType(): String { 198 | val defaultBuildType = "release" 199 | if (isAndroidLibrary()) { 200 | val buildTypes = libraryProject()?.buildTypes 201 | if (buildTypes.isNullOrEmpty()) { 202 | return defaultBuildType 203 | } 204 | buildTypes.forEach { 205 | if (it.name == "release") { 206 | return defaultBuildType 207 | } 208 | } 209 | return buildTypes.first().name 210 | } 211 | return defaultBuildType 212 | 213 | } 214 | -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/extensions/PublicationExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.extensions 2 | 3 | import org.gradle.api.publish.maven.MavenPublication 4 | import org.gradle.api.tasks.TaskProvider 5 | import org.gradle.util.GradleVersion 6 | 7 | 8 | internal fun MavenPublication.withSourcesJar(artifactTask: TaskProvider<*>?) { 9 | artifactTask?.let { 10 | if (GradleVersion.current() >= GRADLE_6_6) { 11 | artifact(it) 12 | } else { 13 | artifact(it.get()) 14 | } 15 | } 16 | } 17 | 18 | internal fun MavenPublication.withJavadocJar(artifactTask: TaskProvider<*>?) { 19 | artifactTask?.let { 20 | if (GradleVersion.current() >= GRADLE_6_6) { 21 | artifact(it) 22 | } else { 23 | artifact(it.get()) 24 | } 25 | } 26 | } 27 | 28 | internal val GRADLE_6_6 = GradleVersion.version("6.6") -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/extensions/StringExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.extensions 2 | 3 | 4 | fun String.formatCapitalize(): String { 5 | return replaceFirstCharacter { if (it.isLowerCase()) it.toTitleCase().toString() else it.toString() } 6 | } 7 | 8 | inline fun String.replaceFirstCharacter(transform: (Char) -> CharSequence): String { 9 | return if (isNotEmpty()) transform(this[0]).toString() + substring(1) else this 10 | } 11 | 12 | fun String.takeIfBlank(elseValue: () -> String?): String { 13 | if (isNotBlank()) { 14 | return this 15 | } 16 | return elseValue() ?: "" 17 | } 18 | 19 | fun String.checkIfLocalVersion(): Boolean = endsWith("-LOCAL") -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/AndroidLibraryMultiVariantPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import org.gradle.api.Project 5 | import org.gradle.api.publish.maven.MavenPublication 6 | 7 | /** 8 | * To be used for `com.android.library` projects. Applying this creates a publication for the component of the given 9 | * variants. Depending on the passed parameters for [packSourceJar] and [packJavadoc], `-javadoc` and `-sources` jars will 10 | * be added to the publication. 11 | * 12 | * If the [includedBuildTypeValues] and [includedFlavorDimensionsAndValues] parameters are not provided or 13 | * empty all variants will be published. Otherwise only variants matching those filters will be included. 14 | * 15 | * Equivalent Gradle set up (AGP 7.1.1): 16 | * android { 17 | * publishing { 18 | * multipleVariants { 19 | * allVariants() // or calls to includeBuildTypeValues and includeFlavorDimensionAndValues 20 | * withSourcesJar() 21 | * withJavadocJar() 22 | * } 23 | * } 24 | * } 25 | * 26 | * afterEvaluate { 27 | * publishing { 28 | * publications { 29 | * create("default") { 30 | * from(components["default"]) 31 | * } 32 | * } 33 | * } 34 | * } 35 | */ 36 | data class AndroidLibraryMultiVariantPublication( 37 | override val packSourceJar: Boolean = true, 38 | val packJavadoc: Boolean = true, 39 | val includedBuildTypeValues: Set = emptySet(), 40 | val includedFlavorDimensionsAndValues: Map> = emptyMap(), 41 | ) : PlatformPublication() { 42 | override val javadocOptions: JavadocJarOptions 43 | get() = JavadocJarOptions.None() 44 | 45 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 46 | TODO("Only supported since AGP 7.1.1") 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/AndroidLibrarySingleVariantPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.defaultMavenPublication 5 | import com.dorck.android.upload.extensions.publications 6 | import com.dorck.android.upload.extensions.withJavadocJar 7 | import com.dorck.android.upload.extensions.withSourcesJar 8 | import com.dorck.android.upload.task.JavaDocJarTask.Companion.javadocJarTask 9 | import com.dorck.android.upload.task.SourceJarTask.Companion.androidSourceJarTask 10 | import org.gradle.api.Project 11 | import org.gradle.api.publish.maven.MavenPublication 12 | import org.gradle.kotlin.dsl.repositories 13 | 14 | /** 15 | * To be used for `com.android.library` projects. Applying this creates a publication for the component of the given 16 | * `variant`. Depending on the passed parameters for [variantName], [packSourceJar] and [packJavadoc], 17 | * `-javadoc` and `-sources` jars will be added to the publication. 18 | * 19 | * Equivalent Gradle set up: 20 | * ``` 21 | * android { 22 | * // Since AGP-7.1, we can still configure in the bottom `publishing` dsl. 23 | * publishing { 24 | * singleVariant("variant") { 25 | * withSourcesJar() 26 | * withJavadocJar() 27 | * } 28 | * } 29 | * } 30 | * 31 | * afterEvaluate { 32 | * publishing { 33 | * publications { 34 | * create("variant") { 35 | * from(components["variant"]) 36 | * } 37 | * } 38 | * } 39 | * } 40 | *``` 41 | */ 42 | data class AndroidLibrarySingleVariantPublication( 43 | val variantName: String = "release", 44 | override val packSourceJar: Boolean = true, 45 | val packJavadoc: Boolean = true 46 | ) : PlatformPublication() { 47 | override val javadocOptions: JavadocJarOptions = JavadocJarOptions.None() 48 | 49 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 50 | project.run { 51 | repositories { 52 | google() 53 | } 54 | publications?.create(defaultMavenPublication, MavenPublication::class.java) { 55 | configure() 56 | val component = components.findByName(variantName) 57 | ?: throw IllegalStateException("No variant#$variantName found!") 58 | from(component) 59 | withSourcesJar(androidSourceJarTask(packSourceJar, variantName)) 60 | withJavadocJar(javadocJarTask(javadocOptions)) 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/GradlePluginPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.publications 5 | import com.dorck.android.upload.extensions.withJavadocJar 6 | import com.dorck.android.upload.extensions.withSourcesJar 7 | import com.dorck.android.upload.task.JavaDocJarTask.Companion.javadocJarTask 8 | import com.dorck.android.upload.task.SourceJarTask.Companion.javaSourceJarTask 9 | import org.gradle.api.Project 10 | import org.gradle.api.publish.maven.MavenPublication 11 | 12 | /** 13 | * To be used for `java-gradle-plugin` projects. Uses the default publication that gets created by that plugin. 14 | * Depending on the passed parameters for [javadocOptions] and [packSourceJar], 15 | * `-javadoc` and `-sources` jars will be added to the publication. 16 | * 17 | * Equivalent Gradle set up: 18 | * ``` 19 | * publishing { 20 | * publications { 21 | * withType().configureEach { 22 | * artifact sourceJar 23 | * } 24 | * } 25 | * } 26 | * ``` 27 | * Refer at: [org.gradle.plugin.devel.plugins.MavenPluginPublishPlugin] 28 | */ 29 | data class GradlePluginPublication @JvmOverloads constructor( 30 | override val javadocOptions: JavadocJarOptions, 31 | override val packSourceJar: Boolean = false 32 | ) : PlatformPublication() { 33 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 34 | project.run { 35 | publications?.withType(MavenPublication::class.java)?.all { 36 | configure() 37 | if (name == "pluginMaven") { 38 | withSourcesJar(javaSourceJarTask(packSourceJar)) 39 | withJavadocJar(javadocJarTask(javadocOptions)) 40 | } 41 | } 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/JavaLibraryPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.defaultMavenPublication 5 | import com.dorck.android.upload.extensions.publications 6 | import com.dorck.android.upload.extensions.withJavadocJar 7 | import com.dorck.android.upload.extensions.withSourcesJar 8 | import com.dorck.android.upload.task.JavaDocJarTask.Companion.javadocJarTask 9 | import com.dorck.android.upload.task.SourceJarTask.Companion.javaSourceJarTask 10 | import org.gradle.api.Project 11 | import org.gradle.api.publish.maven.MavenPublication 12 | import org.gradle.kotlin.dsl.get 13 | 14 | /** 15 | * To be used for `java` and `java-library` projects. Applying this creates a publication for the component called 16 | * `java`. Depending on the passed parameters for [javadocOptions] and [packSourceJar], 17 | * `-javadoc` and `-sources` jars will be added to the publication. 18 | * 19 | * Equivalent Gradle set up: 20 | * ``` 21 | * publishing { 22 | * publications { 23 | * create("maven") { 24 | * from(components["java"]) 25 | * artifact sourceJar 26 | * } 27 | * } 28 | * } 29 | * ``` 30 | */ 31 | data class JavaLibraryPublication @JvmOverloads constructor( 32 | override val javadocOptions: JavadocJarOptions, 33 | override val packSourceJar: Boolean = true 34 | ) : PlatformPublication() { 35 | 36 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 37 | project.run { 38 | publications?.create(defaultMavenPublication, MavenPublication::class.java) { 39 | configure() 40 | from(components["java"]) 41 | withSourcesJar(javaSourceJarTask(packSourceJar)) 42 | withJavadocJar(javadocJarTask(javadocOptions)) 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/KotlinJsPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.defaultMavenPublication 5 | import com.dorck.android.upload.extensions.publications 6 | import com.dorck.android.upload.extensions.withJavadocJar 7 | import com.dorck.android.upload.extensions.withSourcesJar 8 | import com.dorck.android.upload.task.JavaDocJarTask.Companion.javadocJarTask 9 | import com.dorck.android.upload.task.SourceJarTask.Companion.kotlinSourceJarTask 10 | import org.gradle.api.Project 11 | import org.gradle.api.publish.maven.MavenPublication 12 | 13 | /** 14 | * To be used for `org.jetbrains.kotlin.js` projects. Applying this creates a publication for the component called 15 | * `kotlin`. Depending on the passed parameters for [javadocOptions] and [packSourceJar], `-javadoc` and `-sources` jars will be 16 | * added to the publication. 17 | * 18 | * Equivalent Gradle set up: 19 | * ``` 20 | * publications { 21 | * create("maven") { 22 | * from(components["kotlin"]) 23 | * artifact(project.tasks.named("kotlinSourcesJar")) 24 | * } 25 | * } 26 | * ``` 27 | * This does not include javadoc jars because there are no APIs for that available. 28 | */ 29 | 30 | data class KotlinJsPublication @JvmOverloads constructor( 31 | override val javadocOptions: JavadocJarOptions = JavadocJarOptions.Empty(), 32 | override val packSourceJar: Boolean = true 33 | ) : PlatformPublication() { 34 | 35 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 36 | // Create publication, since Kotlin/JS doesn't provide one by default. 37 | // https://youtrack.jetbrains.com/issue/KT-41582 38 | project.run { 39 | publications?.create(defaultMavenPublication, MavenPublication::class.java) { 40 | configure() 41 | from(components.getByName("kotlin")) 42 | withSourcesJar(kotlinSourceJarTask(packSourceJar)) 43 | withJavadocJar(javadocJarTask(javadocOptions)) 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/KotlinLibraryPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.defaultMavenPublication 5 | import com.dorck.android.upload.extensions.publications 6 | import com.dorck.android.upload.extensions.withJavadocJar 7 | import com.dorck.android.upload.extensions.withSourcesJar 8 | import com.dorck.android.upload.task.JavaDocJarTask.Companion.javadocJarTask 9 | import com.dorck.android.upload.task.SourceJarTask.Companion.javaSourceJarTask 10 | import org.gradle.api.Project 11 | import org.gradle.api.publish.maven.MavenPublication 12 | import org.gradle.kotlin.dsl.get 13 | 14 | /** 15 | * To be used for `org.jetbrains.kotlin.jvm` projects. Applying this creates a publication for the component called 16 | * `kotlin`. Depending on the passed parameters for [javadocOptions] and [packSourceJar], 17 | * `-javadoc` and `-sources` jars will be added to the publication. 18 | * 19 | * Equivalent Gradle set up: 20 | * ``` 21 | * publications { 22 | * create("maven") { 23 | * from(components["java"]) 24 | * artifact(project.tasks.named("kotlinSourcesJar")) 25 | * } 26 | * } 27 | * ``` 28 | * This does not include javadoc jars because there are no APIs for that available. 29 | */ 30 | data class KotlinLibraryPublication @JvmOverloads constructor( 31 | override val javadocOptions: JavadocJarOptions = JavadocJarOptions.Empty(), 32 | override val packSourceJar: Boolean = true 33 | ) : PlatformPublication() { 34 | 35 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 36 | project.run { 37 | publications?.create(defaultMavenPublication, MavenPublication::class.java) { 38 | configure() 39 | from(components["java"]) 40 | withSourcesJar(javaSourceJarTask(packSourceJar)) 41 | withJavadocJar(javadocJarTask(javadocOptions)) 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/KotlinMultiplatformPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.publications 5 | import com.dorck.android.upload.extensions.withJavadocJar 6 | import com.dorck.android.upload.task.JavaDocJarTask.Companion.javadocJarTask 7 | import org.gradle.api.Project 8 | import org.gradle.api.publish.maven.MavenPublication 9 | 10 | /** 11 | * To be used for `org.jetbrains.kotlin.multiplatform` projects. Uses the default publications that gets created by 12 | * that plugin, including the automatically created `-sources` jars. Depending on the passed parameters for [javadocOptions], 13 | * `-javadoc` will be added to the publications. 14 | * 15 | * Equivalent Gradle set up: 16 | * ``` 17 | * // Nothing to configure setup is automatic. 18 | * ``` 19 | * 20 | * This does not include javadoc jars because there are no APIs for that available. 21 | */ 22 | data class KotlinMultiplatformPublication @JvmOverloads constructor( 23 | override val javadocOptions: JavadocJarOptions = JavadocJarOptions.Empty() 24 | ) : PlatformPublication() { 25 | // Automatically added by Kotlin MPP plugin. 26 | override val packSourceJar: Boolean 27 | get() = false 28 | 29 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 30 | project.run { 31 | publications?.withType(MavenPublication::class.java)?.all { 32 | configure() 33 | withJavadocJar(javadocJarTask(javadocOptions)) 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/NoneSupportedPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import org.gradle.api.Project 5 | import org.gradle.api.publish.maven.MavenPublication 6 | 7 | data class NoneSupportedPublication( 8 | val message: String 9 | ) : PlatformPublication() { 10 | override val javadocOptions: JavadocJarOptions 11 | get() = JavadocJarOptions.None() 12 | override val packSourceJar: Boolean 13 | get() = false 14 | 15 | override fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) { 16 | throw UnsupportedOperationException(message) 17 | } 18 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/publication/PlatformPublication.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.publication 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import org.gradle.api.Project 5 | import org.gradle.api.publish.maven.MavenPublication 6 | 7 | /** 8 | * Abstract ability of maven publications in different platforms. 9 | */ 10 | abstract class PlatformPublication { 11 | abstract val javadocOptions: JavadocJarOptions 12 | abstract val packSourceJar: Boolean 13 | 14 | internal abstract fun setupPublication(project: Project, configure: MavenPublication.() -> Unit) 15 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/task/JavaDocJarTask.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.task 2 | 3 | import com.dorck.android.upload.config.JavadocJarOptions 4 | import com.dorck.android.upload.extensions.formatCapitalize 5 | import org.gradle.api.Project 6 | import org.gradle.api.plugins.JavaPlugin 7 | import org.gradle.api.tasks.TaskProvider 8 | import org.gradle.jvm.tasks.Jar 9 | 10 | open class JavaDocJarTask : Jar() { 11 | init { 12 | archiveClassifier.set("javadoc") 13 | } 14 | 15 | internal companion object { 16 | internal fun Project.javadocJarTask(javadocOptions: JavadocJarOptions) : TaskProvider<*>? { 17 | return when(javadocOptions) { 18 | is JavadocJarOptions.None -> null 19 | is JavadocJarOptions.Empty -> emptyJavaDocJar() 20 | is JavadocJarOptions.Javadoc -> simpleJavadocJar() 21 | is JavadocJarOptions.Dokka -> dokkaJavadocJar(javadocOptions) 22 | } 23 | } 24 | 25 | private fun Project.emptyJavaDocJar(): TaskProvider<*> = 26 | tasks.register("emptyJavadocJarFor${name.formatCapitalize()}", JavaDocJarTask::class.java) 27 | 28 | private fun Project.simpleJavadocJar(): TaskProvider<*> = 29 | tasks.register("simpleJavadocJarFor${name.formatCapitalize()}", JavaDocJarTask::class.java) { 30 | val task = tasks.named(JavaPlugin.JAVADOC_TASK_NAME) 31 | dependsOn(task) 32 | from(task) 33 | } 34 | 35 | private fun Project.dokkaJavadocJar(dokka: JavadocJarOptions.Dokka): TaskProvider<*> = 36 | tasks.register("dokkaJavadocJarFor${name.formatCapitalize()}", JavaDocJarTask::class.java) { 37 | val task = tasks.named(dokka.taskName) 38 | dependsOn(task) 39 | from(task) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /publish-plugin/src/main/java/com/dorck/android/upload/task/SourceJarTask.kt: -------------------------------------------------------------------------------- 1 | package com.dorck.android.upload.task 2 | 3 | import com.dorck.android.upload.extensions.androidProject 4 | import com.dorck.android.upload.extensions.formatCapitalize 5 | import org.gradle.api.Project 6 | import org.gradle.api.plugins.JavaPlugin 7 | import org.gradle.api.tasks.SourceSetContainer 8 | import org.gradle.api.tasks.TaskProvider 9 | import org.gradle.jvm.tasks.Jar 10 | import org.gradle.kotlin.dsl.get 11 | import org.gradle.kotlin.dsl.the 12 | 13 | open class SourceJarTask : Jar() { 14 | init { 15 | archiveClassifier.set("sources") 16 | } 17 | 18 | internal companion object { 19 | internal fun Project.javaSourceJarTask(packSource: Boolean): TaskProvider<*> = 20 | if (!packSource) { 21 | emptySourceJar() 22 | } else tasks.register("javaSourceJarFor${name.formatCapitalize()}", SourceJarTask::class.java) { 23 | dependsOn(JavaPlugin.CLASSES_TASK_NAME) 24 | val sourceSets = this@javaSourceJarTask.the() 25 | from(sourceSets["main"].allSource) 26 | } 27 | 28 | internal fun Project.kotlinSourceJarTask(packSource: Boolean): TaskProvider<*> = 29 | if (!packSource) { 30 | emptySourceJar() 31 | } else tasks.named("kotlinSourcesJar") 32 | 33 | internal fun Project.androidSourceJarTask(packSource: Boolean, variant: String): TaskProvider<*> = 34 | if (!packSource) { 35 | emptySourceJar() 36 | } else tasks.register("androidSourceJarFor$variant", SourceJarTask::class.java) { 37 | from(androidProject().sourceSets["main"].java.srcDirs) 38 | } 39 | 40 | private fun Project.emptySourceJar(): TaskProvider<*> = 41 | tasks.register("emptySourceJarFor${name.formatCapitalize()}", SourceJarTask::class.java) 42 | } 43 | } -------------------------------------------------------------------------------- /publish-plugin/src/test/kotlin/cn/dorck/publisher/test/ComponentPublisherPluginTest.kt: -------------------------------------------------------------------------------- 1 | package cn.dorck.publisher.test 2 | 3 | import org.gradle.testfixtures.ProjectBuilder 4 | import kotlin.test.Test 5 | import com.google.common.truth.Truth.assertThat 6 | 7 | /** 8 | * Unit tests for `cn.dorck.component.publisher` plugin. 9 | * @author Dorck 10 | * @since 2022/09/06 11 | */ 12 | class ComponentPublisherPluginTest { 13 | 14 | @Test 15 | fun `plugin registers extension test`() { 16 | val project = ProjectBuilder.builder().build() 17 | project.plugins.apply("cn.dorck.component.publisher") 18 | // Check if plugin extension registers succeed. 19 | assertThat(project.extensions.findByName("publishOptions")).isNotNull() 20 | } 21 | 22 | @Test 23 | fun `plugin registers task test`() { 24 | val project = ProjectBuilder.builder().build() 25 | // Check if plugin tasks registers succeed. 26 | assertThat(project.tasks.findByName("publishComponent")).isNotNull() 27 | } 28 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | maven { 7 | url '../Component-publisher/publish-plugin/build/repos/locals' 8 | } 9 | mavenLocal() 10 | } 11 | /*resolutionStrategy { 12 | eachPlugin { 13 | if (requested.id.id == 'component-publisher') { 14 | useModule("cn.dorck.android:component-publisher:1.0.0") 15 | } 16 | } 17 | }*/ 18 | } 19 | dependencyResolutionManagement { 20 | repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) 21 | repositories { 22 | google() 23 | mavenCentral() 24 | maven { url "https://jitpack.io" } 25 | maven { 26 | name 'GithubRepo' 27 | url 'https://maven.pkg.github.com/Moosphan/MavenRepo' 28 | credentials { 29 | username 'Moosphan' 30 | password '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' 31 | } 32 | } 33 | maven { 34 | url '../Component-publisher/publish-plugin/build/repos/locals' 35 | } 36 | mavenLocal() 37 | } 38 | } 39 | rootProject.name = "component-publisher" 40 | include ':app' 41 | include ':publish-plugin' 42 | include ':android-library-sample' 43 | include ':kotlin-library-sample' 44 | include ':java-library-sample' 45 | --------------------------------------------------------------------------------