Android 使用Jenkins 自动化多渠道打包并且分发到蒲公英、下发到钉钉通知【即拿即用】

news/2024/4/20 20:29:44/文章来源:https://blog.csdn.net/Android_LeeJiaLun/article/details/127239497

前言

一、tomcat 安装启动
二、jenkins war 包下载并安装
三、jenkins 配置教程
四、jenkins items 工程配置
五、android gradle 脚本编码
六、分发到蒲公英脚本编码以及七、通知钉钉逻辑编码

前言

Android 在每个版本测试阶段,通常会因为修复BUG 去验证,都会打出大量的apk,为了方便开发人员和测试人员,就需要我们把打包权利交于测试人员,让他们拥有打包->下载安装一条龙。这样不仅仅方便了测试和开发,在版本流程上也会规范一下,还起到了版本归档的作用。下面就是从tomcat 到自动发布蒲公英以及通知钉钉群(也有人用飞书)的流程。

一、tomcat 安装启动

  • 1、tomcat 下载

tomcat 下载地址

下载后配置环境变量
在这里插入图片描述

修改端口号路径:

F:\Program Files\Apache Software Foundation\Tomcat 9.0\conf\server.xml

<Connector executor="tomcatThreadPool"port="9090" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />-->

验证tomcat

cd 进入F:\Program Files\Apache Software Foundation\Tomcat 9.0\bin\startup.bat
.
启动startup.bat,然后浏览器输入localhost:9090

在这里插入图片描述

二、jenkins war 包下载并安装

jenkins 下载地址
在这里插入图片描述
这里可以下载jenkins任意版本,建议下载最新的。

在这里插入图片描述
点击jenkins.war 下载到本地。
将jenkins.war 文件放到一下tomcat 目录下:

F:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps…

启动tomcat 并在浏览器输入:
http://localhost:9090/jenkins

在这里插入图片描述
密码在对应的/home/tomcat/.jenkins/secrets/initialAdminPassword 目录下

在这里插入图片描述
进来后选择第一个安装推荐的插件

在这里插入图片描述

以上就是jenkins 安装的过程。

三、jenkins 配置教程

  • 1、设置ANDROID_HOME 环境变量(要和本身的安卓环境变量地址一样)

  • 2.、jdk 环境变量配置(jdk 环境变量要和Android Studio 中的一致,否则编译会失败)

  • 3、git 配置

  • 4、gradle 配置

  • step1:设置ANDROID_HOME 环境变量(要和本身的安卓环境变量地址一样)
    在这里插入图片描述
    点击Manage Jenkins 进入设置列表,并进入系统设置

在这里插入图片描述
设置ANDROID_HOME 环境变量

  • step2:、jdk 环境变量配置(jdk 环境变量要和Android Studio 中的一致,否则编译会失败)

进入全局设置

在这里插入图片描述
查看自己Android Studio 中jdk 的路径。
在这里插入图片描述

  • step3:、git 配置
    在这里插入图片描述

  • step4:、gradle 配置(要和Android 项目中 gradle 版本一致)
    在这里插入图片描述
    以上的配置就可以。

四、jenkins items 工程配置

  • step1:新建一个新的任务Item 步骤如下
    在这里插入图片描述

  • step2:配置
    在这里插入图片描述

任务配置会分为以下6点
在这里插入图片描述

  • 1、General(常规设置)

  • 2.、源码管理

  • 3、构建触发器

  • 4、构建环境

  • 5、构建步骤

  • 6、构建后操作
    对于android 自动化分发,只需要配置 1、2、5 就够用了。

  • step 1、General(常规设置)
    在这里插入图片描述
    设置一下参数

在这里插入图片描述

这里可以根据自己的需求选择其中一种参数配置。博主大部分使用Choice Parameter,主要是为了做一些标识和渠道分发的配置。

在这里插入图片描述

  • step 2、源码管理

设置git 仓库路径,并添加秘钥管理。
在这里插入图片描述

  • 5、构建步骤
    在这里插入图片描述

Gradle 选择之前系统设置中配置好的。
而Task 名称是Android build.gradle 写好的某个任务,下面会有说到。
这里有一个配置非常重要,就是Jenkins 透传(意思是,Jenkins 选中的参数,可以透传到Android 项目中使用)
点开高级设置如下:
在这里插入图片描述
设置好上面后,点击保存
在这里插入图片描述
至此Jenkins 的配置已经全部结束,下面回到android 的脚本配置

五、android gradle 脚本编码

  • 2.透传设置(重要)
    在项目gradle.properties 文件中配置之前在jenkins 设置的透传参数
# jenkins 透传
PRODUCT_FLAVORS=Quest_Self_all_21100_26
BUILD_TYPE=Debug
TY_URL="https://ecx.ctyun.cn"
  • 2.productFlavors 多渠道配置
    下面是我的三个渠道
flavorDimensions("app", "manufacturer", "version")productFlavors {_all {dimension "manufacturer"}quest_Self {dimension "app"applicationId "com.xxx"versionCode getVersionCode()resValue "string", "app_use_name", appName(true)manifestPlaceholders = [app_icon: "@mipmap/ic_launcher"]buildConfigField "int", "customType", "0"}quest_TY {dimension "app"applicationId "com.xxx"versionCode getVersionCode()resValue "string", "app_use_name", appName(false)manifestPlaceholders = [app_icon: "@mipmap/icon_luancher"]buildConfigField "int", "customType", "2"}quest_HW {dimension "app"applicationId "com.xxx"versionCode getVersionCode()resValue "string", "app_use_name", appName(true)manifestPlaceholders = [app_icon: "@mipmap/ic_launcher"]buildConfigField "int", "customType", "3"}_21100_26 {targetSdkVersion 31versionName getVersionName()dimension "version"}}
  • **3.task packageApk 打包设置 **
    task packageApk {println("BUILD_TYPE:" + BUILD_TYPE)File apkFileDir = null//这一步就是根据透传的渠道名拼接上打包类型(debug\release)组成打包脚本,这里其实就已经完成打包了dependsOn("assemble" + PRODUCT_FLAVORS.capitalize() + BUILD_TYPE.capitalize())//下面就是涉及上传到蒲公英了//因为透传的参数刚好和分发的名字一样,因为apk 存放的路径是小写,所以转换一下大小写(大写是因为task会自动将首位字母转成大写)def filePath = PRODUCT_FLAVORS.capitalize().replace("Q", "q")if (BUILD_TYPE.toLowerCase() == "release") {apkFileDir = new File(project.buildDir, "outputs/apk/" + filePath + "/release")} else {apkFileDir = new File(project.buildDir, "outputs/apk/" + filePath + "/debug")}println("uploadFile" + apkFileDir.path)doLast {rootProject.ext.copyApk(apkFileDir)}}

六、分发到蒲公英脚本编码以及七、通知钉钉逻辑编码

新建一个gradle 文本取名upload.gradle

在这里插入图片描述

import groovy.json.JsonSlurperimport java.text.SimpleDateFormatext.copyApk = { file ->def mFile = findApkFile(file)uploadApk(mFile)
}def findApkFile(File file) {File mFile = nullprintln("apkFile:" + file.name)if (file.isDirectory()) {def files = file.listFiles()for (int i = 0; i < files.length; i++) {def findFile = findApkFile(files[i])if (findFile != null) {return findFile}}} else if (file.name.endsWith(".apk")) {mFile = filereturn mFile} else {return mFile}
}def uploadApk(File uploadApkFile) {// 查找上传的 apk 文件, 这里需要换成自己 apk 路径println("uploadApk:" + uploadApkFile.absolutePath + "--" + uploadApkFile.exists())if (uploadApkFile == null || !uploadApkFile.exists()) {throw new RuntimeException("apk file not exists!")}println "*************** upload start ***************"String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成String PREFIX = "--", LINE_END = "\r\n";String CONTENT_TYPE = "multipart/form-data"; // 内容类型try {URL url = new URL("https://www.pgyer.com/apiv2/app/upload");HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setReadTimeout(30000);conn.setConnectTimeout(30000);conn.setDoInput(true); // 允许输入流conn.setDoOutput(true); // 允许输出流conn.setUseCaches(false); // 不允许使用缓存conn.setRequestMethod("POST"); // 请求方式conn.setRequestProperty("Charset", "UTF-8"); // 设置编码conn.setRequestProperty("connection", "keep-alive");conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);DataOutputStream dos = new DataOutputStream(conn.getOutputStream());StringBuffer sb = new StringBuffer();sb.append(PREFIX).append(BOUNDARY).append(LINE_END);//分界符sb.append("Content-Disposition: form-data; name=\"" + "_api_key" + "\"" + LINE_END);sb.append("Content-Type: text/plain; charset=UTF-8" + LINE_END);//sb.append("Content-Transfer-Encoding: 8bit" + LINE_END);sb.append(LINE_END);sb.append("替换成蒲公英上申请的apiKey");//替换成蒲公英上申请的apiKey (重要)sb.append(LINE_END);//换行!if (uploadApkFile != null) {/*** 当文件不为空,把文件包装并且上传*/sb.append(PREFIX);sb.append(BOUNDARY);sb.append(LINE_END);/*** 这里重点注意: name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件* filename是文件的名字,包含后缀名的 比如:abc.png*/sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + uploadApkFile.getName() + "\"" + LINE_END);sb.append("Content-Type: application/octet-stream; charset=UTF-8" + LINE_END);sb.append(LINE_END);dos.write(sb.toString().getBytes())InputStream is = new FileInputStream(uploadApkFile)byte[] bytes = new byte[1024];int len = 0;while ((len = is.read(bytes)) != -1) {dos.write(bytes, 0, len);}is.close();dos.write(LINE_END.getBytes());byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();dos.write(end_data);dos.flush();/*** 获取响应码 200=成功 当响应成功,获取响应的流*/int res = conn.getResponseCode();if (res == 200) {println("Upload request success");BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))StringBuffer ret = new StringBuffer();String linewhile ((line = br.readLine()) != null) {ret.append(line)}String result = ret.toString();def resp = new JsonSlurper().parseText(result)println resultprintln "*************** upload finish ***************"sendMsgToDing(resp.data)} else {//发送钉钉 消息--构建失败}}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}/* 一下为发送到钉钉的逻辑*/def sendMsgToDing(def data) {def conn = new URL("替换成钉钉Webhook地址").openConnection()
//替换成自己的钉钉webHook的urlconn.setRequestMethod('POST')conn.setRequestProperty("Connection", "Keep-Alive")conn.setRequestProperty("Content-type", "application/json;charset=UTF-8")conn.setConnectTimeout(30000)conn.setReadTimeout(30000)conn.setDoInput(true)conn.setDoOutput(true)def dos = new DataOutputStream(conn.getOutputStream())def downloadUrl = "https://www.pgyer.com/" + data.buildShortcutUrldef qrCodeUrl = "![](" + data.buildQRCodeURL + ")"
//    def detailLink = "[项目地址](${BUILD_URL})"def _title = "### ${PRODUCT_FLAVORS}构建成功ljl" //这句话非常重要,需要将钉钉自定义关键词拼接起来,要识别。def _content = new StringBuffer()_content.append("\n\n### 构建成功")
//    _content.append("\n\n构建版本:${BRANCH_NAME}")_content.append("\n\n构建类型:${BUILD_TYPE}")_content.append("\n\n下载地址:" + downloadUrl)_content.append("\n\n" + qrCodeUrl)
//    _content.append("\n\n构建用户:${BUILD_USER}")_content.append("\n\n构建时间:" + getNowTime())
//    _content.append("\n\n查看详情:" + detailLink)def json = new groovy.json.JsonBuilder()json {msgtype "markdown"markdown {title _titletext _content.toString()   //里面内容一定要包含有钉钉群设置的关键词}at {atMobiles([])isAtAll false}}println(json)dos.writeBytes(json.toString())def input = new BufferedReader(new InputStreamReader(conn.getInputStream()))String line = ""String result = ""while ((line = input.readLine()) != null) {result += line}dos.flush()dos.close()input.close()conn.connect()println(result)println("*************** 钉钉消息已发送 ***************")
}//获取当前时间
static def getNowTime() {def str = ""SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")Calendar lastDate = Calendar.getInstance()str = sdf.format(lastDate.getTime())return str}

钉钉webhook地址获取如下:
新建一个群,

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以上就是整个流程。谢谢支持!有问题可以留言或者私信

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_398774.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

理解vue中的.sync和.$emit

首先来说一下 .sync 修饰符的作用 第一步&#xff1a;先用一句话解释 .sync修饰符可以实现子组件与父组件的双向绑定&#xff0c;并且可以实现子组件同步修改父组件的值。 第二步&#xff1a;具体解释 一般情况下&#xff0c;想要实现父子组件间值的传递&#xff0c;通常使用…

英文论文要怎么查重?

英文论文查重和中文查重一样&#xff0c;只是在渠道选择方面会有些许差别。今天就具体聊聊英文论文怎么查重&#xff0c;并向大家推荐几个比较常用的英文论文查重工具。 英文论文怎么查重&#xff1a; 1、论文为什么要查重 2、论文查重的原理 3、英文论文怎么查重 4、选择…

柳州楼顶种植水稻 国稻种芯·中国水稻节:广西12万亩米飘香

柳州楼顶种植水稻 国稻种芯中国水稻节&#xff1a;广西12万亩米飘香 广西新闻网-南国今报柳江讯&#xff08;记者钟华 通讯员梁睿&#xff09;新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农…

RabbitMQ常用消息模式

目录 1、RabitMQ工作队列 2、交换机 3、RabbitMQ Fanout 发布订阅--- Fanout exchange(扇型交换机) 3.1、创建连接代码 3.1、生产者代码 3.2、消费者代码 4、Direct路由模式 4.1、生产者代码 4.2、消费者代码 5、Topic主题模式 5.1、生产者代码 5.2、消费者代码 1、…

分享两套企业级进销存管理系统源码

▶▶▶▶1&#xff1a;SpringBoot企业级进销存ERP管理系统源码 00189 本系统采用企业级开发标准&#xff0c;使用SpringBoot架构&#xff0c;数据访问层采用Spring Data Jpa&#xff0c;业务控制层采用SpringMvc&#xff0c;安全框架采用Shiro&#xff0c;实现了完整权限系…

风控模型别只会KS、AUC了,来看看其他衡量模型好坏的一些重要指标吧|含实操

当我们训练好一个机器学习模型之后&#xff0c;必然会对模型的综合性能进行评估&#xff0c;针对分类、回归、聚类等不同类型的算法模型&#xff0c;可以采用相关的评价指标&#xff0c;例如分类模型的Accuracy、KS等&#xff1b;回归模型的MAE、MSE等&#xff1b;聚类模型的SS…

Linux下编写C使用的GDB调试器

目录 1.GDB调试器 2.GDB使用 3.实例程序调试 &#xff08;1&#xff09;编写一段C程序 &#xff08;2&#xff09;对C程序进行编译 &#xff08;3&#xff09;调试阶段 ①启动调试 ②查看文件 ③设置断点 ④查看断点情况 ⑤运行代码 ⑥单步运行 ⑦恢复程序 ⑧查看…

数字孪生建筑工程系统开发案例方案,如何选择数孪平台?

据统计&#xff0c;全国建筑业增长值在 GDP 增长中所占比重连续十年保持在 6. 85%以上&#xff0c;其支柱产业的地位依然保持。但是我国建筑业产值利润率已连续五年下滑&#xff0c;部分原因是其生产方式粗放、信息化水平不高、科技创新能力不足等。因此&#xff0c;在发展数字…

java类加载机制解析

一&#xff1a;类加载流程 public class Math {public static final int initData 666;public static User user new User();public int compute(){int a 1;int b 2;return ab;};public static void main(String[] args){Math math new Math();math.compute();} } 当我们…

Mybatis批量插入数据

前言 在很多业务场景中&#xff0c;我们需要批量录入数据。那么意味着我们需要以最高效的方式去实现功能&#xff0c;同时也需要保证软件的便捷性与可维护性&#xff0c;开源字节使用MyBatis foreach标签方式优雅的实现了材料的出入库。源码开放&#xff0c;可前往码云仓库免费…

NR 物理层编码 - slide7 卷积码

前言&#xff1a; 卷积码(n,k,N) 是一种非分组码.与线性分组码的区别: 是一种有记忆的编码方案,n个输出不仅与当前k个输入有关系,也与移位寄存器前N个输入有关系. 发展历史&#xff1a; 1955年 麻省理工的P.Elias 发明 1957年 序列译码法 1963年 门限译码法 1967年 Vi…

MongoDB分片机制

为什么需要分片 应用层实现的手动分片&#xff1a; MongoDB分片组件 mongos路由器负责将应用程序的请求指引到合适的分片上。注意到mongos路由器是在应用程序端实现的&#xff0c;因此分片的配置信息需要保存在另外的服务器上&#xff0c;即配置服务器。mongos通过两阶段提交同…

使用PreparedStatement对数据库的增删改查

目录 介绍 JDBCUtils自定义工具类 增 删 改 查 介绍 可以通过调用 Connection 对象的 preparedStatement() 方法获取PreparedStatement 对象PreparedStatement 接口是 Statement 的子接口&#xff0c;它表示一条预编译过的 SQL 语句PreparedStatement 对象所代表的 SQL 语…

拼搏半个月,刷了 571道Java高频面试题喜提阿里 offer,定级 P7

今年较往年相比面试要难的多&#xff0c;大环境也是对于程序员的要求越来越高&#xff0c;环境是我们无法改变的&#xff0c;我们能改变的只有自己&#xff0c;月初我一好友&#xff0c;努力拼搏一周&#xff0c;刷完了这份阿里 P8 大牛整理的这 571 道 Java 高频面试题笔记&am…

彩色的木棒

一 问题描述 给你一堆木棒。每根棒的每个端点都用一些颜色着色。是否可以将棒对齐成直线&#xff0c;使得接触的端点的颜色具有相同的颜色&#xff1f; 二 输入和输出 1 输入 输入是一系列行&#xff0c;每行包含两个单词&#xff0c;由空格分隔&#xff0c;给出一个木棒的…

SkeyeVSS智慧国土高点视频监控解决方案

随着经济的快速发展、城镇化的快速推进&#xff0c;耕地及矿产资源等不断减少&#xff0c;未批先建、批少用多、私自改变土地用途等各种违法违规用地行为时有发生&#xff0c;在这种情况下&#xff0c;传统的人力巡查工作效率低、执法成本高的弊端进一步凸显。 SkeyeVSS智慧国土…

科技云报道:私有云市场加速洗牌,超云为何异军突起?

科技云报道原创。 近年来在国家相关政策的大力推动下&#xff0c;中国私有云市场发展渐入佳境&#xff0c;一股新的建设高潮汹涌而至。 根据IDC对于2022-2026中国SDS及HCI的市场预测&#xff0c;中国私有云基础架构市场正在从成长阶段迈向成熟阶段&#xff0c;未来3-5年将保持…

自己动手写ls命令——Java版

自己动手写ls命令——Java版 介绍 在前面的文章Linux命令系列之ls——原来最简单的ls这么复杂当中&#xff0c;我们仔细的介绍了关于ls命令的使用和输出结果&#xff0c;在本篇文章当中我们用Java代码自己实现ls命令&#xff0c;更加深入的了解ls命令。 代码实现 文件操作的…

3000字神经网络论文

你遇到了哪些困难和挫折是怎样克服的写下来的作文 我学会了骑自行车人生的道路上&#xff0c;谁都会遇到困难或挫折&#xff0c;就看你敢不敢去挑战它。那一次学自行车&#xff0c;一直让我记忆犹新。一天傍晚&#xff0c;我和爸爸妈妈一起推着车来到体育馆&#xff0c;这次我…

Android同文输入法的使用(开源输入法Trime)

Trime输入法背景源码APP试用下载安装配置部署成功后再一步&#xff1a;学习如何 DIY总结背景 想找一款开源的Android中文输入法&#xff0c;然后发现了这款备受推崇的输入法框架rime。 RIME&#xff0f;中州韵输入法引擎&#xff0c;是一个跨平台的输入法算法框架。 基于这一…