No artigo anterior, apresentamos como usar o plug-in Gradle, reforço de apk e upload para o Dandelion.
Este artigo é principalmente para melhorar ainda mais o processo, através do plug-in Gradle para alcançar: empacotamento - reforço - upload dandelion - enviar mensagem DingTalk , para alcançar a automação completa.
Introdução aos artigos anteriores:
Gradle Custom Plugin Introdução
Gradle Custom Plugin Upload APK para Dandelion
Reforço Gradle Custom Plugin 360
A seguir, vamos ver o processo simples de envio de notificações do DingTalk:
- Crie um robô DingTalk. Obtenha credenciais para enviar mensagens.
- Chame a API de acordo com o formato de dados exigido pelas credenciais e DingTalk
A documentação da API do DingTalk contém descrições detalhadas do processo, portanto não entrarei em detalhes aqui.
Desenvolver mensagem DingTalk Fluxo de tarefa da tarefa:
- 1. Crie um robô DingTalk
- 2. Crie uma tarefa Tarefa para enviar mensagens DingTalk
- 3. Modifique e carregue a tarefa Dandelion Task
- Quarto, modifique as dependências das tarefas no plug-in Plugin
1. Crie um robô DingTalk
Para enviar mensagens DingTalk, precisamos criar um bot DingTalk no grupo DingTalk. Após criar o robô DingTalk, envie mensagens para o grupo DingTalk através do Webhook e segredo do robô.
1. Clique no assistente de grupo inteligente do grupo
2. Clique em Criar Robô
Clique para adicionar um robô (permissão pode ser necessária). Isso requer operação do computador. Depois de criar o robô, você obterá o Webhook e o segredo.
Esses dois parâmetros são o que precisamos para enviar dados.
Após a criação do robô, obtivemos o webhook e o secret, que são as credenciais para envio de mensagens DingTalk.
Após criar o robô, podemos chamar a API através das credenciais de acordo com a documentação da API do DingTalk . Aqui eu não uso o SDK oficial, mas envio mensagens pelo OKHttp de acordo com o formato do documento.
2. Crie uma tarefa Tarefa para enviar mensagens DingTalk
De acordo com a documentação, sabemos que para enviar o DingTalk, devemos passar os parâmetros: webhook, secret .
Em seguida, selecione diferentes parâmetros de acordo com o tipo de mensagem que enviamos, aqui usamos o tipo de link para enviar mensagens:
{
"msgtype": "link",
"link": {
"text": "这个即将发布的新版本,创始人xx称它为“红树林”。
而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是“红树林”?",
"title": "时代的火车向前开",
"picUrl": "",
"messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"
}
}
Aqui vemos que precisamos fazer o upload: text, title, picUrl, messageUrl , os quatro parâmetros
picUrl, link messageUrl, devem ser obtidos após o upload para o Dandelion.
Nesse caso, precisamos que Extension adicione 4 parâmetros: webhook, secret, text, title . Os outros dois parâmetros (picUrl, messageUrl) precisam ser obtidos após o upload do Dandelion com sucesso.
Depois que os parâmetros acima estiverem prontos, precisamos apenas chamar a API por meio de OKHttp para concluir.
As preparações incluem:
- Crie uma classe de modelo que atenda às necessidades do DingTalk
- Adicionar parâmetros à extensão (os quatro parâmetros analisados acima)
- Obtenha o parâmetro Url após o upload do Dandelion com sucesso
- Analisando os parâmetros da extensão
- API de chamada para enviar mensagem DingTalk
1. Construa uma classe Model que atenda às necessidades do DingTalk
O envio do DingTalk é entregue através do Json, primeiro construímos o modelo que ele precisa
public static class DingTalkModel {
public String picUrl;
public String messageUrl;
public String title;
public String text;
}
public static class DingTalkRequest {
public String msgtype = "link";
public DingTalkModel link;
public DingTalkRequest(DingTalkModel link) {
this.link = link;
}
}
2. Adicione parâmetros à extensão
Existem principalmente quatro parâmetros aqui: credenciais de upload (webhook e segredo), parâmetros de envio de mensagem (título, conteúdo).
Adicione parâmetros à classe Extension.
public class Extension {
public String uKey;
public String apiKey;
public String appName;
//==========加固相关的信息
//指向加固的jar包
public File reinforceJarFile;
//登陆用户名
public String reinforceUsername;
//登陆密码
public String reinforcePassword;
//输出apk的目录
public File outputFile;
//dingding
public String dingWebhook;
public String dingSecret;
public String title;
public String content;
public Extension() {
}
public Extension(String uKey, String apiKey, String appName, File reinforceJarFile,
String reinforceUsername, String reinforcePassword, File outputFile,
String dingWebhook, String dingSecret, String title, String content) {
this.uKey = uKey;
this.apiKey = apiKey;
this.appName = appName;
this.reinforceJarFile = reinforceJarFile;
this.reinforceUsername = reinforceUsername;
this.reinforcePassword = reinforcePassword;
this.outputFile = outputFile;
this.dingWebhook = dingWebhook;
this.dingSecret = dingSecret;
this.title = title;
this.content = content;
}
public Extension(String uKey, String apiKey, String appName, File reinforceJarFile,
String reinforceUsername, String reinforcePassword, File outputFile) {
this.uKey = uKey;
this.apiKey = apiKey;
this.appName = appName;
this.reinforceJarFile = reinforceJarFile;
this.reinforceUsername = reinforceUsername;
this.reinforcePassword = reinforcePassword;
this.outputFile = outputFile;
}
public Extension(String appName, String uKey, String apiKey) {
this.uKey = uKey;
this.apiKey = apiKey;
this.appName = appName;
}
public static Extension getConfig(Project project) {
Extension extension = project.getExtensions().findByType(Extension.class);
if (extension == null) {
extension = new Extension();
}
return extension;
}
}
3. Obtenha o parâmetro Url após fazer o upload do Dandelion com sucesso
Definimos propriedades estáticas na tarefa DingTalk. Quando a tarefa de upload do Dandelion for concluída, chame o método setData() para passar os parâmetros.
public class DingTalkTask extends DefaultTask {
private static String sShotcutUrl, sQRCodeURL;
...
public static void setData(String shortcutUrl, String qrUrl) {
sShotcutUrl = shortcutUrl;
sQRCodeURL = qrUrl;
}
}
4. Analisar os parâmetros da Extensão
Aqui está a preparação dos parâmetros antes da rede. Precisamos analisar os parâmetros necessários aqui e colocá-los na classe do modelo.
private DingTalkModel initModel() {
DingTalkModel model = new DingTalkModel();
model.picUrl = sQRCodeURL;
model.messageUrl = "http://www.pgyer.com/" + sShotcutUrl;
Extension extension =Extension.getConfig(mTargetProject);
model.title = extension.title;
model.text = extension.content;
mWebhook = extension.dingWebhook;
mSecret = extension.dingSecret;
return model;
}
Crie aqui o modelo requerido pelo DingTalk e obtenha todos os parâmetros correspondentes.
5. Chame a API para enviar a mensagem DingTalk
Isso, chame diretamente usando OKHttp
@TaskAction
public void sendDingtalk() {
DingTalkModel model = initModel();
String url = createDingUrl();
OkHttpClient okHttpClient = new OkHttpClient();
Gson gson = new Gson();
String json = gson.toJson(new DingTalkRequest(model));
RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
System.out.println("send message result: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* create dingtalk url
*/
private String createDingUrl() {
Long timestamp = System.currentTimeMillis();
return mWebhook + "×tamp=" + timestamp + "&sign=" + createSign(timestamp, mSecret);
}
private String createSign(Long timestamp, String secret) {
String stringToSign = timestamp + "\n" + secret;
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
Neste ponto, nossa Tarefa DingTalk é criada. Poste o código completo.
public class DingTalkTask extends DefaultTask {
private static String sShotcutUrl, sQRCodeURL;
private BaseVariant mVariant;
private Project mTargetProject;
private String mWebhook;
private String mSecret;
public static class DingTalkModel {
public String picUrl;
public String messageUrl;
public String title;
public String text;
}
public static class DingTalkRequest {
public String msgtype = "link";
public DingTalkModel link;
public DingTalkRequest(DingTalkModel link) {
this.link = link;
}
}
public void init(BaseVariant variant, Project project) {
this.mVariant = variant;
this.mTargetProject = project;
setDescription("send message to ding talk");
setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
}
@TaskAction
public void sendDingtalk() {
DingTalkModel model = initModel();
String url = createDingUrl();
OkHttpClient okHttpClient = new OkHttpClient();
Gson gson = new Gson();
String json = gson.toJson(new DingTalkRequest(model));
RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
System.out.println("send message result: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* create dingtalk url
*/
private String createDingUrl() {
Long timestamp = System.currentTimeMillis();
return mWebhook + "×tamp=" + timestamp + "&sign=" + createSign(timestamp, mSecret);
}
private DingTalkModel initModel() {
DingTalkModel model = new DingTalkModel();
model.picUrl = sQRCodeURL;
model.messageUrl = "http://www.pgyer.com/" + sShotcutUrl;
Extension extension =Extension.getConfig(mTargetProject);
model.title = extension.title;
model.text = extension.content;
mWebhook = extension.dingWebhook;
mSecret = extension.dingSecret;
return model;
}
private String createSign(Long timestamp, String secret) {
String stringToSign = timestamp + "\n" + secret;
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static void setData(String shortcutUrl, String qrUrl) {
sShotcutUrl = shortcutUrl;
sQRCodeURL = qrUrl;
}
}
3. Modifique e carregue a tarefa Dandelion Task
Conforme mencionado acima, após a conclusão do upload, precisamos passar a URL para a tarefa DingTalk Task.
public class PGYUploadTask extends DefaultTask {
private BaseVariant mVariant;
private Project mTargetProject;
public static class PGYRequest {
public String uKey;
public String apiKey;
//1,install by public 2,install by password 3,install by invite
public String installType;
}
public void init(BaseVariant variant, Project project) {
this.mVariant = variant;
this.mTargetProject = project;
setDescription("upload to pgy");
setGroup(TestJavaPlugin.PLUGIN_EXTENSION_NAME);
}
@TaskAction
public void uploadToPGY() {
Extension extension = Extension.getConfig(mTargetProject);
PGYRequest request = new PGYRequest();
request.apiKey = extension.apiKey;
request.uKey = extension.uKey;
File apkDir = extension.outputFile;
if (apkDir == null || !apkDir.exists()) {
upload(request);
} else {
File[] files = apkDir.listFiles();
if (files != null && files.length > 0) {
upload(request.uKey, request.apiKey, files[0]);
} else {
upload(request);
}
}
}
private void upload(PGYRequest request) {
for (BaseVariantOutput output : mVariant.getOutputs()) {
File file = output.getOutputFile();
if (file == null || !file.exists()) {
throw new GradleException("apk file is not exist!");
}
upload(request.uKey, request.apiKey, file);
}
}
private void upload(String ukey, String apiKey, File apkFile) {
//builder
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
//add part
bodyBuilder.addFormDataPart("uKey", ukey);
bodyBuilder.addFormDataPart("_api_key", apiKey);
//add file
bodyBuilder.addFormDataPart("file", apkFile.getName(), RequestBody
.create(MediaType.parse("*/*"), apkFile));
//request
Request request = new Request.Builder()
.url("http://upload.pgyer.com/apiv1/app/upload")
.post(bodyBuilder.build())
.build();
OkHttpClient client = new OkHttpClient();
try {
Response response = client.newCall(request).execute();
String result = response.body().string();
System.out.println("upload result: " + result);
parseResult(result);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void parseResult(String result) {
if (result == null || result.length() == 0) {
throw new IllegalArgumentException("upload apk to PGY failed");
}
PGYResonpse data = new Gson().fromJson(result, PGYResonpse.class);
if (data == null || data.code != 0) {
throw new IllegalArgumentException("upload apk to PGY failed");
}
DingTalkTask.setData(data.data.appShortcutUrl, data.data.appQRCodeURL);
}
public static class PGYResonpse {
public int code;
public String message;
public PGYDetail data;
public static class PGYDetail {
public String appShortcutUrl;
public String appUpdated;
public String appQRCodeURL;
public String appVersion;
public String appVersionNo;
public String appIcon;
}
}
}
Aqui, primeiro crie a classe Model para os resultados retornados pelo Dandelion; depois de obter os resultados carregados do Dandelion, passe os dados para a tarefa DingTalk Task por meio do método DingTalkTask.setData().
Quarto, modifique as dependências das tarefas no plug-in Plugin
Aqui, é principalmente para modificar as dependências da tarefa no plug-in Plugin. Deixe a Tarefa do DingTalk depender da tarefa de upload do Dandelion, para que a notificação do DingTalk seja enviada após o upload ser bem-sucedido.
public class TestJavaPlugin implements Plugin<Project> {
public static final String PLUGIN_EXTENSION_NAME = "uploadHelperJava";
public static final String ANDROID_EXTENSION_NAME = "android";
@Override
public void apply(Project project) {
project.getExtensions()
.create(PLUGIN_EXTENSION_NAME, Extension.class);
//项目编译完成后,回调
project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
DomainObjectSet<ApplicationVariant> appVariants = ((AppExtension) project
.getExtensions().findByName(ANDROID_EXTENSION_NAME)).getApplicationVariants();
for (ApplicationVariant variant : appVariants) {
//release apk
if (variant.getBuildType().getName().equalsIgnoreCase("uploadRelease")) {
String variantName =
variant.getName().substring(0, 1).toUpperCase() + variant.getName()
.substring(1);
//create task
ReinforceTask reinforceTask = project.getTasks()
.create("reinforceFor" + variantName, ReinforceTask.class);
reinforceTask.init(variant, project);
PGYUploadTask uploadTask = project.getTasks()
.create("uploadJavaFor" + variantName, PGYUploadTask.class);
uploadTask.init(variant, project);
DingTalkTask talkTask = project.getTasks()
.create("dingTalkFor" + variantName, DingTalkTask.class);
talkTask.init(variant, project);
//depend on
variant.getAssembleProvider().get()
.dependsOn(project.getTasks().findByName("clean"));
reinforceTask.dependsOn(variant.getAssembleProvider().get());
uploadTask.dependsOn(reinforceTask);
talkTask.dependsOn(uploadTask);
}
}
}
});
}
}
Aqui, também criamos a Tarefa DingTalk. Em seguida, as dependências foram modificadas. Neste ponto, todo o nosso processo: empacotamento – fortalecimento – upload do Dandelion – envio de notificações do DingTalk está completo.
Cinco, ligue
O lançamento de plug-ins e a introdução de plug-ins foram mencionados em artigos anteriores (há um link no início), então não vou escrevê-los aqui.
Vejamos o build.gradle no módulo do aplicativo que precisa chamar o plug-in
apply plugin: 'com.android.application'
android {
......
signingConfigs {
release {
storeFile file('xxx.jks')
storePassword "xxx"
keyAlias "xxx"
keyPassword "xxx"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
uploadRelease {
minifyEnabled false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
......
apply plugin: 'com.liu.alone.plugin'
uploadHelperJava {
appName = "测试APP"
uKey = "xxxx"
apiKey = "xxxx"
//加固相关的信息
reinforceJarFile = file("../libs/jiagu.jar")
reinforceUsername = "18032793516"
reinforcePassword = "san7758521"
outputFile = file("${buildDir.getAbsolutePath()}\\jiagu")
//钉钉,这里需要修改成自己的值
dingWebhook = "https://oapi.dingtalk.com/robot/send?access_token=xxx"
dingSecret= "xxx"
title = "新版本APP上传完成"
content="体验新版本APP,请到蒲公英下载"
}
Chamada de tarefa:
Resultado da impressão:
> Task :app:reinforceForUploadRelease
==============start JiaGu
....
任务完成_已签名
> Task :app:uploadJavaForUploadRelease
upload result: {"code":0,"message":"","data":{"appKey":"3e95fe0e7fa74e9f7a310bbdfbd139cc","userKey":"c9d2625c0cf221d8f4a98738f4c05e9a","appType":"2","appIsLastest":"2","appFileSize":"3264349","appName":"PluginTest","appVersion":"1.0","appVersionNo":"1","appBuildVersion":"4","appIdentifier":"com.liu.plugintest","appIcon":"9664daaa8a725f726bcb6018495f14a7","appDescription":"","appUpdateDescription":"","appScreenshots":"","appShortcutUrl":"DqmH","appCreated":"2020-01-03 12:21:24","appUpdated":"2020-01-03 12:21:24","appQRCodeURL":"http:\/\/www.pgyer.com\/app\/qrcodeHistory\/9c27fd62288ce774fafb622d8f6df0bbcd9f90cf0b9ba39846c1c6864c93b135"}}
> Task :app:dingTalkForUploadRelease
send message result: {"errcode":0,"errmsg":"ok"}
Aqui, vemos que três tarefas foram executadas sucessivamente: Task :app:reinforceForUploadRelease, Task :app:uploadJavaForUploadRelease, Task :app:dingTalkForUploadRelease
Finalmente, notificação DingTalk
Neste ponto, todo o nosso processo automatizado de lançamentos de teste: empacotamento — reforço — upload do Dandelion — envio de notificações DingTalk é realizado personalizando o plug-in Gradle.
outros documentos
plugin-build.gradle
apply plugin: 'java-library'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.android.tools.build:gradle:3.3.1'
implementation("com.squareup.okhttp3:okhttp:3.8.1")
}
repositories {
mavenCentral()
}
group = 'com.liu.alone.plugin'
version = '1.0.0'
archivesBaseName = 'java-plugin'
//
//upload
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}