教你如何用一行命令:Android打包->上传->发测试包通知

简介: 教你如何用一行命令:Android打包->上传->发测试包通知

背景


你是不是经常遇到这个场景,测试找你要测试包,你还在忙着手动打包,然后上传云存储,再然后拿着链接,发到群里通知测试。也可能是你已经用上了Jenkins实现,可我今天要做的就是教你如何自己实现呢?这样做有什么优势呢?

  • 没有环境的差异,云端打包固然好,但总会遇到环境问题导致的打包失败,且不好修复
  • 100%的可定制化,随心所欲
  • 连云存储都是自己的,不担心别的平台对你的测试包进行反编译,窃取技术

一行代码


// Debug 包
node gradle-build.js -d 
// Release包
node gradle-build.js -r

背后如何设计的呢?请看

流程设计


image.png

  • 命令行 Gradle 打包
  • 失败后查找原因,然后再重试
  • 成功后直接上传自己搭建的云存储
  • 然后将Url返回
  • 收到Url后直接调用DingDing Robot Api通知到群里

总体就是三步,第一打包,第二上传,第三通知,那么我们如何能做到这三步呢?请往下看

Step One 打包


打包你是不是想到了命令行输入gradlew assembleDebug,是的,我们也会用到它,Shell脚本固然简单,但它缺乏很多有力的支持,比如我们自定义一个执行脚本如:gradlew -d(打Debug包),当然我举例不是很贴切,就是想然你理解一下,脚本语言有很多,建议你用自己熟悉的语言,其实用什么并不重要,重要的是我们实现了什么,那我是如何实现的呢?我选择了熟悉的nodejs脚本,请看实现:

安装依赖

npm i shelljs
npm i commander

脚本具体实现

const shell = require('shelljs');
const dingding = require('./dingdingNotification')
const file = require('./upload')
var program = require('commander');
program
  .version('0.0.1')
  .option('-d, --debug', 'gradlew assembleDebug')
  .option('-s, --sandbox', 'gradlew assembleSandbox')
  .option('-r, --release', 'gradlew assembleRelease')
  .parse(process.argv);
console.log('正在执行:');
//debug版本打包流程定义
if (program.debug) {
    console.log('  - debug');
    //执行gradle assembleDebug脚本
    builds('gradle assembleDebug',(code)=>{
        if(code==1){
            console.log('执行完毕');
            //build成功,上传文件
            file.postFile({ file: './flutter.png', content_type: 'png' },function(response){
                console.log('response:'+response);
                //钉钉通知
                dingding.talk("Android Debug 安装包构建完成,请点击下方链接下载:Http://apk.ibaozi.cn/tmp/")
            })
        }else{
            console.log('执行失败');
         }
    })
}
//sandbox版本打包流程定义
if (program.sandbox) {
    console.log('  - sandbox');
}
//release版本打包流程定义
if (program.release) {
    console.log('  - release');
}
// 命令执行,返回1说明build成功
function builds(task, call) {
    if (shell.exec(task).code == 0) {
        call(1)
    } else
        call('gradle app:assemble构建失败')
}
  • shelljs 可以实现在命令行执行脚本,脚本执行成功返回1,失败返回构建失败
  • commander 帮助我们检测js脚本输入参数,并安装参数执行对应脚本,我这里写了debug作为示例

Step Two 上传


上传看似简单其实我们设计了两个地方,一个是上传脚本,一个是Server端存储,考虑到存储端实现起来确实复杂,我有个特别简单的实现分享给你,也许搭起来费点力气,可如果搭起来就会很舒服,请先看效果图:

手动上传实现

image.png

脚本上传实现

npm i needle // 首先安装needle 用它上传文件哦
// js脚本实现
var needle = require('needle');
// { file: './flutter.png', content_type: 'png' }
function postFile(file,callBack){
    needle.post('http://localhost:4000/upload', {
        path:'test',
        version:'1.0.0',
        version_code:20,
        file: file
    }, { multipart: true }, function(err, resp, body) {
        console.log(err, resp, body)
        callBack(resp)
    });
}
module.exports = {
    postFile:postFile
}
// 上传调用
const file = require('./upload')
file.postFile({ file: './flutter.png', content_type: 'png' },function(response){
                console.log('response:'+response);
            })

上传后的目录结构

image.png

点击就可以下载,也可以通过链接查看,看着是不是还算可以,那我们都做了什么呢,很简单的,来跟我一起实现一下:

首先安装依赖

npm i express // 提供静态存储文件夹public,提供下载支持
npm i serve-index // 负责将public目录文件转换为文件列表展示

对的就这俩就行了,接下我们改造下express默认生成的项目代码

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const serveIndex = require('serve-index')
var usersRouter = require('./routes/users');
var indexRouter = require('./routes/index');
var app = express();
var rootPath = path.join(__dirname, 'public');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use('/ftp', express.static(rootPath), serveIndex(rootPath, {'icons': true}));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};
  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
module.exports = app;

最主要的就是这一行

app.use('/ftp', express.static(rootPath), serveIndex(rootPath, {'icons': true}));

将public文件夹委托给serveIndex,并新增一个目录地址ftp,浏览地址如下:

http://localhost:4000/ftp/

浏览搞定了,就差上传了,我们这里用到一个库formidable,它来帮助我们处理上传过来的文件流处理逻辑

npm i formidable
var express = require('express');
var router = express.Router();
const formidable = require('formidable');
var path = require('path');
var fs = require('fs');
const { isString } = require('util');
var fileRootPath = path.resolve(__dirname,'../public')
console.log(fileRootPath);
/* GET home page. */
router.get('/', function(req, res, next) {
    res.send(`
    <h2>Julive <code>"File Storage"</code> For You</h2>
    <form action="/upload" enctype="multipart/form-data" method="post">
      <h3>请选择文件</h3>
      <div>文件: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
      <h3>点击提交</h3>
      <input type="submit" value="开始上传" />
    </form>
  `);
});
router.post('/upload', async (req, res) => {
  try {
    const form = formidable({ multiples: true,uploadDir: fileRootPath});
    var path = "/"
    form.once('error', console.error);
    form.on('field', (name, value) => {
        if(name=="path"){
            if(!isNull(value)){
                path = "/"+value+"/"
            }
        }
    });
    form.on('file', (name, file) => {                
        fs.rename(file.path, form.uploadDir + path + file.name,(error)=>{
            if(error){
                next(err);
                return;
            }
        });
    });
    form.parse(req, (err, fields, files) => {
      if (err) {
        next(err);
        return;
      }
      console.log("fields.path:"+fields.path)
      console.log("fields.version:"+fields.version)
      console.log("fields.version_code:"+fields.version_code)
      files.url = 'http://file.ibaozi.cn/ftp/'
      res.json({ fields, files });
    });
  } catch (err) {
      res.status(500).send(err);
  }
});
function isNull(str){
    if ( str == ""|| str == null) return true;
    var regu = "^[ ]+$";
    var re = new RegExp(regu);
    return re.test(str);
}
module.exports = router;

通过formidable,我们可以在各个回调里处理相应的逻辑,如once打印错误信息,在form.on('field', 里处理的文件的路径逻辑等等吧。上传调用http://localhost:4000/upload接口即可。

还有个get接口发现了吧,那个就是手动上传的页面逻辑哦。到此,Server端也实现完了

Step Three 通知


通知的实现如下,这里用到flyio,来发起请求,为什么用它?因为这个早就写好的脚本,就懒得再改了

npm i flyio 

通知

const fly = require("flyio");
function dingtalk(content){
    const url = "https://oapi.dingtalk.com/robot/send?access_token=40e67411719fbd584134a7deefc1c643f2d0916f8efcefb12e7759591fa7e637";
    if(content==null){
        content = "Android Apk 安装包构建成功,下载地址:https://www.pgyer.com/gIEm  "
    }
    console.log(content);
    const data = {
        "msgtype": "text", "text": {
            "content": content
        },
        "at": {
            "isAtAll": true
        }
    };
    fly.config.headers = {"Content-Type": "application/json"};
//    fly.post(url, data).then((response) => {
//        console.log(response)
//    });
}
module.exports = {
    talk:dingtalk
}

好了结束了,就这么多。

总结


我们回顾一下,本期主要是介绍了通过一行命令完成Android打包->上传->发测试包通知的过程,主要是用到了我熟悉的nodejs脚本,如果你还对nodejs不了解,建议抽时间学习下,也希望对你有帮助,这个云存储目前没有安全方面的访问控制,用的话建议公司内部私网使用,也可以像我,买个服务器放上去,自己没事就折腾一下,哈哈。

项目开源地址


github.com/ibaozi-cn/f…

目录
相关文章
|
1月前
|
自然语言处理 机器人 Python
ChatGPT使用学习:ChatPaper安装到测试详细教程(一文包会)
ChatPaper是一个基于文本生成技术的智能研究论文工具,能够根据用户输入进行智能回复和互动。它支持快速下载、阅读论文,并通过分析论文的关键信息帮助用户判断是否需要深入了解。用户可以通过命令行或网页界面操作,进行论文搜索、下载、总结等。
43 1
ChatGPT使用学习:ChatPaper安装到测试详细教程(一文包会)
|
2月前
|
测试技术 Shell Android开发
Android 性能测试初探 (六)
本节聊聊性能测试的最后一项- 流量,当然我所指的性能测试是针对大部分应用而言的,可能还有部分应用会关注网速、弱网之类的测试,但本系列文章都不去一一探讨了。
54 6
|
2月前
|
JavaScript 测试技术 Android开发
Android 性能测试初探 (四)
本文介绍了GPU在移动端性能测试中的重要性,并详细解释了过度绘制、帧率和帧方差的概念。针对GPU测试,文章列举了三项主要测试内容:界面过度绘制、屏幕滑动帧速率和平滑度。其中,过度绘制测试需遵循特定标准,而帧速率和平滑度测试则可通过软件或硬件方法实现。在软件测试中,使用Systrace插件和高速相机是两种常用手段。对于不同机型,帧率及帧方差的测试标准也需相应调整。
50 5
|
2月前
|
测试技术 Shell Android开发
Android 性能测试初探 (三)
本文承接《Android性能测试初探(二)》,深入探讨CPU与内存测试。介绍了移动端内存测试的重要性及其测试目标,并详细列举了不同状态下应用内存消耗情况的测试项目。此外,还提供了多种内存测试方法,包括使用`procrank`等工具的具体操作步骤。最后,文章也简要提及了CPU测试的相关内容,帮助读者更好地理解Android性能测试的关键要素。
49 5
|
2月前
|
测试技术 Shell 定位技术
Android 性能测试初探 (五)
聊聊大家不常关注的测试项- 功耗
48 3
|
2月前
|
算法 测试技术 Android开发
Android 性能测试初探 (二)
上回大体介绍了下在 android 端的性能测试项,现在我们就细节测试项做一些阐述(包括如何自己 DIY 测试)
45 4
|
2月前
|
测试技术 API Android开发
Android 性能测试初探 (一)
Android 性能测试,跟pc性能测试一样分为客户端及服务器,但在客户端上的性能测试分为 2 类: 一类为 rom 版本的性能测试;一类为应用的性能测试。
51 3
|
4天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
29 3
|
1月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
56 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
2月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
230 7
Jmeter实现WebSocket协议的接口测试方法