前言

通过前面布置Jenkins主动化打包使命,以及桌面端APK工具的开发,根本现已把国内项目组所面对的问题处理了。可是呢,Jenkins这一套着实是繁琐,并且对海外项目组支持也不友好。所以我又决议开发一个gradle插件来作为一个轻量级的打包发布方案,该方案能够一起统筹公司海内外的一切项目。全体目标大概便是如下:

  1. 依靠assembleXxxRelease等Task构建新使命;
  2. 将打包生成的APK文件上传到FTP服务器;
  3. 依据服务器上APK文件的地址生成二维码;
  4. 发送通知消息到钉钉群组;

该插件现已开源,GitHub地址在:ApkPublisherGradlePlugin

插件的开发

gradle插件的开发又涉及多种办法,咱们这儿在不同的阶段别离运用不同办法进行演示。 首要运用AS新建GradleSample5项目。

学习阶段(build.gradle脚本)

在这个阶段咱们只需学习怎样创立使命(Task),并且了解怎样依靠使命即可。

首要创立使命“activeTask1”,咱们需求在app模块下的build.gradle文件中编写如下代码:

tasks.register("activeTask1") {
    doLast {
        println(">>>>> Greeting from activeTask1")
    }
}

点击 Sync Now 后就能够在右侧gradle面板中看到咱们新创立的使命了。

Gradle Plugin的开发及发布
双击该使命即可履行,并打印出日志信息:

11:42:39: Executing 'activeTask1'...
> Task :app:activeTask1
>>>>> Greeting from activeTask1
BUILD SUCCESSFUL in 284ms
1 actionable task: 1 executed
11:42:40: Execution finished 'activeTask1'.

接下来,咱们需求创立一个activeTask2,该使命需求依靠activeTask1,也便是说履行task2的时分要先履行task1,那么要害便是dependsOn()

tasks.register("activeTask2") {
    dependsOn("activeTask1")
    doLast {
        println(">>>>> Greeting from activeTask2")
    }
}

Gradle Plugin的开发及发布

那么这时分履行activeTask2的话,输出信息则如下所示:

11:46:41: Executing 'activeTask2'...
> Task :app:activeTask1
>>>>> Greeting from activeTask1
> Task :app:activeTask2
>>>>> Greeting from activeTask2
BUILD SUCCESSFUL in 180ms
2 actionable tasks: 2 executed
11:46:41: Execution finished 'activeTask2'.

所以这便是咱们后续依靠assemble相关使命所需求学习的一点前置内容了。

开发阶段(buildSrc)

buildSrc是Gradle项目的一个特别目录,咱们先看下官网给的全体介绍。

Complex build logic is usually a good candidate for being encapsulated either as custom task or binary plugin. Custom task and plugin implementations should not live in the build script. It is very convenient to use buildSrc for that purpose as long as the code does not need to be shared among multiple, independent projects. The directory buildSrc is treated as an included build. Upon discovery of the directory, Gradle automatically compiles and tests this code and puts it in the classpath of your build script. For multi-project builds there can be only one buildSrc directory, which has to sit in the root project directory. buildSrc should be preferred over script plugins as it is easier to maintain, refactor and test the code. buildSrc uses the same source code conventions applicable to Java and Groovy projects. It also provides direct access to the Gradle API. Additional dependencies can be declared in a dedicated build.gradle under buildSrc.

全体大致意思呢便是,gradle发现buildSrc目录后,会主动编译和测试其中的代码,会优先于其他的脚本。这对咱们开发插件来说就十分的便当了,假如咱们开发一个插件有必要先发布然后再从项目中依靠然后再履行的话,效率就大打折扣了。

咱们在项目的根目录下创立一个buildSrc的目录,然后创立一个build.gradle的文件放入该目录下,build.gradle文件内容如下所示:

Gradle Plugin的开发及发布

点击Sync Now后能够看到,buildSrc目录款式变了,阐明AS成功识别了该目录。

Gradle Plugin的开发及发布

接下来咱们来开发示例插件了,这儿咱们运用Java而不运用Groovy。因为其一我对Groovy不熟悉,其二Groovy太灵活了,我虽然能写出来相关的代码,能够我无法检查到相关的类提示等,这就导致我猎奇某个类、某个变量到底是怎样来的。所以我挑选了Java(Kotlin当然也不错),这样我能够明确知道相关类型,也方便检查源码。

Plugin

buildSrc目录建好后咱们创立src目录,然后树立插件类MyPlugin并实现Plugin接口如下:

Gradle Plugin的开发及发布
假如这个时分你跟我呈现相同的状况,例如上面的红色报错,并且办法中连一个简单的String类都无法导入,那么阐明AS的JDK装备呈现了问题。请先检查下文疑难杂症华章,处理后再进行插件的开发。

咱们实现apply办法后在其中打印相关信息:

import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class MyPlugin implements Plugin<Project> {
    @java.lang.Override
    public void apply(Project project) {
        System.out.println(">>>>> Greeting from buildSrc MyPlugin");
    }
}

然后在buildSrc的build.gradle中声明该插件:

plugins {
    id 'java-gradle-plugin'
}
gradlePlugin {
    plugins {
        pluginDemo {
            id = "my-plugin"
            implementationClass = 'com.vsloong.myplugin.MyPlugin'
        }
    }
}

在app模块下的build.gradle中增加该插件的依靠:

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    //增加插件的依靠
    id 'my-plugin'
}

Sync后即可看到相关打印信息:

…… Task :buildSrc:validatePlugins UP-TO-DATE
Task :buildSrc:check UP-TO-DATE
Task :buildSrc:build

Configure project :app
>>>>> Greeting from buildSrc MyPlugin

Task :prepareKotlinBuildScriptModel UP-TO-DATE

BUILD SUCCESSFUL in 13s

这表明咱们的插件现已成功运行起来了,接下来需求注册咱们自己的Task了。

Task

创立MyTask类承继自DefaultTask,这其中有两点需求留意: 1、在结构函数中增加分组(分组名自行界说),便于咱们找到该使命; 2、增加一个doTaskAction()(办法名自行界说)的办法打印相关信息,留意该办法需求运用 @TaskAction 注解;

import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
public class MyTask extends DefaultTask {
    public MyTask() {
        setGroup("myTaskGroup");
    }
    @TaskAction
    public void doTaskAction() {
        System.out.println(">>>>> Greeting from buildSrc MyTask");
    }
}

然后将MyTask注册到使命列表中,在MyPlugin的apply()办法中注册咱们的使命,使命名是myTask1:

import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class MyPlugin implements Plugin<Project> {
    @java.lang.Override
    public void apply(Project project) {
        System.out.println(">>>>> Greeting from buildSrc MyPlugin");
        project.getTasks().register("myTask1", MyTask.class);
    }
}

Sync项目后即可在右侧gradle面板中检查到咱们创立的使命组以及使命:

Gradle Plugin的开发及发布
双击myTask1使命即可在控制台检查到相关输出信息:

……
Task :app:myTask1
>>>>> Greeting from buildSrc MyTask

BUILD SUCCESSFUL in 2s

通过上述的一系列过程之后咱们对Plugin以及Task有了一个大致的了解了,接下来咱们回归到开始时分的使命,首要咱们需求创立自己的使命并依靠于项目中的assembleRelease等使命。

这儿咱们或许需求了解下Gradle的生命周期,见官网 Build Lifecycle,具体不再胪陈。这就比如咱们在Activity的生命周期中收到onCreate、onResume等回调相同,在Gradle的生命周期中也有相应的回调,比如咱们要用到的afterEvaluate(Action<? super Project> var1),在项目评估完后即可收到该回调,收到该回调后咱们就能够获取到项目中的一切使命,并刺进自己的使命。

接下来咱们先改造下MyTask,因为咱们的使命需求依靠于匹配到的assembleXxxRelease使命,所以咱们设置一个dependsOnTaskName变量,并且通过结构函数传递进来,然后调用dependsOn()办法进行依靠。

留意:结构函数中增加参数后就需求运用@Inject进行注解:

public class MyTask extends DefaultTask {
    private String dependsOnTaskName;
    @Inject
    public MyTask(String dependsOnTaskName) {
        setGroup("myTaskGroup");
        this.dependsOnTaskName = dependsOnTaskName;
        dependsOn(dependsOnTaskName);
    }
    @TaskAction
    public void doTaskAction() {
        System.out.println(">>>>> Greeting from buildSrc MyTask");
    }
}

然后在MyPlugin的apply()办法中,增加gradle项目评估完成的回调,在回调中获取一切匹配到的assembleXxxRelease使命,然后创立publishXxxRelease使命,一起记得传递需求依靠的使命名给MyTask。

public class MyPlugin implements Plugin<Project> {
    @java.lang.Override
    public void apply(Project project) {
        System.out.println(">>>>> Greeting from buildSrc MyPlugin");
        project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                for (Task task : project.getTasks()) {
                    String taskName = task.getName();
                    if (taskName.startsWith("assemble") && taskName.endsWith("Release")) {
                        String myTaskName = taskName.replace("assemble", "publish");
                        System.out.println(">>>>> 匹配到使命 = " + myTaskName);
                        project.getTasks().register(myTaskName, MyTask.class, taskName);
                    }
                }
            }
        });
    }
}

此刻,Sync项目后能够在右侧gradle面板中看到咱们创立的使命了:

Gradle Plugin的开发及发布
双击publishRelease使命能够从输出日志中看到,先履行了releaseAssemble使命后再履行的publisherRelease使命:

……
Task :app:packageRelease
……
Task :app:assembleRelease

Task :app:publishRelease
>>>>> Greeting from buildSrc MyTask

BUILD SUCCESSFUL in 1m 24s
49 actionable tasks: 43 executed, 6 up-to-date
11:25:03: Execution finished ‘publishRelease’.

上面是未装备项目Flavor的状况,假如项目中有多个Flavor的状况呢?比如咱们在app模块下的build.gradle中增加如下Flavor:

android {
    defaultConfig {
        //增加 产品、环境 两个维度
        flavorDimensions "product", "environment"
    }
    productFlavors {
        //产品1
        app1 {
            dimension "product"
        }
    	//产品2
        app2 {
            dimension "product"
        }
    	//开发环境
        develop {
            dimension "environment"
        }
    	//出产环境
        product {
            dimension "environment"
        }
    }
}    

Sync项目后即可在右侧看到咱们创立的使命组中多了新增的这些风味:

Gradle Plugin的开发及发布

Extension

要实现文章最初的业务,咱们还需求装备FTP地址,端口号,钉钉机器token人等信息,这就需求运用到Extension了。新建MyPluginExtension类:

public class MyPluginExtension {
    public String host;
    public String port;
}

然后在MyPlugin类中创立相关Extension的信息:

public class MyPlugin implements Plugin<Project> {
    @java.lang.Override
    public void apply(Project project) {
        project.getExtensions().create("myExtensionInfo", MyPluginExtension.class);
        //....
    }
}

读取的时分能够在需求的当地运用getExtension()办法读取,比如在MyTask类中:

public class MyTask extends DefaultTask {
    @TaskAction
    public void doTaskAction() {
        MyPluginExtension extension = getProject().getExtensions().getByType(MyPluginExtension.class);
        System.out.println(">>>>> extension.host = " + extension.host);
        System.out.println(">>>>> extension.port = " + extension.port);
    }
}

此刻履行相关使命后输出结果肯定为null:

Task :app:publishApp1ProductRelease
>>>>> extension.host = null
>>>>> extension.port = null

BUILD SUCCESSFUL in 1m 7s

在app模块下的build.gradle文件中最外层,增加咱们插件所需的装备信息如下:

myExtensionInfo {
    host = "192.168.1.1"
    port = 21
}

然后再履行相关使命就能拿到装备信息了:

Task :app:publishApp1ProductRelease
>>>>> extension.host = 192.168.1.1
>>>>> extension.port = 21

BUILD SUCCESSFUL in 3s

OK,至此咱们现已解说完了和咱们的目标相关的Plugin和Task内容了,剩余的关于二维码、FTP以及DingTalk SDK的运用请检查源码吧。

发布阶段(独立module)

通过上面两个阶段的内容咱们以及开发结束一个简陋的插件了,接下来咱们先测验发布到本地文件夹,然后再测验发布到MavenCentral中。

假如你是初次写插件,我主张你再新建一个工程,然后创立一个新的module将该工程中的buildSrc中的内容悉数拷贝到新module中,这样还简单处理一点,不会受上述阶段运用插件和装备插件信息的影响。

这儿呢,我是将buildSrc目录直接改名为了 module-plugin-my,然后在setting.gradle文件中包含该模块:

include ':module-plugin-my'

一起需求将app模块下的build.gradle做一下修正,注掉了自己的插件id,注掉了插件的装备信息:

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
//    id 'my-plugin'
}
//myExtensionInfo {
//    host = "192.168.1.1"
//    port = 21
//}

OK,接下来预备发布了。

发布到本地

先在module-plugin-my模块下的build.gradle中增加maven-publish插件,然后装备发布内容:

plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
}
publishing {
    publications {
        maven(MavenPublication) {
            groupId = "io.github.vsLoong"
            artifactId = "my-plugin"
            version = "0.0.1"
            from components.java
        }
    }
    repositories {
        maven {
            //发布到该工程下的localRepo文件夹下
            url = "../localRepo"
        }
    }
}
gradlePlugin {
    plugins {
        apkPublisher {
            id = "my-plugin"
            implementationClass = 'com.vsloong.myplugin.MyPlugin'
        }
    }
}

脚本内容如上所示,Sync工程后,能够在右侧gradle面板中找到publish使命组了:

Gradle Plugin的开发及发布

双击publish使命,构建结束后即可在工程目录下检查到生成的依靠库房:

Gradle Plugin的开发及发布

那么怎样运用该库房这儿有得好好说说了,7.0后的装备有些变化,可是运用之前的办法的话仍是没问题的,这儿就演示了通用的写法,在项目的build.gradle文件最初增加如下信息:

buildscript {
    repositories {
        maven {
            url("./localRepo")
        }
    }
    dependencies {
        classpath "io.github.vsLoong:my-plugin:0.0.1"
    }
}

然后在app模块下的build.gradle文件中运用该插件,并增加插件装备信息,Sync项目后即可成功看到咱们自界说的Tasks成功显示在gradle面板了:

plugins {
	//......
    id 'my-plugin'
}
myExtensionInfo {
    host = "192.168.1.1"
    port = 21
}

发布到MavenCentral

预备sonatype账号

MavenCentral是由sonatype运营的,所以首要咱们需求创立一个sonatype的账号,网上文章十分多了,这儿就提一点,现在填写groupId的时分现已不支持com.github最初的命名了,主张运用io.github.yourgroupid。

创立pgp证书

账号注册好并拥有上传权限后,咱们还需求的一个东西便是签名证书。相似咱们的apk文件需求签名后才干发布到运用商场相同,咱们的插件也需求一个签名。创立签名证书的话需求运用GPG4,我是Windows电脑所以运用了win工具,官网地址是:gpg4win.org 。我下载的版别是Gpg4win-4.0.4,页面如下所示。

Gradle Plugin的开发及发布

点击 文件-> New OpenPGP Key Pair… ,填写自己的名称和邮箱地址:

Gradle Plugin的开发及发布

填写完跋文得点击高档设置,将密钥类型设置为RSA,4096比特,OK后进入创立证书的页面,填写证书暗码,这儿的暗码需求记住后续会用到:

Gradle Plugin的开发及发布

创立好证书后,回到主页面右键该证书,然后导出公钥备份私钥

Gradle Plugin的开发及发布

导出和备份的时分也请有必要手动更改其文件的后缀名为pgp:

Gradle Plugin的开发及发布

最后右键你创立好的证书,然后挑选“在服务器上发布…”,发布成功后过段时间,在主面板中点击“在服务器上查找”,能找到自己名称的证书的话,则表明这一步现已成功了。

Gradle Plugin的开发及发布

装备sonatype和证书

通过上两步的操作后咱们有了sonatype的账号暗码以及库房的上传权限,还有了现已发布到服务器的证书。上传插件到库房需求这些信息,但这些信息又是不能暴漏到项目中的,否则这些信息被他人拿到岂不是能够随意上传内容到你的库房了,所以这儿的话咱们需求将这些信息装备到本地。首要检查你的Gradle user home文件夹在哪里,例如我这儿更改到的是D盘下AndroidGradle目录下:

Gradle Plugin的开发及发布

那么咱们就将备份的私钥文件拷贝到该目录,然后创立gradle.properties文件并输入如下内容:

# pgp信息
signing.keyId=证书密钥ID的后8位
signing.password=证书的暗码
signing.secretKeyRingFile=D\:\\AndroidGradle\\你的私钥文件.pgp
# sonatype账号、暗码
ossrhUsername=sonatype账号
ossrhPassword=sonatype暗码

发布Snapshot版别

上述的pgp证书及sonatype信息在本地设置好后,编辑module-plugin-my模块下的build.gradle文件(请细心依照如下脚本内容装备,防止犯错):

plugins {
    id 'java-gradle-plugin'
    //发布运用
    id 'maven-publish'
    id 'signing'
}
//发布所需的相关内容
def MAVEN_GROUP_ID = "io.github.vsLoong"
def MAVEN_ARTIFACT_ID = "my-plugin"
def MAVEN_ARTIFACT_VERSION = "0.0.1-SNAPSHOT"
def REPOSITORY_URL_SNAPSHOT = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
def REPOSITORY_URL_RELEASE = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
gradlePlugin {
    plugins {
        apkPublisher {
            id = MAVEN_ARTIFACT_ID
            implementationClass = 'com.vsloong.myplugin.MyPlugin'
        }
    }
}
// 将源码打包
task publishSourcesJar(type: Jar) {
    classifier = 'sources'
    exclude "**/R.class"  //扫除`R.class`
    exclude "**/BuildConfig.class"  //扫除`BuildConfig.class`
}
// mavenCentral要求有必要有Javadoc的信息
task publishJavadocsJar(type: Jar, dependsOn: publishSourcesJar) {
    archiveClassifier.set('javadoc')
}
afterEvaluate {
    publishing {
        publications {
            maven(MavenPublication) {
                groupId = MAVEN_GROUP_ID
                artifactId = MAVEN_ARTIFACT_ID
                version = MAVEN_ARTIFACT_VERSION
                from components.java
                artifact publishSourcesJar
                artifact publishJavadocsJar
                pom {
                    name = 'Apk publisher gradle plugin'
                    description = 'A gradle plugin that helps upload APK files to FTP servers and generate QR code images with download links.'
                    url = 'https://github.com/vsLoong/ApkPublisherGradlePlugin'
                    licenses {
                        license {
                            name = 'The Apache License, Version 2.0'
                            url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                        }
                    }
                    developers {
                        developer {
                            id = 'vsLoong'
                            name = 'vsLoong'
                            email = '你的邮箱地址'
                        }
                    }
                    scm {
                        connection = 'https://github.com/vsLoong/ApkPublisherGradlePlugin.git'
                        developerConnection = 'https://github.com/vsLoong/ApkPublisherGradlePlugin.git'
                        url = 'https://github.com/vsLoong/ApkPublisherGradlePlugin'
                    }
                }
            }
        }
        repositories {
            maven {
                allowInsecureProtocol true
                url = MAVEN_ARTIFACT_VERSION.endsWith('SNAPSHOT') ? REPOSITORY_URL_SNAPSHOT : REPOSITORY_URL_RELEASE
                credentials {
                    username = ossrhUsername
                    password = ossrhPassword
                }
            }
        }
    }
}
// 证书及签名信息
signing {
    sign publishing.publications
}

装备结束Sync项目后在右侧双击 publishMavenPublicationToMavenRepository(这个姓名取决于上述脚本中的相关命名),然后即可成功上传到MavenCentral了:

Gradle Plugin的开发及发布

请留意脚本中的一处版别判断的代码,假如版别号后缀是SNAPSHOT,那么会发布到Snapshots库房,否则会发布到Releases库房。发布到Snapshots库房的话没有那么严格,能够先发布到该库房进行测验。
发布到Snapshots库房成功后能够登录 s01.oss.sonatype.org/#welcome 进行检查,此刻在项目中也能够依靠到该插件的SNAPSHOT版别。

发布Release版别

发布到Releases库房的话,你的插件会先放在 暂存库 中,咱们需求点击左边的 Build Promotion -> Staging Repositories,然后选中你发布的插件操作Close以及Release过程。Close这一步会检测你发布上来内容的各种规范等,假如你在Close的阶段成功了,那么恭喜你少走了很多弯路,我所踩的坑都现已罗列在下文疑难杂症中了,一起上文脚本也根本是最完善的脚本了。

下面是终于close成功的截图,就差点击Release按钮就能够发布了。Release后再耐心等等就能够从Maven Central中查找到你的插件了,查找地址在 search.maven.org/。

Gradle Plugin的开发及发布

疑难杂症

JDK路径

也有或许你树立Android工程的时分AS现已提示过你了,例如下图这种状况。

Gradle Plugin的开发及发布
可是离谱的状况便是你依照提示select a JDK 处理完后,即便是挑选了自己安装的JDK11的路径,可是AS仍旧不收效。
Gradle Plugin的开发及发布

这时咱们能够检查下 .idea/misc.xml文件,相关装备如下:

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
    <component name="ExternalStorageConfigurationManager" enabled="true" />
    <component name="ProjectRootManager" default="true" languageLevel="JDK_11"
        project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK" version="2">
        <output url="file://$PROJECT_DIR$/build/classes" />
    </component>
    <component name="ProjectType">
        <option name="id" value="Android" />
    </component>
</project>

project-jdk-name字段的值是Android Studio default JDK,项目左边的依靠中依旧是Android Studio default JDK。

Gradle Plugin的开发及发布
所以假如咱们运用上面自行安装的JDK11版别的话,直接手动把 project-jdk-name=”Android Studio default JDK” 修正为 project-jdk-name=”11″,然后重启Android Studio即可。

假如这时分你的AS仍是无法识别这种状况,那么你就用IntelliJ IDEA打开这个项目,然后IDEA也会提示你JDK有问题,依照提示装备一遍应该也能够了。假如还有其他状况我也束手无策了,我就在这个破问题上卡了一天。

发布到库房

Received status code 403 from server: Forbidden

Execution failed for task ':apk-publisher-gradle-plugin:publishPluginMavenPublicationToMavenRepository'.
> Failed to publish publication 'pluginMaven' to repository 'maven'
> Could not PUT 'https://s01.oss.sonatype.org/content/repositories/snapshots/ApkPublisherGradlePlugin/apk-publisher-gradle-plugin/unspecified/apk-publisher-gradle-plugin-unspecified.jar'. Received status code 403 from server: Forbidden
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

处理方案增加sign,或许服务器上还未查询到签名。

It may not be a PGP secret key ring

Execution failed for task ':apk-publisher-gradle-plugin:signMavenPublication'.
> Unable to read secret key from file: E:\WorkSpace\ApkPublisherGradlePlugin\vsLoong_0x000000_SECRET.pgp (it may not be a PGP secret key ring)

请有必要将导出时分默许的asc后缀,修正为pgp后缀。假如是导出了asc格局的文件后,又手动修正文件名后缀为pgp同样不能够,也会报错如上。

Gradle Plugin的开发及发布

Unknown public key algorithm encountered

请有必要在创立时挑选高档设置,将密钥类型更改为 RSA。

Gradle Plugin的开发及发布

CLOSE过程

假如你在编写发布脚本的时分未依照上文脚本增加sourcesJar,javadocJar,以及有必要的POM信息,那么在发布Release版别操作close过程时或许会遇到如下过错,请认真依照上述发布脚本进行检查。

Gradle Plugin的开发及发布

Event: Failed: Javadoc Validation

typeId javadoc-staging failureMessage Missing: no javadoc jar found in folder ‘/io/github/vsLoong/apk-publisher-gradle-plugin/1.0.0’

Event: Failed: POM Validation

typeId pom-staging failureMessage Invalid POM: /io/github/vsLoong/apk-publisher-gradle-plugin/1.0.0/apk-publisher-gradle-plugin-1.0.0.pom: Project name missing, Project description missing, Project URL missing, License information missing, SCM URL missing, Developer information missing

Event: Failed: Sources Validation

typeId sources-staging failureMessage Missing: no sources jar found in folder ‘/io/github/vsLoong/apk-publisher-gradle-plugin/1.0.0’

结束

该插件已开源,GitHub地址在:ApkPublisherGradlePlugin 。 最后,祝大家一路披荆斩棘,都能顺利发布好玩的gradle插件吧。

参考文章

  • Android Studio中不能展开jdk源码的问题
  • Maven Publish Plugin
  • Signatory credentials
  • The Signing Plugin
  • The Central Repository Documentation
  • Missing: no javadoc jar found in folder处理办法